Merge "RCFilters: Don't emit wikipage.content on first load"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Tue, 18 Jul 2017 23:18:51 +0000 (23:18 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Tue, 18 Jul 2017 23:18:51 +0000 (23:18 +0000)
87 files changed:
RELEASE-NOTES-1.30
includes/DefaultSettings.php
includes/EditPage.php
includes/XmlJsCode.php
includes/changes/ChangesListStringOptionsFilter.php
includes/diff/DifferenceEngine.php
includes/exception/MWExceptionRenderer.php
includes/htmlform/fields/HTMLSelectAndOtherField.php
includes/htmlform/fields/HTMLSelectOrOtherField.php
includes/installer/SqliteInstaller.php
includes/jobqueue/JobQueueDB.php
includes/libs/MemoizedCallable.php
includes/rcfeed/RedisPubSubFeedEngine.php
includes/specialpage/ChangesListSpecialPage.php
includes/specials/SpecialRedirect.php
languages/i18n/be-tarask.json
languages/i18n/bho.json
languages/i18n/din.json
languages/i18n/en.json
languages/i18n/eu.json
languages/i18n/ha.json
languages/i18n/inh.json
languages/i18n/ko.json
languages/i18n/qqq.json
languages/i18n/roa-tara.json
languages/i18n/sr-ec.json
languages/i18n/sr-el.json
languages/i18n/tg-cyrl.json
languages/i18n/tt-cyrl.json
languages/i18n/zh-hant.json
maintenance/deleteDefaultMessages.php
mw-config/config.js
package.json
resources/Resources.php
resources/src/jquery/jquery.badge.js
resources/src/jquery/jquery.colorUtil.js
resources/src/jquery/jquery.makeCollapsible.js
resources/src/jquery/jquery.mwExtension.js
resources/src/jquery/jquery.suggestions.js
resources/src/jquery/jquery.tablesorter.js
resources/src/mediawiki.action/mediawiki.action.edit.preview.js
resources/src/mediawiki.action/mediawiki.action.history.js
resources/src/mediawiki.action/mediawiki.action.view.metadata.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/mw.rcfilters.Controller.js
resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.SavedLinksListItemWidget.less
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterMenuHeaderWidget.js
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterMenuSectionOptionWidget.js
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterTagMultiselectWidget.js
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.MenuSelectWidget.js
resources/src/mediawiki.special/mediawiki.special.contributions.js
resources/src/mediawiki.special/mediawiki.special.preferences.js
resources/src/mediawiki.special/mediawiki.special.search.commonsInterwikiWidget.js
resources/src/mediawiki.widgets.datetime/DateTimeFormatter.js
resources/src/mediawiki.widgets.datetime/DateTimeInputWidget.js
resources/src/mediawiki.widgets/MediaSearch/mw.widgets.MediaSearchWidget.js
resources/src/mediawiki.widgets/mw.widgets.CalendarWidget.js
resources/src/mediawiki.widgets/mw.widgets.DateInputWidget.js
resources/src/mediawiki/api/rollback.js
resources/src/mediawiki/api/upload.js
resources/src/mediawiki/htmlform/hide-if.js
resources/src/mediawiki/mediawiki.ForeignStructuredUpload.BookletLayout.js
resources/src/mediawiki/mediawiki.RegExp.js
resources/src/mediawiki/mediawiki.Title.js
resources/src/mediawiki/mediawiki.debug.js
resources/src/mediawiki/mediawiki.feedback.js
resources/src/mediawiki/mediawiki.jqueryMsg.js
resources/src/mediawiki/mediawiki.js
resources/src/mediawiki/mediawiki.notification.js
resources/src/mediawiki/mediawiki.requestIdleCallback.js
resources/src/mediawiki/mediawiki.searchSuggest.js
resources/src/mediawiki/mediawiki.toc.js
resources/src/mediawiki/page/patrol.ajax.js
resources/src/mediawiki/page/rollback.js
tests/phan/stubs/wikidiff.php
tests/phpunit/MediaWikiTestCase.php
tests/phpunit/includes/specialpage/ChangesListSpecialPageTest.php
tests/phpunit/suite.xml
tests/qunit/data/testrunner.js
tests/qunit/suites/resources/jquery/jquery.byteLimit.test.js
tests/qunit/suites/resources/jquery/jquery.makeCollapsible.test.js
tests/qunit/suites/resources/mediawiki.api/mediawiki.api.options.test.js
tests/qunit/suites/resources/mediawiki.api/mediawiki.api.test.js
tests/qunit/suites/resources/mediawiki/mediawiki.jqueryMsg.test.js
tests/qunit/suites/resources/mediawiki/mediawiki.loader.test.js
tests/selenium/wdio.conf.js

index 87e6ce5..51f9764 100644 (file)
@@ -41,6 +41,7 @@ section).
   enabled by setting $wgUsePigLatinVariant to true.
 * Added RecentChangesPurgeRows hook to allow extensions to purge data that
   depends on the recentchanges table.
+* Added JS config values wgDiffOldId/wgDiffNewId to the output of diff pages.
 
 === Languages updated in 1.30 ===
 
index 6ce9a66..f35715e 100644 (file)
@@ -8301,6 +8301,20 @@ $wgUpdateRowsPerQuery = 100;
  */
 $wgExternalDiffEngine = false;
 
+/**
+ * wikidiff2 supports detection of changes in moved paragraphs.
+ * This setting controls the maximum number of paragraphs to compare before it bails out.
+ * Supported values:
+ * * 0: detection of moved paragraphs is disabled
+ * * int > 0: maximum number of paragraphs to compare
+ * Note: number of paragraph comparisons is in O(n^2).
+ * This setting is only effective if the wikidiff2 PHP/HHVM module is used as diffengine.
+ * See $wgExternalDiffEngine.
+ *
+ * @since 1.30
+ */
+$wgWikiDiff2MovedParagraphDetectionCutoff = 0;
+
 /**
  * Disable redirects to special pages and interwiki redirects, which use a 302
  * and have no "redirected from" link.
index 814c248..973327b 100644 (file)
@@ -422,8 +422,6 @@ class EditPage {
         * @param Article $article
         */
        public function __construct( Article $article ) {
-               global $wgOOUIEditPage;
-
                $this->mArticle = $article;
                $this->page = $article->getPage(); // model object
                $this->mTitle = $article->getTitle();
@@ -434,7 +432,7 @@ class EditPage {
                $handler = ContentHandler::getForModelID( $this->contentModel );
                $this->contentFormat = $handler->getDefaultFormat();
 
-               $this->oouiEnabled = $wgOOUIEditPage;
+               $this->oouiEnabled = $this->context->getConfig()->get( 'OOUIEditPage' );
        }
 
        /**
index d1cd037..35a0607 100644 (file)
  * interpret a given string as being a JavaScript expression, instead of string
  * data.
  *
- * Example:
+ * @par Example:
+ * @code
+ *     Xml::encodeJsVar( new XmlJsCode( 'a + b' ) );
+ * @encode
  *
- *    Xml::encodeJsVar( new XmlJsCode( 'a + b' ) );
- *
- * Returns "a + b".
+ * This returns "a + b".
  *
  * @note As of 1.21, XmlJsCode objects cannot be nested inside objects or arrays. The sole
  *       exception is the $args argument to Xml::encodeJsCall() because Xml::encodeJsVar() is
index 6754d67..76d0bef 100644 (file)
@@ -19,10 +19,12 @@ class ChangesListStringOptionsFilter extends ChangesListFilter {
         * @inheritdoc
         */
        public function isSelected( FormOptions $opts ) {
-               $values = explode(
-                       ChangesListStringOptionsFilterGroup::SEPARATOR,
-                       $opts[ $this->getGroup()->getName() ]
-               );
+               $option = $opts[ $this->getGroup()->getName() ];
+               if ( $option === ChangesListStringOptionsFilterGroup::ALL ) {
+                       return true;
+               }
+
+               $values = explode( ChangesListStringOptionsFilterGroup::SEPARATOR, $option );
                return in_array( $this->getName(), $values );
        }
 }
index 0b58cc1..95420d9 100644 (file)
@@ -377,6 +377,11 @@ class DifferenceEngine extends ContextSource {
                        }
                }
 
+               $out->addJsConfigVars( [
+                       'wgDiffOldId' => $this->mOldid,
+                       'wgDiffNewId' => $this->mNewid,
+               ] );
+
                # Make "next revision link"
                # Skip next link on the top revision
                if ( $samePage && !$this->mNewRev->isCurrent() ) {
@@ -908,10 +913,35 @@ class DifferenceEngine extends ContextSource {
                        $wgExternalDiffEngine = false;
                }
 
+               // Better external diff engine, the 2 may some day be dropped
+               // This one does the escaping and segmenting itself
                if ( function_exists( 'wikidiff2_do_diff' ) && $wgExternalDiffEngine === false ) {
-                       # Better external diff engine, the 2 may some day be dropped
-                       # This one does the escaping and segmenting itself
-                       $text = wikidiff2_do_diff( $otext, $ntext, 2 );
+                       $wikidiff2Version = phpversion( 'wikidiff2' );
+                       if (
+                               $wikidiff2Version !== false &&
+                               version_compare( $wikidiff2Version, '0.3.0', '>=' )
+                       ) {
+                               $text = wikidiff2_do_diff(
+                                       $otext,
+                                       $ntext,
+                                       2,
+                                       $this->getConfig()->get( 'WikiDiff2MovedParagraphDetectionCutoff' )
+                               );
+                       } else {
+                               // Don't pass the 4th parameter for compatibility with older versions of wikidiff2
+                               $text = wikidiff2_do_diff(
+                                       $otext,
+                                       $ntext,
+                                       2
+                               );
+
+                               // Log a warning in case the configuration value is set to not silently ignore it
+                               if ( $this->getConfig()->get( 'WikiDiff2MovedParagraphDetectionCutoff' ) > 0 ) {
+                                       wfLogWarning( '$wgWikiDiff2MovedParagraphDetectionCutoff is set but has no
+                                               effect since the used version of WikiDiff2 does not support it.' );
+                               }
+                       }
+
                        $text .= $this->debug( 'wikidiff2' );
 
                        return $text;
index 2eb821a..60d760f 100644 (file)
@@ -211,7 +211,7 @@ class MWExceptionRenderer {
                                "\nBacktrace:\n" .
                                MWExceptionHandler::getRedactedTraceAsString( $e ) . "\n";
                } else {
-                       return self::getShowBacktraceError( $e );
+                       return self::getShowBacktraceError( $e ) . "\n";
                }
        }
 
@@ -242,7 +242,7 @@ class MWExceptionRenderer {
                        $vars[] = '$wgShowDBErrorBacktrace = true;';
                }
                $vars = implode( ' and ', $vars );
-               return "Set $vars at the bottom of LocalSettings.php to show detailed debugging information\n";
+               return "Set $vars at the bottom of LocalSettings.php to show detailed debugging information.";
        }
 
        /**
index 9af60e5..38b487a 100644 (file)
@@ -63,8 +63,70 @@ class HTMLSelectAndOtherField extends HTMLSelectField {
                return "$select<br />\n$textbox";
        }
 
+       protected function getOOUIModules() {
+               return [ 'mediawiki.widgets.SelectWithInputWidget' ];
+       }
+
        public function getInputOOUI( $value ) {
-               return false;
+               $this->mParent->getOutput()->addModuleStyles( 'mediawiki.widgets.SelectWithInputWidget.styles' );
+
+               # TextInput
+               $textAttribs = [
+                       'id' => $this->mID . '-other',
+                       'name' => $this->mName . '-other',
+                       'size' => $this->getSize(),
+                       'class' => [ 'mw-htmlform-select-and-other-field' ],
+                       'data-id-select' => $this->mID,
+                       'value' => $value[2],
+               ];
+
+               $allowedParams = [
+                       'required',
+                       'autofocus',
+                       'multiple',
+                       'disabled',
+                       'tabindex',
+                       'maxlength',
+               ];
+
+               $textAttribs += OOUI\Element::configFromHtmlAttributes(
+                       $this->getAttributes( $allowedParams )
+               );
+
+               if ( $this->mClass !== '' ) {
+                       $textAttribs['classes'] = [ $this->mClass ];
+               }
+
+               # DropdownInput
+               $dropdownInputAttribs = [
+                       'name' => $this->mName,
+                       'id' => $this->mID,
+                       'options' => $this->getOptionsOOUI(),
+                       'value' => $value[1],
+               ];
+
+               $allowedParams = [
+                       'tabindex',
+                       'disabled',
+               ];
+
+               $dropdownInputAttribs += OOUI\Element::configFromHtmlAttributes(
+                       $this->getAttributes( $allowedParams )
+               );
+
+               if ( $this->mClass !== '' ) {
+                       $dropdownInputAttribs['classes'] = [ $this->mClass ];
+               }
+
+               return $this->getInputWidget( [
+                       'textinput' => $textAttribs,
+                       'dropdowninput' => $dropdownInputAttribs,
+                       'or' => false,
+               ] );
+       }
+
+       public function getInputWidget( $params ) {
+               return new Mediawiki\Widget\SelectWithInputWidget( $params );
        }
 
        /**
index bb41079..a009b28 100644 (file)
@@ -64,8 +64,80 @@ class HTMLSelectOrOtherField extends HTMLTextField {
                return "$select<br />\n$textbox";
        }
 
+       protected function shouldInfuseOOUI() {
+               return true;
+       }
+
+       protected function getOOUIModules() {
+               return [ 'mediawiki.widgets.SelectWithInputWidget' ];
+       }
+
        public function getInputOOUI( $value ) {
-               return false;
+               $this->mParent->getOutput()->addModuleStyles( 'mediawiki.widgets.SelectWithInputWidget.styles' );
+
+               $valInSelect = false;
+               if ( $value !== false ) {
+                       $value = strval( $value );
+                       $valInSelect = in_array(
+                               $value, HTMLFormField::flattenOptions( $this->getOptions() ), true
+                       );
+               }
+
+               # DropdownInput
+               $dropdownAttribs = [
+                       'id' => $this->mID,
+                       'name' => $this->mName,
+                       'options' => $this->getOptionsOOUI(),
+                       'value' => $valInSelect ? $value : 'other',
+                       'class' => [ 'mw-htmlform-select-or-other' ],
+               ];
+
+               $allowedParams = [
+                       'disabled',
+                       'tabindex',
+               ];
+
+               $dropdownAttribs += OOUI\Element::configFromHtmlAttributes(
+                       $this->getAttributes( $allowedParams )
+               );
+
+               # TextInput
+               $textAttribs = [
+                       'id' => $this->mID . '-other',
+                       'name' => $this->mName . '-other',
+                       'size' => $this->getSize(),
+                       'value' => $valInSelect ? '' : $value,
+               ];
+
+               $allowedParams = [
+                       'required',
+                       'autofocus',
+                       'multiple',
+                       'disabled',
+                       'tabindex',
+                       'maxlength',
+               ];
+
+               $textAttribs += OOUI\Element::configFromHtmlAttributes(
+                       $this->getAttributes( $allowedParams )
+               );
+
+               if ( $this->mClass !== '' ) {
+                       $textAttribs['classes'] = [ $this->mClass ];
+               }
+               if ( $this->mPlaceholder !== '' ) {
+                       $textAttribs['placeholder'] = $this->mPlaceholder;
+               }
+
+               return $this->getInputWidget( [
+                       'textinput' => $textAttribs,
+                       'dropdowninput' => $dropdownAttribs,
+                       'or' => true,
+               ] );
+       }
+
+       public function getInputWidget( $params ) {
+               return new Mediawiki\Widget\SelectWithInputWidget( $params );
        }
 
        /**
index 3943374..0f639ba 100644 (file)
@@ -32,7 +32,8 @@ use Wikimedia\Rdbms\DBConnectionError;
  * @since 1.17
  */
 class SqliteInstaller extends DatabaseInstaller {
-       const MINIMUM_VERSION = '3.3.7';
+
+       public $minimumVersion = '3.3.7';
 
        /**
         * @var DatabaseSqlite
@@ -60,8 +61,8 @@ class SqliteInstaller extends DatabaseInstaller {
                $result = Status::newGood();
                // Bail out if SQLite is too old
                $db = DatabaseSqlite::newStandaloneInstance( ':memory:' );
-               if ( version_compare( $db->getServerVersion(), self::MINIMUM_VERSION, '<' ) ) {
-                       $result->fatal( 'config-outdated-sqlite', $db->getServerVersion(), self::MINIMUM_VERSION );
+               if ( version_compare( $db->getServerVersion(), $this->minimumVersion, '<' ) ) {
+                       $result->fatal( 'config-outdated-sqlite', $db->getServerVersion(), $this->minimumVersion );
                }
                // Check for FTS3 full-text search module
                if ( DatabaseSqlite::getFulltextSearchModule() != 'FTS3' ) {
index cefe74d..b7cc133 100644 (file)
@@ -768,7 +768,7 @@ class JobQueueDB extends JobQueue {
        protected function getDB( $index ) {
                $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
                $lb = ( $this->cluster !== false )
-                       ? $lbFactory->getExternalLB( $this->cluster, $this->wiki )
+                       ? $lbFactory->getExternalLB( $this->cluster )
                        : $lbFactory->getMainLB( $this->wiki );
 
                return $lb->getConnectionRef( $index, [], $this->wiki );
index 6b4281f..01adeab 100644 (file)
@@ -1,4 +1,24 @@
 <?php
+/**
+ * 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
+ * @author Ori Livneh
+ */
+
 /**
  * APC-backed and APCu-backed function memoization
  *
  * MemoizedCallable::call( 'range', array( 5, 8 ) );  // same
  * @endcode
  *
- * 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
- * @author Ori Livneh
  * @since 1.27
  */
 class MemoizedCallable {
index 4c011be..f0fcd7d 100644 (file)
  * 'rc'. If the URI contains a query string, its parameters will be parsed
  * as RedisConnectionPool options.
  *
- * @example
+ * @par Example:
+ * @code
  * $wgRCFeeds['redis'] = array(
  *      'formatter' => 'JSONRCFeedFormatter',
  *      'uri'       => "redis://127.0.0.1:6379/rc.$wgDBname",
  * );
+ * @encode
  *
  * @since 1.22
  */
index b85d272..645fbb2 100644 (file)
@@ -86,8 +86,6 @@ abstract class ChangesListSpecialPage extends SpecialPage {
                                'filters' => [
                                        [
                                                'name' => 'hideliu',
-                                               'label' => 'rcfilters-filter-registered-label',
-                                               'description' => 'rcfilters-filter-registered-description',
                                                // rcshowhideliu-show, rcshowhideliu-hide,
                                                // wlshowhideliu
                                                'showHideSuffix' => 'showhideliu',
@@ -97,16 +95,11 @@ abstract class ChangesListSpecialPage extends SpecialPage {
                                                ) {
                                                        $conds[] = 'rc_user = 0';
                                                },
-                                               'cssClassSuffix' => 'liu',
-                                               'isRowApplicableCallable' => function ( $ctx, $rc ) {
-                                                       return $rc->getAttribute( 'rc_user' );
-                                               },
+                                               'isReplacedInStructuredUi' => true,
 
                                        ],
                                        [
                                                'name' => 'hideanons',
-                                               'label' => 'rcfilters-filter-unregistered-label',
-                                               'description' => 'rcfilters-filter-unregistered-description',
                                                // rcshowhideanons-show, rcshowhideanons-hide,
                                                // wlshowhideanons
                                                'showHideSuffix' => 'showhideanons',
@@ -116,10 +109,7 @@ abstract class ChangesListSpecialPage extends SpecialPage {
                                                ) {
                                                        $conds[] = 'rc_user != 0';
                                                },
-                                               'cssClassSuffix' => 'anon',
-                                               'isRowApplicableCallable' => function ( $ctx, $rc ) {
-                                                       return !$rc->getAttribute( 'rc_user' );
-                                               },
+                                               'isReplacedInStructuredUi' => true,
                                        ]
                                ],
                        ],
@@ -128,9 +118,26 @@ abstract class ChangesListSpecialPage extends SpecialPage {
                                'name' => 'userExpLevel',
                                'title' => 'rcfilters-filtergroup-userExpLevel',
                                'class' => ChangesListStringOptionsFilterGroup::class,
-                               // Excludes unregistered users
-                               'isFullCoverage' => false,
+                               'isFullCoverage' => true,
                                'filters' => [
+                                       [
+                                               'name' => 'unregistered',
+                                               'label' => 'rcfilters-filter-user-experience-level-unregistered-label',
+                                               'description' => 'rcfilters-filter-user-experience-level-unregistered-description',
+                                               'cssClassSuffix' => 'user-unregistered',
+                                               'isRowApplicableCallable' => function ( $ctx, $rc ) {
+                                                       return !$rc->getAttribute( 'rc_user' );
+                                               }
+                                       ],
+                                       [
+                                               'name' => 'registered',
+                                               'label' => 'rcfilters-filter-user-experience-level-registered-label',
+                                               'description' => 'rcfilters-filter-user-experience-level-registered-description',
+                                               'cssClassSuffix' => 'user-registered',
+                                               'isRowApplicableCallable' => function ( $ctx, $rc ) {
+                                                       return $rc->getAttribute( 'rc_user' );
+                                               }
+                                       ],
                                        [
                                                'name' => 'newcomer',
                                                'label' => 'rcfilters-filter-user-experience-level-newcomer-label',
@@ -632,19 +639,10 @@ abstract class ChangesListSpecialPage extends SpecialPage {
                $this->registerFiltersFromDefinitions( [ $unstructuredGroupDefinition ] );
 
                $userExperienceLevel = $this->getFilterGroup( 'userExpLevel' );
-
-               $registration = $this->getFilterGroup( 'registration' );
-               $anons = $registration->getFilter( 'hideanons' );
-
-               // This means there is a conflict between any item in user experience level
-               // being checked and only anons being *shown* (hideliu=1&hideanons=0 in the
-               // URL, or equivalent).
-               $userExperienceLevel->conflictsWith(
-                       $anons,
-                       'rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global',
-                       'rcfilters-filtergroup-user-experience-level-conflicts-unregistered',
-                       'rcfilters-filter-unregistered-conflicts-user-experience-level'
-               );
+               $registered = $userExperienceLevel->getFilter( 'registered' );
+               $registered->setAsSupersetOf( $userExperienceLevel->getFilter( 'newcomer' ) );
+               $registered->setAsSupersetOf( $userExperienceLevel->getFilter( 'learner' ) );
+               $registered->setAsSupersetOf( $userExperienceLevel->getFilter( 'experienced' ) );
 
                $categoryFilter = $changeTypeGroup->getFilter( 'hidecategorization' );
                $logactionsFilter = $changeTypeGroup->getFilter( 'hidelog' );
@@ -1337,15 +1335,35 @@ abstract class ChangesListSpecialPage extends SpecialPage {
                        $wgLearnerMemberSince,
                        $wgExperiencedUserMemberSince;
 
-               $LEVEL_COUNT = 3;
+               $LEVEL_COUNT = 5;
 
-               // If all levels are selected, all logged-in users are included (but no
-               // anons), so we can short-circuit.
+               // If all levels are selected, don't filter
                if ( count( $selectedExpLevels ) === $LEVEL_COUNT ) {
+                       return;
+               }
+
+               // both 'registered' and 'unregistered', experience levels, if any, are included in 'registered'
+               if (
+                       in_array( 'registered', $selectedExpLevels ) &&
+                       in_array( 'unregistered', $selectedExpLevels )
+               ) {
+                       return;
+               }
+
+               // 'registered' but not 'unregistered', experience levels, if any, are included in 'registered'
+               if (
+                       in_array( 'registered', $selectedExpLevels ) &&
+                       !in_array( 'unregistered', $selectedExpLevels )
+               ) {
                        $conds[] = 'rc_user != 0';
                        return;
                }
 
+               if ( $selectedExpLevels === [ 'unregistered' ] ) {
+                       $conds[] = 'rc_user = 0';
+                       return;
+               }
+
                $tables[] = 'user';
                $join_conds['user'] = [ 'LEFT JOIN', 'rc_user = user_id' ];
 
@@ -1373,24 +1391,39 @@ abstract class ChangesListSpecialPage extends SpecialPage {
                        IDatabase::LIST_AND
                );
 
+               $conditions = [];
+
+               if ( in_array( 'unregistered', $selectedExpLevels ) ) {
+                       $selectedExpLevels = array_diff( $selectedExpLevels, [ 'unregistered' ] );
+                       $conditions[] = 'rc_user = 0';
+               }
+
                if ( $selectedExpLevels === [ 'newcomer' ] ) {
-                       $conds[] = "NOT ( $aboveNewcomer )";
+                       $conditions[] = "NOT ( $aboveNewcomer )";
                } elseif ( $selectedExpLevels === [ 'learner' ] ) {
-                       $conds[] = $dbr->makeList(
+                       $conditions[] = $dbr->makeList(
                                [ $aboveNewcomer, "NOT ( $aboveLearner )" ],
                                IDatabase::LIST_AND
                        );
                } elseif ( $selectedExpLevels === [ 'experienced' ] ) {
-                       $conds[] = $aboveLearner;
+                       $conditions[] = $aboveLearner;
                } elseif ( $selectedExpLevels === [ 'learner', 'newcomer' ] ) {
-                       $conds[] = "NOT ( $aboveLearner )";
+                       $conditions[] = "NOT ( $aboveLearner )";
                } elseif ( $selectedExpLevels === [ 'experienced', 'newcomer' ] ) {
-                       $conds[] = $dbr->makeList(
+                       $conditions[] = $dbr->makeList(
                                [ "NOT ( $aboveNewcomer )", $aboveLearner ],
                                IDatabase::LIST_OR
                        );
                } elseif ( $selectedExpLevels === [ 'experienced', 'learner' ] ) {
-                       $conds[] = $aboveNewcomer;
+                       $conditions[] = $aboveNewcomer;
+               } elseif ( $selectedExpLevels === [ 'experienced', 'learner', 'newcomer' ] ) {
+                       $conditions[] = 'rc_user != 0';
+               }
+
+               if ( count( $conditions ) > 1 ) {
+                       $conds[] = $dbr->makeList( $conditions, IDatabase::LIST_OR );
+               } elseif ( count( $conditions ) === 1 ) {
+                       $conds[] = reset( $conditions );
                }
        }
 }
index 1d1df6a..3273046 100644 (file)
@@ -33,16 +33,18 @@ class SpecialRedirect extends FormSpecialPage {
        /**
         * The type of the redirect (user/file/revision)
         *
+        * Example value: `'user'`
+        *
         * @var string $mType
-        * @example 'user'
         */
        protected $mType;
 
        /**
         * The identifier/value for the redirect (which id, which file)
         *
+        * Example value: `'42'`
+        *
         * @var string $mValue
-        * @example '42'
         */
        protected $mValue;
 
index ec0cdb2..b9935f8 100644 (file)
        "removecredentials-invalidsubpage": "$1 не зьяўляецца слушным тыпам уліковых зьвестак.",
        "removecredentials-success": "Вашыя ўліковыя зьвесткі былі выдаленыя.",
        "credentialsform-provider": "Тып уліковых зьвестак:",
-       "credentialsform-account": "Назва рахунку:"
+       "credentialsform-account": "Назва рахунку:",
+       "cannotlink-no-provider-title": "Няма рахункаў для далучэньня"
 }
index 696ce6d..c63d81d 100644 (file)
        "navigation-heading": "नेविगेशन मेनू",
        "errorpagetitle": "खराबी",
        "returnto": "$1 पर लवटीं।",
-       "tagline": "भोजपुरी {{SITENAME}} से",
+       "tagline": "{{SITENAME}} से",
        "help": "मदद",
        "search": "खोज",
        "search-ignored-headings": " #<!-- एह लाइन के बिलकुल अइसहीं छोड़ दीं --> <pre>\n# हेडिंग जिनहन पर खोज करत समय धियान ना दिहल जाई।\n# एह हेडिंग वाला पन्ना जइसहीं सूचीबद्ध होखी, बदलाव प्रभावी हो जइहें।\n# आप एगो खाली संपादन (null edit) कइ के दुबारा सूचीकरण के लागू कर सकत बानी।\n# एकर सिंटेक्स अइसे बा कि:\n#   * Everything from a \"#\" character to the end of the line is a comment.\n#   * Every non-blank line is the exact title to ignore, case and everything.\nसंदर्भ\nबाहरी कड़ी\nइहो देखल जाय\n #</pre> <!-- एह लाइन के बिलकुल अइसहीं छोड़ दीं -->",
        "blockedtext": "'''राउर सदस्यनाम अथवा आइ॰पी पता अवरोधित कर दिहल गईल बा ।'''\n\nअवरोध $1 द्वारा करल गईल रहल।\nअवरोध के कारण बा ''$2''\n\n* अवरोध के आरंभ: $8\n* अवरोध के समाप्ति: $6\n* अवरोधित इकाई: $7\n\nइ अवरोध के बारे में चर्चा करे खातिर रउआ $1 या केहु अन्य [[{{MediaWiki:Grouppage-sysop}}|प्रबन्धक]] से संपर्क कर सकत बानी।\nअगर रउआ [[Special:Preferences|आपन वरीयता]] में वैद्य ई-मेल पता प्रविष्ट कइले होखब तबे 'इ प्रयोक्ता के ई-मेल भेजीं' वाला सुविधा के प्रयोग कर सकत बानी अउर रउआ एकर प्रयोग करे से ना रोकल गईल होखे।\nराउर हाल के आइ॰पी पता $3 ह अउर अवरोध क्रमांक #$5 ह।\nआपन कउनो भी प्रश्न में कृपया इ सब जानकारी भी शामिल करब।",
        "autoblockedtext": "राउर आइ॰पी पता अपने आप अवरुद्ध हो गईल बा काहे कि एकर प्रयोग केहु अन्य सदस्य द्वारा होत रहल,\nजे $1 द्वारा अवरोधित करल गईल रहलन। \nअवरोध करे के कारण बा:\n\n:''$2''\n\n* अवरोध प्रारंभ: $8\n* अवरोध समाप्ति: $6\n* अवरोधित सदस्य: $7\n\nअवरोध के चर्चा करे खातिर रउआ $1 या केहु अन्य [[{{MediaWiki:Grouppage-sysop}}|प्रबंधक]] से संपर्क कर सकत बानी।\n\nकृपया ध्यान रहे कि यदि रउआ \"इ सदस्य के ई-मेल भेजीं\" वाला सुविधा के प्रयोग करे के चाहत बानी त राउर [[Special:Preferences|वरीयता]] में वैद्य ई-मेल पता होखे के चाहीं अउर एकर प्रयोग रउआ खातिर अवरोधित ना भईल होखे।\n\nराउर हाल के आइ॰पी पता $3 ह अउर अवरोध क्रमांक #$5 ह।\nआपन कउनो भी प्रश्न में कृपया इ सब जानकारी शामिल करब।",
        "systemblockedtext": "राउर खाता या आइपी पता के मीडियाविकि द्वारा ऑटोमेटिक रूप से रोक दिहल गइल बा।\nएकरा खातिर कारण दिहल गइल बा:\n\n\n:<em>$2</em>\n\n* रोक के सुरुआत: $8\n* रोक समाप्त होखी: $6\n* रोक लगावे वाला: $7\n\nराउर वर्तमान आइपी पता $3 बा।\nअगर कौनों सवाल करीं तब ऊपर बतावल सगरी जानकारी देईं।",
-       "blockednoreason": "à¤\95à¤\89नà¥\8b à¤\95ारण à¤\89लà¥\8dलà¥\87à¤\96ित à¤¨à¤\88à¤\96à¥\87",
-       "whitelistedittext": "रà¤\89à¤\86 à¤ªà¤¨à¥\8dना à¤¸à¤®à¥\8dपादन à¤\95रà¥\87 à¤\96ातिर $1 à¤\95रà¥\87 à¤\95à¥\87 à¤ªà¤¡à¤¼à¥\80।",
-       "confirmedittext": "सà¤\82पादन à¤\95रà¥\87 à¤¸à¥\87 à¤ªà¤¹à¤¿à¤²à¥\87 à¤\86पà¤\95à¥\87 à¤\85ापना à¤\88-मà¥\87ल à¤ªà¤¤à¤¾ à¤ªà¥\8dरमाणित à¤\95रावल à¤\9cरà¥\81रà¥\80 à¤¬à¤¾à¥¤\nà¤\95à¥\83पया à¤\86पन [[Special:Preferences|राà¤\89र à¤ªà¤¸à¤¨à¥\8dद]] à¤®à¥\87à¤\82 à¤\9cाà¤\95à¥\87 à¤\85ापन à¤\88-मà¥\87ल à¤ªà¤¤à¤¾ à¤¦à¤¿à¤¹à¥\80à¤\82 à¤\85à¤\89र à¤\89के प्रमाणित करीं।",
-       "nosuchsectiontitle": "à¤\96णà¥\8dड à¤¨à¤¾ à¤®à¤¿à¤² à¤¸à¤\95ल।",
-       "nosuchsectiontext": "à¤\86प à¤\8fà¤\97à¥\8b à¤\85à¤\87सन à¤\85नà¥\81भाà¤\97 à¤\95à¥\87 à¤¸à¤®à¥\8dपादन à¤\95रà¥\87 à¤\95à¥\87 à¤ªà¥\8dरयतà¥\8dन à¤\95र à¤°à¤¹à¤² à¤¬à¤¾à¤¨à¥\80 à¤\9cवन à¤\85सà¥\8dतितà¥\8dव à¤®à¥\87à¤\82 à¤¨à¤\87à¤\96à¥\87।\nसà¤\82भव à¤¬à¤¾ à¤\95ि à¤\9cब à¤\86प à¤ªà¤¨à¥\8dना à¤ªà¤¢à¤¤ à¤¯à¤¾ à¤¦à¥\87à¤\96त à¤°à¤¹à¤¨à¥\80 à¤¤à¤µà¤¨à¥\87 à¤\98ड़à¥\80 à¤\89 à¤\95à¥\87 à¤\85पनà¥\80 à¤\9cà¤\97ह à¤¸à¥\87 à¤¹à¤¿à¤²à¤¾à¤µà¤² à¤\97à¤\87ल à¤¹à¥\8bà¤\96à¥\87 à¤¯à¤¾ à¤¹à¤\9fा à¤¦à¤¿à¤¹à¤² à¤\97à¤\88ल à¤¹à¥\8bà¤\88।",
+       "blockednoreason": "à¤\95वनà¥\8b à¤\95ारण à¤¨à¤\87à¤\96à¥\87 à¤¬à¤¤à¤¾à¤µà¤² à¤\97à¤\87ल",
+       "whitelistedittext": "पनà¥\8dना à¤ªà¤° à¤¸à¤\82पादन à¤\95रà¥\87 à¤\96ातिर $1 à¤\95रà¥\80à¤\82।",
+       "confirmedittext": "सà¤\82पादन à¤\95रà¥\87 à¤¸à¥\87 à¤ªà¤¹à¤¿à¤²à¥\87 à¤\86पà¤\95à¥\87 à¤\86पन à¤\88मà¥\87ल à¤ªà¤¤à¤¾ à¤ªà¥\8dरमाणित à¤\95रावल à¤\9cरà¥\81रà¥\80 à¤¬à¤¾à¥¤\nà¤\86पन [[Special:Preferences|पसà¤\82द à¤¸à¥\87à¤\9fिà¤\82à¤\97]] à¤®à¥\87à¤\82 à¤\9cाà¤\95à¥\87 à¤\85ापन à¤\88मà¥\87ल à¤ªà¤¤à¤¾ à¤¸à¥\87à¤\9f à¤\95रà¥\80à¤\82 à¤\86 à¤\93à¤\95रा के प्रमाणित करीं।",
+       "nosuchsectiontitle": "à¤\96à¤\82ड à¤¨à¤¾ à¤®à¤¿à¤²à¤²",
+       "nosuchsectiontext": "à¤\86प à¤\8fà¤\97à¥\8b à¤\85à¤\87सन à¤\96à¤\82ड à¤\95à¥\87 à¤¸à¤\82पादन à¤\95रà¥\87 à¤\95à¥\87 à¤\95à¥\8bसिस à¤\95 à¤°à¤¹à¤² à¤¬à¤¾à¤¨à¥\80 à¤\9cवन à¤®à¥\8cà¤\9cà¥\82द à¤¨à¤\87à¤\96à¥\87।\nहà¥\8b à¤¸à¤\95त à¤¬à¤¾ à¤\95ि à¤\9cवना à¤\9bन à¤\86प à¤ªà¤¨à¥\8dना à¤¦à¥\87à¤\96त à¤°à¤¹à¤²à¥\80à¤\82 à¤\93हà¥\80 à¤¸à¤®à¤¯ à¤\88 à¤\96à¤\82ड à¤\98सà¤\95ा à¤­à¤¾ à¤¹à¤\9fा à¤¦à¤¿à¤¹à¤² à¤\97à¤\87ल à¤¹à¥\8bà¤\96à¥\87।",
        "loginreqtitle": "खाता में प्रवेश जरुरी बा",
        "loginreqlink": "लॉग इन",
        "loginreqpagetext": "रउआ अन्य पन्ना देखे खातिर $1 करे के पड़ी।",
-       "accmailtitle": "गुप्त-शब्द भेजा गईल",
+       "accmailtitle": "गुप्तशब्द भेजाइल",
        "accmailtext": "[[User talk:$1|$1]] खातिर एगो यंत्र जनित गुप्तशब्द $2 के भेज दिहल गइल बा। खाता में प्रवेश कइला के बाद इ '''[[Special:ChangePassword|गुप्तशब्द बदल लीं]]'' वाला पन्ना पर बदलल जा सकत बा।",
        "newarticle": "(नया)",
        "newarticletext": "रउआ एगो अइसन कड़ी के पन्ना के अनुसरण कइले बानी जवन अभी तक उपलब्ध नइखे।\nपन्ना बनावे खातिर, नीचे के बाकस में टाइप करे के शुरु करीं (ज्यादा जानकारी खातिर देखीं [$1 मदद पन्ना])।\nयदि रउआ अहिजा गलती से आ गइल बानी त, आपन ब्राउजर के '''बैक''' (Back) बटन दबाईं!",
        "revdelete-hide-text": "संशोधन पाठ्य",
        "revdelete-hide-image": "फाइल के सामग्री छुपाँईं",
        "revdelete-hide-name": "टारगेट आ पैरामीटर छिपाईं",
-       "revdelete-hide-comment": "साराà¤\82श à¤¸à¤®à¥\8dपादन",
+       "revdelete-hide-comment": "सà¤\82पादन à¤¸à¤¾à¤°à¤¾à¤\82श",
        "revdelete-hide-user": "सम्पादक के सदस्यनाम/आइ॰पी पता",
        "revdelete-hide-restricted": "डेटा के अउरी सदस्य सभ की साथै साथ प्रबंधक लोगन खातिर भी ढाँप दीं",
        "revdelete-radio-same": "(मत बदलीं)",
        "mergehistory-from": "स्रोत पन्ना:",
        "mergehistory-into": "लक्ष्य पन्ना:",
        "mergehistory-list": "विलय जोग्य संपादन इतिहास",
+       "mergehistory-go": "बिलय करे जोग संपादन देखावल जाव",
        "mergehistory-submit": "अवतरण विलय करीं",
        "mergehistory-empty": "कौनों अवतरण विलय नइखे कइल जा सकत।",
        "mergehistory-done": " $1 के $3 {{PLURAL:$3|अवतरण|अवतरण सभ}} सफलता से [[:$2]] में विलय भइल।",
        "mergehistory-fail": "इतिहास विलय करे में अक्षम, पन्ना आ एकर टाइम पैरामीटर चेक करीं।",
+       "mergehistory-fail-bad-timestamp": "समयमोहर अबैध बा।",
+       "mergehistory-fail-invalid-source": "स्रोत पन्ना अबैध बा।",
+       "mergehistory-fail-invalid-dest": "गंतब्य पन्ना अबैध बा।",
+       "mergehistory-fail-no-change": "इतिहास बिलय द्वारा कवनो रिवीजन के बिलय ना भइल। पन्ना आ टाइम पैरामीटर के दोबारा जाँच करीं।",
+       "mergehistory-fail-permission": "इतिहास बिलय खातिर पर्याप्त परमीशन नइखे।",
+       "mergehistory-fail-self-merge": "स्रोत आ गंतब्य पन्ना एकही बा।",
+       "mergehistory-fail-timestamps-overlap": "स्रोत रिवीजन या त गंतब्य रिवीजन के साथे ओभरलैप करत बा या बाद में आवत बा।",
+       "mergehistory-fail-toobig": "{{PLURAL:$1|रिवीजन|रिवीजन सभ}} के $1 के सीमा से ढेर रिवीजन घसकावे के पड़ी आ एही कारण इतिहास बिलय नइखे कइल जा सकत।",
+       "mergehistory-no-source": "स्रोत पन्ना $1 मौजूद नइखे।",
+       "mergehistory-no-destination": "गंतब्य पन्ना $1 मौजूद नइखे।",
+       "mergehistory-invalid-source": "स्रोत पन्ना एगो बैध टाइटिल होखे के चाहीं।",
+       "mergehistory-invalid-destination": "गंतब्य पन्ना एगो बैध टाइटिल होखे के चाहीं।",
        "mergehistory-autocomment": "[[:$1]] के [[:$2]] में विलय कइल गइल",
        "mergehistory-comment": "[[:$1]] के [[:$2]] में विलय कइल गइल: $3",
        "mergehistory-same-destination": "स्रोत आ लक्ष्य पन्ना एकही ना होखे सकत बा",
        "mergelogpagetext": "एक पन्ना इतिहास के दुसर पन्ना इतिहास में तुरंत विलय भइले के एगो सूची नीचे दिहल बा।",
        "history-title": "''$1'' के संशोधन इतिहास",
        "difference-title": "\"$1\" की अवतरण में अंतर",
+       "difference-title-multipage": "\"$1\" आ \"$2\" पन्ना सभ के बीच अंतर",
+       "difference-multipage": "(पन्नवन के बीच अंतर)",
        "lineno": "लाइन $1:",
        "compareselectedversions": "चुनल गइल संशोधन में अंतर देखीं",
        "showhideselectedversions": "चुनल गइल संशोधन के दृश्यता बदलीं",
        "editundo": "वापस लीं",
+       "diff-empty": "(कौनों अंतर नइखे)",
        "diff-multi-sameuser": "(एही सदस्य द्वारा कइल {{PLURAL:$1|बीच के एगो बदलाव|बीच के $1 बदलाव}} नइखे देखावल जात)",
        "searchresults": "खोज परिणाम",
        "searchresults-title": "$1 खातिर खोज परिणाम",
        "search-section": "(खंड $1)",
        "search-category": "(श्रेणी $1)",
        "search-suggest": "का राउर मतलब बा: $1",
+       "search-rewritten": "$1 खातिर रिजल्ट। एकरे जगह $2 खातिर खोज करीं।",
        "search-interwiki-caption": "साथी प्रोजेक्ट सभ से रिजल्ट",
        "search-interwiki-default": "$1 से परिणाम:",
        "search-interwiki-more": "(अउर)",
        "powersearch-togglenone": "कउनो ना",
        "search-external": "बाहरी खोज",
        "preferences": "वरीयता",
-       "mypreferences": "हमार सेटिंग",
-       "prefs-edits": "सम्पादन संख्या",
-       "prefsnologintext2": "आपन वरीयता में बदलाव लावे खातिर प्रवेश करीं।",
-       "prefs-skin": "त्वचा",
-       "skin-preview": "पूर्वावलोकन",
-       "datedefault": "वरीयता नईखे",
-       "prefs-user-pages": "सदस्य पन्ना",
+       "mypreferences": "पसंदसेटिंग",
+       "prefs-edits": "संपादन संख्या",
+       "prefsnologintext2": "आपन पसंदसेटिंग बदले खातिर खाता में प्रवेश करीं।",
+       "prefs-skin": "स्किन",
+       "skin-preview": "झलक",
+       "datedefault": "कौनो खास पसंद नइखे",
+       "prefs-labs": "लैब्स के चीज",
+       "prefs-user-pages": "प्रयोगकर्ता पन्ना",
        "prefs-personal": "प्रयोगकर्ता प्रोफाइल",
        "prefs-rc": "हाल के बदलाव",
        "prefs-watchlist": "धियानसूची",
        "prefs-editwatchlist": "धियानसूची संपादन",
-       "prefs-editwatchlist-label": "à¤\85पनà¥\80 à¤§à¤¿à¤¯à¤¾à¤¨à¤¸à¥\82à¤\9aà¥\80 à¤\95à¥\87 à¤\9aà¥\80à¤\9c संपादित करीं:",
-       "prefs-editwatchlist-edit": "à¤\85पनà¥\80 à¤§à¤¿à¤¯à¤¾à¤¨à¤¸à¥\82à¤\9aà¥\80 à¤\95à¥\87 टाइटिल देखीं आ हटाईं",
+       "prefs-editwatchlist-label": "à¤\86पन à¤§à¤¿à¤¯à¤¾à¤¨à¤¸à¥\82à¤\9aà¥\80 à¤®à¥\87à¤\82 à¤¸à¤¾à¤®à¤¿à¤² à¤\8fà¤\82à¤\9fà¥\8dरà¥\80 संपादित करीं:",
+       "prefs-editwatchlist-edit": "à¤\86पन à¤§à¤¿à¤¯à¤¾à¤¨à¤¸à¥\82à¤\9aà¥\80 à¤®à¥\87à¤\82 à¤¸à¤¾à¤®à¤¿à¤² टाइटिल देखीं आ हटाईं",
        "prefs-editwatchlist-raw": "टटका धियानसूची संपादित करीं",
        "prefs-editwatchlist-clear": "आपन धियानसूची साफ करीं",
        "prefs-watchlist-days": "धियानसूची में देखावे खातिर दिन",
        "restoreprefs": "सगरी डिफाल्ट सेटिंग पहिले जइसन करीं (सगरी खंड में)",
        "prefs-editing": "संपादन",
        "searchresultshead": "खोज",
+       "stub-threshold": "आधार कड़ी फारमेटिंग($1) खातिर थ्रेशोल्ड:",
        "stub-threshold-sample-link": "नमूना",
        "stub-threshold-disabled": "अक्षम",
-       "recentchangesdays": "हाल à¤®à¥\87à¤\82 à¤­à¤\87ल à¤ªà¤°à¤¿à¤µà¤°à¥\8dतन में देखावे खातिर दिन:",
+       "recentchangesdays": "हाल à¤\95à¥\87 à¤¬à¤¦à¤²à¤¾à¤µ में देखावे खातिर दिन:",
        "recentchangesdays-max": "अधिकतम $1{{PLURAL:$1|दिन}}",
        "recentchangescount": "डिफाल्ट में देखावे खातिर संपादन संख्या:",
-       "prefs-help-recentchangescount": "à¤\8fमà¥\8dमà¥\87à¤\82 à¤¹à¤¾à¤² à¤®à¥\87à¤\82 à¤­à¤\87ल à¤ªà¤°à¤¿à¤µà¤°à¥\8dतन, à¤ªà¤¨à¥\8dना इतिहास, आ लॉग सब बाटे।",
+       "prefs-help-recentchangescount": "à¤\8fमà¥\8dमà¥\87à¤\82 à¤¹à¤¾à¤² à¤®à¥\87à¤\82 à¤­à¤\87ल à¤¬à¤¦à¤²à¤¾à¤µ, à¤ªà¤¨à¥\8dना à¤\95à¥\87 इतिहास, आ लॉग सब बाटे।",
        "savedprefs": "राउर वरीयताएँ सुरक्षित कर दिहल गईल।",
        "timezonelegend": "समय जोन:",
        "localtime": "लोकल समय:",
        "userrights-user-editname": "प्रयोगकर्ता नाँव लिखीं:",
        "editusergroup": "प्रयोगकर्ता मंडली लोड करीं",
        "editinguser": "अधिकार बदलाव {{GENDER:$1|प्रयोगकर्ता}}<strong>[[User:$1|$1]]</strong> $2",
+       "viewinguserrights": "प्रयोगकर्ता अधिकार देखावल जात बा:{{GENDER:$1|प्रयोगकर्ता}} <strong>[[User:$1|$1]]</strong> $2",
+       "userrights-editusergroup": "{{GENDER:$1|प्रयोगकर्ता}} मंडली संपादित करीं",
+       "userrights-viewusergroup": "{{GENDER:$1|प्रयोगकर्ता}} मंडली देखीं",
+       "saveusergroups": "{{GENDER:$1|प्रयोगकर्ता}} मंडली सहेजीं",
+       "userrights-groupsmember": "सदस्यता हासिल बा:",
+       "userrights-groupsmember-auto": "निहित सदस्यता हासिल बा:",
+       "userrights-groups-help": "ई प्रयोगकर्ता कवना मंडली में रहिहें ई चीज आप बदल सकत बानी:\n* सही के निसान वाला बक्सा के मतलब बा एह मंडली में ई शामिल बाने।\n* बिना सही के निसान वाला बक्सा के मतलब बा एह मंडली में ई शामिल नइखें।\n* एक ठो * के चीन्हा अइसन अधिकार के सूचित करे ला जवना के आप एक बेर दे देइब त हटा नइखीं सकत, या एकरे ठीक उल्टा भी।\n* एक ठो # के चीन्हा सूचित करे ला कि एह मंडली के सदस्यता के खतम होखे के समय आप पाछे (बाद में) क सकत बानी लेकिन आगे नइखीं ले आ सकत।",
        "userrights-reason": "कारण:",
-       "group": "मंडली (ग्रुप):",
+       "userrights-no-interwiki": "अन्य विकि सभ पर सदस्य मंडली के संपादन करे खातिर रउवाँ लगे परमीशन नइखे।",
+       "userrights-changeable-col": "जवन मंडली आप बदल सकत बानी",
+       "userrights-unchangeable-col": "जवन मंडली आप नइखीं बदल सकत",
+       "userrights-expiry-current": "$1 के खतम होखी",
+       "userrights-expiry-none": "एक्सपायर ना होखी",
+       "userrights-expiry": "एक्सपायर हो जाई:",
+       "userrights-expiry-existing": "वर्तमान में एकरा एक्सपायर होखे के टाइम बा: $3, $2",
+       "userrights-expiry-othertime": "अन्य समय:",
+       "userrights-expiry-options": "1 दिन:1 दिन,1 हप्ता:1 हप्ता,1 महीना:1 महीना,3 महीना:3 महीना,6 महीना:6 महीना,1 बरिस:1 बरिस",
+       "userrights-invalid-expiry": "मंडली \"$1\" खातिर एक्सपायरी समय अबैध बा।",
+       "group": "मंडली:",
        "group-user": "सदस्य",
-       "group-autoconfirmed": "à¤\96à¥\81द à¤ªà¤°à¥\80à¤\95à¥\8dषित à¤¸à¤¦à¤¸à¥\8dय",
+       "group-autoconfirmed": "सà¥\8bतà¤\83परमाणित à¤ªà¥\8dरयà¥\8bà¤\97à¤\95रà¥\8dता",
        "group-bot": "बॉट",
        "group-sysop": "प्रबंधक",
        "group-bureaucrat": "ब्यूरोक्रेट",
        "group-suppress": "सप्रेसर",
        "group-all": "(सब)",
        "group-user-member": "{{GENDER:$1|सदस्य}}",
-       "group-autoconfirmed-member": "{{GENDER:$1|à¤\96à¥\81द à¤\85सà¥\8dथापित à¤¸à¤¦à¤¸à¥\8dय}}",
+       "group-autoconfirmed-member": "{{GENDER:$1|सà¥\8bतà¤\83परमाणित à¤ªà¥\8dरयà¥\8bà¤\97à¤\95रà¥\8dता}}",
        "group-bot-member": "{{GENDER:$1|बॉट}}",
        "group-sysop-member": "{{GENDER:$1|प्रबंधक}}",
        "group-bureaucrat-member": "{{GENDER:$1|प्रशासक}}",
        "group-suppress-member": "{{GENDER:$1|सप्रेस}}",
        "grouppage-user": "{{ns:project}}:सदस्य सभ",
-       "grouppage-autoconfirmed": "{{ns:project}}:à¤\96à¥\81द à¤\85सà¥\8dथापित à¤¸à¤¦à¤¸à¥\8dय à¤¸à¤­",
+       "grouppage-autoconfirmed": "{{ns:project}}:सà¥\8bतà¤\83परमाणित à¤ªà¥\8dरयà¥\8bà¤\97à¤\95रà¥\8dता",
        "grouppage-bot": "{{ns:project}}:बॉट सभ",
        "grouppage-sysop": "{{ns:project}}:प्रबंधक सभ",
        "grouppage-bureaucrat": "{{ns:project}}:प्रशासक सभ",
        "right-createpage": "पन्ना बनाईं (बातचीत पन्ना की अलावा)",
        "right-createtalk": "बातचीत पन्ना बनाईं",
        "right-createaccount": "नया सदस्य खाता बनाईं",
-       "right-minoredit": "à¤\9bà¥\8bà¤\9f à¤¸à¤\82पादन चिह्नित करीं",
+       "right-minoredit": "सà¤\82पादन à¤\9bà¥\8bà¤\9f चिह्नित करीं",
        "right-move": "पन्ना स्थानांतरण करीं",
        "right-move-subpages": "पन्नवन के उनहन की उपपन्नवन की संघे स्थानांतरित करीं",
        "right-move-rootuserpages": "मूल (root) सदस्य पन्नवन के स्थानांतरित करीं",
        "right-movefile": "फाइल सब स्थानांतरित करीं",
        "right-suppressredirect": "स्थानांतरण करत घरी मूल पन्ना से पुनर्निदेश मत बनाईं",
        "right-upload": "फाइल अपलोड करीं",
-       "right-reupload": "पà¥\81रान à¤«à¤¾à¤\87ल à¤\95à¥\80 à¤\8aपर à¤¨à¤¯à¤¾ à¤²à¤¾à¤¦ीं",
-       "right-reupload-own": "खुदे लादल फाइल पर नया फाइल लादीं",
-       "right-reupload-shared": "लà¥\8bà¤\95ल à¤®à¥\87à¤\82 à¤¸à¤¾à¤\9dा à¤®à¥\80डिया à¤­à¤£à¥\8dडार à¤\95à¥\87 à¤«à¤¾à¤\87ल à¤¸à¤­ à¤\95à¥\87 ओवरराइड करीं",
+       "right-reupload": "मà¥\8cà¤\9cà¥\82द à¤«à¤¾à¤\87ल à¤ªà¤° à¤\93भरराà¤\87à¤\9f à¤\95रीं",
+       "right-reupload-own": "खुद के अपलोड कइल फाइल ओभरराइट करीं",
+       "right-reupload-shared": "साà¤\9dा à¤®à¥\80डिया à¤­à¤\82डार à¤\95à¥\87 à¤«à¤¾à¤\87ल à¤¸à¤­ à¤\95à¥\87 à¤²à¥\8bà¤\95ल ओवरराइड करीं",
        "right-upload_by_url": "यूआरयल से फाइल अपलोड करीं",
        "right-purge": "बिना पुछले कौनों पन्ना के साइट कैश के फिर लोड करीं",
-       "right-autoconfirmed": "आइ पी आधारित रेट के सीमा से प्रभावित ना होखे",
+       "right-autoconfirmed": "आइपी-आधारित रेट सीमा से ना परभावित",
        "right-bot": "ऑटोमेटेड प्रोसेस मानल जाय",
        "right-writeapi": "API लेखन के इस्तेमाल",
        "right-delete": "पन्ना हटाईं",
        "action-movefile": "ई फाइल स्थानांतरित करीं",
        "action-upload": "इ फाइल अपलोड करीं",
        "action-reupload": "पहिले से मौजूद ए फाइल पर दूसर लादीं",
-       "action-delete": "ई पन्ना के मिटाईं",
+       "action-delete": "ए पन्ना के मिटाईं",
+       "action-deleterevision": "रिवीजन मेटाईं",
+       "action-deletelogentry": "लॉग के एंट्री मेटाईं",
        "action-unwatchedpages": "ध्यानसूची में जवन पन्ना नइखे ओकर सूची देखीं",
        "enhancedrc-history": "इतिहास",
        "recentchanges": "हाल के बदलाव",
        "ancientpages": "सबसे पुरान संशोधन वाला पन्ना",
        "move": "स्थानांतरण",
        "movethispage": "एह पन्ना के स्थानांतरण करीं",
-       "suppress": "à¤\93वरसाà¤\87à¤\9fर",
+       "suppress": "सपà¥\8dरà¥\87स",
        "apihelp": "एपीआइ (API) मदद",
        "apihelp-no-such-module": "मॉड्युल $1 ना मिलल।",
        "booksources": "किताबी स्रोत",
index 8c967f0..b9b8980 100644 (file)
        "oct": "Pthi",
        "nov": "Ptht",
        "dec": "Pthr",
-       "pagecategories": "{{PLURAL:$1|Atëkthok|Atëkthuɔk}}",
-       "category_header": "Apääm në atëkthok \"$1\"ic",
-       "subcategories": "Tëktëëkkor",
+       "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",
-       "hidden-categories": "{{PLURAL:$1|Atëkthok cï thiaan|Atëkthuɔk cï thiaan}}",
-       "category-subcat-count": "{{PLURAL:$2|Bekätakthookë anɔŋ bekätakthookthiikɛ̈ kepɛ̈c.|Akuutkäŋë anɔŋ \n{{PLURAL:$1|bekätakthookë|$1 bekäŋtakthookkɛ̈}}, në $2 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.}}",
        "category-file-count": "{{PLURAL:$2|Bekätakthook kän anɔŋic wëtmät kän etök.|{{PLURAL:|Wëtmät de $1 thiöökë atɔ̈ |wëlmäät ke $1 thiookkɛ̈ aa tɔ̈}} në bekätakthook känic, në $2 yiic ëbɛ̈n.}}",
        "listingcontinuesabbrev": "ɣäthtueŋ",
        "nstab-project": "Apam kälooi",
        "nstab-image": "Apamduööt",
        "nstab-template": "Macuëc",
-       "nstab-category": "Atëkthok",
+       "nstab-category": "Bekätakthook",
        "mainpage-nstab": "Apam këdït",
        "badtitle": "Rin awäc",
        "badtitletext": "Këjiɛmë ca thiëëcë acïï lɔcök, tëdɛ̈ ka cïn kë tɔ̈u thïn, tëdɛ̈ ka këjiɛmë de thuɔkmɛ̈t wälä de wikimɛ̈t aa këcëkɛ nyiɛc nuet apath. Tëkdɛ̈ kä nɔŋic cït cïï lëu bïke luööi në käjiɛmëkeyiic.",
        "templatesused": "{{PLURAL:$1|Macuëc|Mïcuëc}} ee luööi në apam känic",
        "template-protected": "(cïtiit)",
        "template-semiprotected": "(gëlamääth)",
-       "hiddencategories": "Apamkën ee rem ë {{PLURAL:$1|1 bekätakthook cï thiaan |$1 bekäŋtakthook cï  thiaan}}:",
-       "permissionserrorstext-withaction": "Yïn acïï nɔŋ nhomlääu ba $2, \n{{PLURAL:$1|wɛ̈t de kän|wët de käk}}:",
+       "hiddencategories": "Apamkën ee rem ë {{PLURAL:$1|1 bekätakthook cï thiaan |$1 bekätakthuɔk cï thiaan}}:",
+       "permissionserrorstext-withaction": "Yïn acïï nɔŋ nhomlääu ba $2, {{PLURAL:$1|wɛ̈t de kän|wët de käk}}:",
        "moveddeleted-notice": "Apam acï cuɔthwei. Athörtɔ̈ɔ̈u de cothëwei ku nyiɛɛi në apamë aa cïke gam piiny ëtɛ̈n tɔŋ raan wïc bë ke kueen",
        "content-model-javascript": "JavaScript",
        "viewpagelogs": "Ɣoi athörtɔ̈ɔ̈u në apamkën",
        "logeventslist-submit": "Nyooth",
        "allarticles": "Abɛ̈ɛ̈k ëbɛ̈n",
        "allpagessubmit": "Lɔ",
-       "categories": "Atëkthuɔk",
+       "categories": "Bekätakthuɔk",
        "categories-submit": "Nyooth",
        "sp-deletedcontributions-contribs": "amöc",
        "linksearch-ns": "Rinɣɔnläu",
        "tooltip-ca-nstab-project": "Ɣoi apam kätɔ̈",
        "tooltip-ca-nstab-image": "Ɣoië apäm ë makec",
        "tooltip-ca-nstab-template": "Tïŋ macuëc",
-       "tooltip-ca-nstab-category": "Ɣoië apäm atëkthok",
+       "tooltip-ca-nstab-category": "Ɣoië apäm bekätakthook",
        "tooltip-save": "Tɔ̈ɔ̈uë weerdu",
        "tooltip-preview": "Tiɛ̈ŋë tueŋ weerdu. Yïn looië yen këcë guor tɔ̈ɔ̈u.",
        "tooltip-diff": "Nyoothë weer ɣo cä looi këcïgɔ̈tic",
        "exif-colorspace": "Tëlääu kiit",
        "exif-datetimeoriginal": "Akölnïn ku akölic ë cäk ë akutëyith",
        "exif-datetimedigitized": "Akölnïn ku akölic ë cɔkakuënnhialkupiny",
-       "exif-iimcategory": "Atëkthok",
+       "exif-iimcategory": "Bekätakthook",
        "exif-disclaimer": "Acëkakuɔ",
        "exif-orientation-1": "Epath",
        "namespacesall": "ëbɛ̈n",
index 4d51c9e..263a1c1 100644 (file)
        "rcfilters-invalid-filter": "Invalid filter",
        "rcfilters-empty-filter": "No active filters. All contributions are shown.",
        "rcfilters-filterlist-title": "Filters",
-       "rcfilters-filterlist-whatsthis": "What's this?",
+       "rcfilters-filterlist-whatsthis": "How do these work?",
        "rcfilters-filterlist-feedbacklink": "Provide feedback on the new (beta) filters",
        "rcfilters-highlightbutton-title": "Highlight results",
        "rcfilters-highlightmenu-title": "Select a color",
        "rcfilters-noresults-conflict": "No results found because the search criteria are in conflict",
        "rcfilters-state-message-subset": "This filter has no effect because its results are included with those of the following, broader {{PLURAL:$2|filter|filters}} (try highlighting to distinguish it): $1",
        "rcfilters-state-message-fullcoverage": "Selecting all filters in a group is the same as selecting none, so this filter has no effect. Group includes: $1",
-       "rcfilters-filtergroup-registration": "User registration",
-       "rcfilters-filter-registered-label": "Registered",
-       "rcfilters-filter-registered-description": "Logged-in editors.",
-       "rcfilters-filter-unregistered-label": "Unregistered",
-       "rcfilters-filter-unregistered-description": "Editors who aren’t logged in.",
-       "rcfilters-filter-unregistered-conflicts-user-experience-level": "This filter conflicts with the following Experience {{PLURAL:$2|filter|filters}}, which {{PLURAL:$2|finds|find}} only registered users: $1",
        "rcfilters-filtergroup-authorship": "Contribution authorship",
        "rcfilters-filter-editsbyself-label": "Changes by you",
        "rcfilters-filter-editsbyself-description": "Your own contributions.",
        "rcfilters-filter-editsbyother-label": "Changes by others",
        "rcfilters-filter-editsbyother-description": "All changes except your own.",
-       "rcfilters-filtergroup-userExpLevel": "Experience level (for registered users only)",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "Experience filters find only registered users, so this filter conflicts with the “Unregistered” filter.",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "The \"Unregistered\" filter conflicts with one or more Experience filters, which find registered users only. The conflicting filters are marked in the Active Filters area, above.",
+       "rcfilters-filtergroup-userExpLevel": "Experience registration and experience",
+       "rcfilters-filter-user-experience-level-registered-label": "Registered",
+       "rcfilters-filter-user-experience-level-registered-description": "Logged-in editors.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Unregistered",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Editors who aren't logged-in.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Newcomers",
-       "rcfilters-filter-user-experience-level-newcomer-description": "Fewer than 10 edits and 4 days of activity.",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Registered editors with fewer than 10 edits and 4 days of activity.",
        "rcfilters-filter-user-experience-level-learner-label": "Learners",
-       "rcfilters-filter-user-experience-level-learner-description": "More experience than \"Newcomers\" but less than \"Experienced users\".",
+       "rcfilters-filter-user-experience-level-learner-description": "Registered editors whose experience falls between \"Newcomers\" and \"Experienced users.\"",
        "rcfilters-filter-user-experience-level-experienced-label": "Experienced users",
-       "rcfilters-filter-user-experience-level-experienced-description": "More than 30 days of activity and 500 edits.",
+       "rcfilters-filter-user-experience-level-experienced-description": "Registered editors with more than 500 edits and 30 days of activity.",
        "rcfilters-filtergroup-automated": "Automated contributions",
        "rcfilters-filter-bots-label": "Bot",
        "rcfilters-filter-bots-description": "Edits made by automated tools.",
        "rcfilters-tag-prefix-namespace": ":$1",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:not</strong> $1",
        "rcfilters-tag-prefix-tags": "#$1",
+       "rcfilters-exclude-button-off": "Exclude selected",
+       "rcfilters-exclude-button-on": "Excluding selected",
        "rcfilters-view-tags": "Tagged edits",
        "rcfilters-view-namespaces-tooltip": "Filter results by namespace",
        "rcfilters-view-tags-tooltip": "Filter results using edit tags",
index eb36a71..d47751e 100644 (file)
        "undelete-search-title": "Ezabatutako orrialdeak bilatu",
        "undelete-search-box": "Ezabatutako orrialdeak bilatu",
        "undelete-search-prefix": "Honela hasten diren orrialdeak erakutsi:",
+       "undelete-search-full": "Orrialde izenburuak erakutsi:",
        "undelete-search-submit": "Bilatu",
        "undelete-no-results": "Ez da bat datorren orrialderik aurkitu ezabaketen artxiboan.",
        "undelete-filename-mismatch": "Ezin da $1 denbora-marka duten fitxategi aldaketa ezabatua berrezarri: fitxategi-izena ez dator bat",
index 44fe822..dec017d 100644 (file)
        "printableversion": "Sufar bugawa",
        "permalink": "Dawwamammen mahaɗi",
        "print": "Buga",
+       "view": "Duba",
        "edit": "Gyarawa",
        "create": "Ƙirƙira",
        "delete": "Soke",
        "showdiff": "Nuna sauye-sauye",
        "anoneditwarning": "'''Hattara:''' Ba ku yi logi ba.\nZa a rubuta adireshinku na IP a cikin tarihin sauye-sauyen wannan shafi.",
        "summary-preview": "Rigya-gani na taƙaici:",
+       "loginreqlink": "Shiga",
        "newarticle": "(Sabo)",
        "newarticletext": "Kun latsa mahaɗi zuwa shafin da babu shi tukuna.\nDomin ƙirƙiro wannan shafin, ku fara rubutu a cikin fage na ƙasa (duba [$1 shafin taimako] don ƙarin bayani).\nIdan kun ɓata ne cikin shawaginku, to ku latsa maɓallin '''baya''' na safuwayan shawaginku.",
        "noarticletext": "A halin yanzu babu matani a kan wannan shafi.\nKuna iya [[Special:Search/{{PAGENAME}}|nemo kan wannan shafi]] cikin wasu shafuna,\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} bincika rajistan ayyukan],\nko [{{fullurl:{{FULLPAGENAME}}|action=edit}} gyara wannan shafi]</span>.",
        "prevn": "baya {{PLURAL:$1|$1}}",
        "nextn": "gaba {{PLURAL:$1|$1}}",
        "viewprevnext": "Duba ($1 {{int:pipe-separator}} $2) ($3)",
+       "searchprofile-everything": "Duk abin da",
        "search-result-size": "$1 ({{PLURAL:$2|1 kalma|$2 kalmomi}})",
        "search-redirect": "(turawa daga $1)",
        "search-section": "(sashe $1)",
        "rightslog": "Rajistan bayar da izini ga ma'aikata",
        "action-edit": "gyara wannan shafi",
        "nchanges": "{{PLURAL:$1|sauyi|sauye-sauye}} $1",
+       "enhancedrc-history": "Tarihi",
        "recentchanges": "Sauye-sauyen baya-bayan nan",
        "recentchanges-legend": "Zaɓi na sauye-sauyen baya-bayan nan",
        "recentchanges-feed-description": "Bi sawun sauye-sauyen ƙarshe na wikin da ke cikin wannan kwarare",
+       "recentchanges-label-minor": "Karamin gyara ne",
        "rclistfrom": "Nuna sabbin sauye-sauye tun daga $3 $2",
        "rcshowhideminor": "$1 ƙananen sauye-sauye",
        "rcshowhidebots": "Rabuwat $1",
+       "rcshowhidebots-show": "Nuna",
        "rcshowhideliu": "$1 Ma'aikata logaggi",
        "rcshowhideanons": "$1 ma'aikata masu ɓoyayye suna",
        "rcshowhidemine": "$1 sauye-sauyena",
        "tooltip-search": "Binciko {{SITENAME}}",
        "tooltip-search-go": "A je ga shafi mai wannan suna idan akwai shi",
        "tooltip-search-fulltext": "Binciki shafuna masu wannan matani",
+       "tooltip-p-logo": "Duba babban shafin",
        "tooltip-n-mainpage": "Duba shafin Marhabin",
        "tooltip-n-mainpage-description": "Duba shafin marhabin",
        "tooltip-n-portal": "A game da wannan shiri, abinda za a iya yi, ina za a samu abubuwa",
index 4feb7b8..37b4749 100644 (file)
        "anontalk": "Дувца оттадар",
        "navigation": "Навигаци",
        "and": "&#32;а",
-       "qbfind": "Лахар",
-       "qbbrowse": "БIаргтохар",
-       "qbedit": "Нийсде",
-       "qbpageoptions": "ОагIон оттамаш",
-       "qbmyoptions": "Хьа гIирсаш тоаяраш",
        "faq": "КТХ",
-       "faqpage": "Project:КТХ",
        "actions": "Ардамаш",
        "namespaces": "ЦIерий аренаш",
        "variants": "Варианташ",
        "edit-local": "Хувца локальни йоазонца сурт оттадар",
        "create": "Хьакхолла",
        "create-local": "ТIатоха локальни йоазонца сурт оттадар",
-       "editthispage": "Нийсъе ер оагIув",
-       "create-this-page": "Хьакхолла ер оагӀув",
        "delete": "ДӀаяккха",
-       "deletethispage": "ДӀаяккха ер оагӀув",
-       "undeletethispage": "Юхаметтаоттае ер оагӀув",
        "undelete_short": "Юхаметтаоттде {{PLURAL:$1|$1 нийсдар|$1 нийсдараш}}",
        "viewdeleted_short": "{{PLURAL:$1|$1 дIадаьккха нийсдарга|дIадаьккха нийсдарга|$1 дIадаьккха нийсдарашга}} хьажар",
        "protect": "ГIо де",
        "protect_change": "хувца",
-       "protectthispage": "ГIо (лорадар) де укх оагIон",
        "unprotect": "ГIо хувца",
-       "unprotectthispage": "Укх оагIон гIо (лорадар) хувца",
        "newpage": "Керда оагӀув",
-       "talkpage": "Ер оагIув ювца",
        "talkpagelinktext": "дувца оттадар",
        "specialpage": "ГIулакха оагӀув",
        "personaltools": "Доакъашхочун гӀирсаш",
-       "articlepage": "БIаргтоха оагIонга",
        "talk": "Дувца оттадар",
        "views": "Хьажараш",
        "toolbox": "ГӀирсаш",
-       "userpage": "Доакъашхочун оагIон бIаргтоха",
-       "projectpage": "Проекта оагIон бIаргтоха",
        "imagepage": "Файла оагIон бIаргтоха",
        "mediawikipage": "Хьахьокха хоам бара оагIув",
        "templatepage": "Лера оагIон бIаргтоха",
        "redirectedfrom": "($1 дIа-сахьожаяьй укхаз)",
        "redirectpagesub": "ОагIув-дIа-сахьожадар",
        "redirectto": "ДIа-сахьожадар укхаза:",
-       "lastmodifiedat": "УкÑ\85 Ð¾Ð°Ð³Ioн Ñ\82IеÑ\85Ñ\85Ñ\8cаÑ\80а Ñ\85Ñ\83вÑ\86ам: $2, $1.",
+       "lastmodifiedat": "Ð\95Ñ\80 Ð¾Ð°Ð³IÑ\83в Ñ\82IеÑ\85Ñ\85Ñ\8cаÑ\80а Ñ\85ийÑ\86а Ñ\85иннай $2 $1 Ñ\8fÑ\8cннаÑ\87а Ñ\85аÌ\81на.",
        "viewcount": "Укх оагIонга хьежа хиннаб $1{{PLURAL:$1|-зза}}.",
        "protectedpage": "ГIо оттадаь лораяь оагIув",
        "jumpto": "Дехьавала укхаза:",
        "noarticletext-nopermission": "ХIанз укх оагӀон тӀа текст яц.\nШун аьттув ба [[Special:Search/{{PAGENAME}}|цу тайпара цӀи белгалъяр хьалаха]] кхыйола оагIонаш тIа, иштта\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} тара дола тептарай дIаяздаьраш].</span> Ер оагӀув хьакхолла Хьа бокъо яц.",
        "note": "'''Белгалдоахар:'''",
        "previewnote": "'''Теркам бе, ер хьалххе бIаргтохар мара бац.'''\nХьа хувцамаш хIанза а дIаяздаь дац!",
+       "continue-editing": "Хувцар кхы дIахо де",
        "editing": "Хувцам: $1",
        "creating": "«$1» оагIув хьакхоллар",
        "editingsection": "Хувцам: $1 (оагӀон дáкъа)",
        "page_first": "цхьоаллагIа",
        "page_last": "тӀехьара",
        "histlegend": "Версий хоржам: белгалъе шун вIаши йиста безам бола оагIон версеш, тIаккха тоIае '''{{int:compare-submit}}'''.<br />\nКхетавар: '''({{int:cur}})''' — карара версеца дола башхалонаш; '''({{int:last}})''' — хьалха йоагIаш версеца дола башхалонаш; '''{{int:minoreditletter}}''' — зIамига хувцамаш.",
-       "history-fieldset-title": "Ð\98Ñ\81Ñ\82оÑ\80ена Ð±IаÑ\80гÑ\82оÑ\85а",
+       "history-fieldset-title": "Ð\94аÑ\8c Ñ\85инна Ñ\85Ñ\83вÑ\86амаÑ\88 Ð»Ð°Ñ\85аÑ\80",
        "history-show-deleted": "Алхха дӀадаьккхараш",
        "histfirst": "эггара къаьнагIа",
        "histlast": "эггара кердагIа",
        "lineno": "МугI $1:",
        "compareselectedversions": "ВIаши йиста хержа версеш",
        "editundo": "юхадаккха",
+       "diff-empty": "(башхалонаш яц)",
        "diff-multi-sameuser": "({{PLURAL:$1|цхьа юкъ хулаш йола верси|$1 юкъ хулаш йола версеш}} гуш яц цу доакъашхочун)",
        "searchresults": "Лахар чакхдоалаш корадаьр",
        "searchresults-title": "«$1» лахар",
        "nextn-title": "{{PLURAL:$1|ТIадоагIа $1 яздар|ТIадоагIа $1 яздараш}}",
        "shown-title": "Гойта $1 {{PLURAL:$1|яздаьр|яздаьраш}} укх оáгIон тIа",
        "viewprevnext": "ДIахьажа ($1 {{int:pipe-separator}} $2) ($3)",
-       "searchmenu-exists": "<strong>Укх вики чу йолаш я оагӀув «[[:$1]]».</strong>",
+       "searchmenu-exists": "'''Укх вики чу йолаш я оагӀув «[[:$1]]».'''",
        "searchmenu-new": "<strong>Хьакхолла оагIув «[[:$1]]» укх вики-проекте!</strong>\n{{PLURAL:$2|0=|Иштта хьажа Iайха лийха оагIонга.|Иштта хьажа хьай лахара хьахиннарашка.}}",
        "searchprofile-articles": "Кертера оагIонаш",
        "searchprofile-images": "Мультимедиа",
        "nolinkstoimage": "Укх файла тIахьожавеш йола оагIонаш яц.",
        "sharedupload": "Ер файл $1 чура я, из пайда эцаш лелае мегаш я кхыйола проекташ чу.",
        "sharedupload-desc-here": "Ер файл $1 чура я, иштта кхыйола проекташ чу пайда эца аьттув болаш я.\nЦун [$2 сурт оттадара оагIон] хоам кIалхахь хьабоалабаьб.",
+       "filepage-nofile": "Ишта цӀи йола файл йоацаш я.",
        "uploadnewversion-linktext": "Чуяккха укх файла керда верси",
        "upload-disallowed-here": "Хьа бокъо яц ер файл юха дӀаязъе.",
        "filerevert-comment": "Бахьан:",
        "booksources-search-legend": "Джейнах лаьца хоам лахар",
        "booksources-search": "Хьалáха",
        "log": "Тептараш",
+       "logempty": "Укх оагӀон дӀаяздаьраш тептара чу дац.",
        "allpages": "Еррига оагIонаш",
        "prevpage": "Хьалха йоагIа оагIув ($1)",
        "allpagesfrom": "Гучаяккха оагIонаш йолалуш йола укхох:",
        "version-specialpages": "ГIулакха оагӀонаш",
        "version-version": "($1)",
        "version-software-version": "Верси",
+       "redirect-submit": "Дехьавала",
+       "redirect-lookup": "Лахар:",
+       "redirect-value": "Боарам:",
+       "redirect-user": "Доакъашхочун ID",
+       "redirect-page": "ОагӀон ID",
+       "redirect-revision": "ОагӀон верси",
+       "redirect-file": "Файла цӀи",
        "fileduplicatesearch-filename": "Файла цӀи:",
        "fileduplicatesearch-submit": "Хьалáха",
        "specialpages": "ЛаьрххIа йола оагIонаш",
index 74b9d41..c8e6b86 100644 (file)
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} ([[Special:NewPages|새 문서 목록]]도 보세요)",
        "recentchanges-legend-plusminus": "(<em>±123</em>)",
        "recentchanges-submit": "보기",
+       "rcfilters-legend-heading": "<strong>약어 목록:</strong>",
        "rcfilters-activefilters": "사용 중인 필터",
        "rcfilters-advancedfilters": "고급 필터",
        "rcfilters-quickfilters": "저장된 필터",
index 44335fc..182b910 100644 (file)
        "rcfilters-invalid-filter": "A label for an invalid filter.",
        "rcfilters-empty-filter": "Placeholder for the filter list when no filters were chosen.",
        "rcfilters-filterlist-title": "Title for the filters list.\n{{Identical|Filter}}",
-       "rcfilters-filterlist-whatsthis": "Caption for the link that opens a popup with explanations about this filter group.",
+       "rcfilters-filterlist-whatsthis": "Caption for the link that opens a popup with explanations about this filter group, explaining what it is and how it works.",
        "rcfilters-filterlist-feedbacklink": "Caption for the link to the feedback page about the filters beta feature.",
        "rcfilters-highlightbutton-title": "Title for the highlight button used to toggle the highlight feature on and off.",
        "rcfilters-highlightmenu-title": "Title for the highlight menu used to select the highlight color for an individual filter.",
        "rcfilters-noresults-conflict": "A message displayed in the results area when no results found because there are filters in conflict with one another.",
        "rcfilters-state-message-subset": "Tooltip shown when hovering over a filter tag when one or more broader filters that contain the hovered filter are also selected. This indicates that the hovered filter has no effect because all the results it matches are also matched by the broader filter(s).  Parameters:\n* $1 - Comma-separated string of selected broader filters that this filter is a subset of\n* $2 - Count of filters in $1, for PLURAL",
        "rcfilters-state-message-fullcoverage": "Tooltip shown when hovering over a filter tag when all the filters in its group are selected. This indicates that the hovered filter has no effect because the selected filters in the group cover all changes. Parameters:\n* $1 - Comma-separated string of selected filters in the group\n* $2 - Count of filters in $1, for PLURAL",
-       "rcfilters-filtergroup-registration": "Title for the filter group for editor registration type.",
-       "rcfilters-filter-registered-label": "Label for the filter for showing edits made by logged-in users.\n{{Identical|Registered}}",
-       "rcfilters-filter-registered-description": "Description for the filter for showing edits made by logged-in users.",
-       "rcfilters-filter-unregistered-label": "Label for the filter for showing edits made by logged-out users.\n{{Identical|Unregistered}}",
-       "rcfilters-filter-unregistered-description": "Description for the filter for showing edits made by logged-out users.",
-       "rcfilters-filter-unregistered-conflicts-user-experience-level": "Tooltip shown when hovering over a Unregistered filter tag, when a User Experience Level filter is also selected.\n\n\"Unregistered\" is {{msg-mw|Rcfilters-filter-unregistered-label}}.\n\n\"Experience\" is based on {{msg-mw|Rcfilters-filtergroup-userExpLevel}}.\n\nThis indicates that no results will be shown, because users matched by the User Experience Level groups are never unregistered.  Parameters:\n* $1 - Comma-separated string of selected User Experience Level filters, e.g. \"Newcomer, Experienced\"\n* $2 - Count of selected User Experience Level filters, for PLURAL",
        "rcfilters-filtergroup-authorship": "Title for the filter group for edit authorship. This filter group allows the user to choose between \"Your own edits\" and \"Edits by others\". More info: https://phabricator.wikimedia.org/T149859",
        "rcfilters-filter-editsbyself-label": "Label for the filter for showing edits made by the current user.",
        "rcfilters-filter-editsbyself-description": "Description for the filter for showing edits made by the current user.",
        "rcfilters-filter-editsbyother-label": "Label for the filter for showing edits made by anyone other than the current user.",
        "rcfilters-filter-editsbyother-description": "Description for the filter for showing edits made by anyone other than the current user.",
        "rcfilters-filtergroup-userExpLevel": "Title for the filter group for user experience levels.",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "Tooltip shown when hovering over a User Experience Level filter tag, when only Unregistered users are being shown.  This indicates that no results will be shown, because users matched by the User Experience Level groups are never unregistered.\n\n\"Unregistered\" is {{msg-mw|Rcfilters-filter-unregistered-label}}.",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "Message shown in the result area when both a User Experience Level filter and the Unregistered filter are selected.  This indicates that no results will be shown because users selected by the User Experience Filter are never unregistered.\n\n\"Unregistered\" is {{msg-mw|Rcfilters-filter-unregistered-label}}.\n\n\"Experience\" is based on {{msg-mw|Rcfilters-filtergroup-userExpLevel}}.",
+       "rcfilters-filter-user-experience-level-registered-label": "Label for the filter for showing edits made by logged-in editors.",
+       "rcfilters-filter-user-experience-level-registered-description": "Description for the filter for showing edits made by logged-in editors.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Label for the filter for showing edits made by anonymous editors.",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Description for the filter for showing edits made by anonymous editors.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Label for the filter for showing edits made by new editors.",
        "rcfilters-filter-user-experience-level-newcomer-description": "Description for the filter for showing edits made by new editors.",
        "rcfilters-filter-user-experience-level-learner-label": "Label for the filter for showing edits made by learning editors.",
        "rcfilters-tag-prefix-namespace": "Prefix for the namespace tags in [[Special:RecentChanges]]. Namespace tags use a colon (:) as prefix. Please keep this format.\n\nParameters:\n* $1 - Filter name.",
        "rcfilters-tag-prefix-namespace-inverted": "Prefix for the namespace inverted tags in [[Special:RecentChanges]]. Namespace tags use a colon (:) as prefix. Please keep this format.\n\nParameters:\n* $1 - Filter name.\n{{Identical|Not}}",
        "rcfilters-tag-prefix-tags": "Prefix for the edit tags in [[Special:RecentChanges]]. Edit tags use a hash (#) as prefix. Please keep this format.\n\nParameters:\n* $1 - Tag display name.",
+       "rcfilters-exclude-button-off": "Title for the button that excludes selected namespaces, when it is not yet active.",
+       "rcfilters-exclude-button-on": "Title for the button that excludes selected namespaces, when it is not yet active.",
        "rcfilters-view-tags": "Title for the tags view in [[Special:RecentChanges]]\n{{Identical|Tag}}",
        "rcfilters-view-namespaces-tooltip": "Tooltip for the button that loads the namespace view in [[Special:RecentChanges]]",
        "rcfilters-view-tags-tooltip": "Tooltip for the button that loads the tags view in [[Special:RecentChanges]]",
index 92c4bb1..fe2444a 100644 (file)
        "lockedbyandtime": "(de $1 'u $2 a le $3)",
        "move-page": "Spuèste $1",
        "move-page-legend": "Spuèste 'a pàgene",
-       "movepagetext": "Ausanne 'u form aqquà sotte ste cange 'u nome d'a pàgene, spustanne tutte 'a storia soje sus a 'u nome nuéve.\nU' vecchie titole devènde 'nu ridirezionamende sus 'a pàgena nove.\nTu puè aggiornà 'u ridirezionamende ca apponde a 'u titole origgenale automaticamende.\nCe tu no ste scacchie, sta secure de condrollà [[Special:DoubleRedirects|doppie ridirezionaminde]] o [[Special:BrokenRedirects|ridirezionaminde scuasciate]].\nTu si 'u responsabbile de quidde ca cumbine, assicurate ca 'u collegamende condinue a appondà addò avessa scè.\n\nVide Bbuene ca 'a pàgene '''non''' g'avène spustate ce esiste n'otra pàgene cu 'u titole nuéve, a mene ca jè vacande o jè 'na pàgene de ridirezionamende senza storie.\nQuieste significhe ca tu puè fà turnà 'u vecchie nome 'a pàgene ce jedde ha state renomenate e t'è rese conde ca è fatte 'na studecarije sovrascrevènne 'na pàgene esistende.\n\n'''ATTENZIONE!'''\nQuiste pò essere 'nu cangiamende drastiche e inaspettate de 'na pàgene famose assaje;\npe piacere a essere secure-secure de le conseguenze apprime de condinuà.",
-       "movepagetext-noredirectfixer": "Ausanne 'u module aqquà sotte puè renomenà 'na pàgene, spustanne tutte 'a storia soje sotte a 'u nome nuève.\n'U titele vecchie addevende 'na pàgene de ridirezionamende a 'u titele nuève.\nMe raccomande condrolle le redirezionaminde [[Special:DoubleRedirects|a doppie]] o [[Special:BrokenRedirects|scuasciate]].\nTu si responsabbele de assicurarte ca le collagaminde appondene a 'u punde giuste.\n\nVide ca 'a pàgene '''non''' g'avene spustate ce già stè 'na pàgene cu 'u titele nuève, a meno che non g'è vacande o jè 'nu ridirezionamende e non ge tène 'na storie de cangiaminde.\nQuiste signifeche ca jè possibbele renominà 'na pàgene accume se chiamave apprime addò tu è fatte 'n'errore e non g'è possibbele sovrascirevere 'na pàgene esistende.\n\n'''Fà Attenziò!'''\nQuiste pò essere 'nu cangiamende inaspettate pe 'na pàgene popolare;\nPe piacere ha essere secure secure de avere capite le conzeguenze apprime de scè nnande.",
+       "movepagetext": "Ausanne 'u form aqquà sotte ste cange 'u nome d'a pàgene, spustanne tutte 'a storia soje sus a 'u nome nuéve.\nU' vecchie titole devènde 'nu ridirezionamende sus 'a pàgena nove.\nTu puè aggiornà 'u ridirezionamende ca apponde a 'u titole origgenale automaticamende.\nCe tu no ste scacchie, sta secure de condrollà [[Special:DoubleRedirects|doppie ridirezionaminde]] o [[Special:BrokenRedirects|ridirezionaminde scuasciate]].\nTu si 'u responsabbile de quidde ca cumbine, assicurate ca 'u collegamende condinue a appondà addò avessa scè.\n\nVide Bbuene ca 'a pàgene <strong>non</strong> g'avène spustate ce esiste n'otra pàgene cu 'u titole nuéve, a mene ca jè vacande o jè 'na pàgene de ridirezionamende senza storie.\nQuieste significhe ca tu puè fà turnà 'u vecchie nome 'a pàgene ce jedde ha state renomenate e t'è rese conde ca è fatte 'na studecarije sovrascrevènne 'na pàgene esistende.\n\n<strong>ATTENZIONE!</strong>\nQuiste pò essere 'nu cangiamende drastiche e inaspettate de 'na pàgene famose assaje;\npe piacere a essere secure-secure de le conseguenze apprime de condinuà.",
+       "movepagetext-noredirectfixer": "Ausanne 'u module aqquà sotte puè renomenà 'na pàgene, spustanne tutte 'a storia soje sotte a 'u nome nuève.\n'U titele vecchie addevende 'na pàgene de ridirezionamende a 'u titele nuève.\nMe raccomande condrolle le redirezionaminde [[Special:DoubleRedirects|a doppie]] o [[Special:BrokenRedirects|scuasciate]].\nTu si responsabbele de assicurarte ca le collagaminde appondene a 'u punde giuste.\n\nVide ca 'a pàgene <strong>non</strong> g'avene spustate ce già stè 'na pàgene cu 'u titele nuève, a meno che non g'è vacande o jè 'nu ridirezionamende e non ge tène 'na storie de cangiaminde.\nQuiste signifeche ca jè possibbele renominà 'na pàgene accume se chiamave apprime addò tu è fatte 'n'errore e non g'è possibbele sovrascirevere 'na pàgene esistende.\n\n<strong>Fà Attenziò!</strong>\nQuiste pò essere 'nu cangiamende inaspettate pe 'na pàgene popolare;\nPe piacere ha essere secure secure de avere capite le conzeguenze apprime de scè nnande.",
        "movepagetalktext": "Ce tu cazze sta buatte, 'A pàgene de le 'ngazzaminde associate avène spustate automaticamende, sembre ca non g'esisite 'n'otra pàgene de le 'ngazzaminde.\n\nJndr'à stu case, 'a pàgene non g'avène spustate e pò t'a cupià a màne 'u condenute sue.",
        "moveuserpage-warning": "'''Attenziò:''' Tu stè spuèste 'na pàgene utende. Vide bbuène ca sulamende 'a pàgene avène spustate ma l'utende ''non'' g'avene renomenate.",
        "movecategorypage-warning": "<strong>Attenziò:</strong> Tu vuè ccu spuéste 'na pàgene categorije. Vide ca sulamende 'a pàgene avène spustate ma tutte le pàggene ca stonne jndr'à categorije <em>non</em> g'avène spustate sotte a quedda nove.",
index 8eadb75..cd5452d 100644 (file)
        "page_last": "последња",
        "histlegend": "Избор разлика: изаберите кутијице измена за упоређивање и притисните ентер или дугме на дну.<br />\nОбјашњење: <strong>({{int:cur}})</strong> = разлика с тренутном изменом, <strong>({{int:last}})</strong> = разлика с претходном изменом, <strong>{{int:minoreditletter}}</strong> = мала измена",
        "history-fieldset-title": "Преглед измена",
-       "history-show-deleted": "Само Ð¾Ð±Ñ\80иÑ\81ано",
+       "history-show-deleted": "Само Ð¾Ð±Ñ\80иÑ\81ане Ð¸Ð·Ð¼Ñ\98ене",
        "histfirst": "најстарије",
        "histlast": "најновије",
        "historysize": "({{PLURAL:$1|1 бајт|$1 бајта|$1 бајтова}})",
        "prefs-help-prefershttps": "Ова подешавања ће ступити на снагу при следећој пријави.",
        "prefswarning-warning": "Променили сте ваша подешавања али нисте их још сачували.\nАко не притиснете „$1“ ваша подешавања ће бити изгубљена.",
        "prefs-tabs-navigation-hint": "Савет: можете користити типке са левом и десном стрелицом за кретање кроз картице.",
-       "userrights": "УпÑ\80авÑ\99аÑ\9aе ÐºÐ¾Ñ\80иÑ\81ниÑ\87ким Ð¿Ñ\80авима",
+       "userrights": "Ð\9aоÑ\80иÑ\81ниÑ\87ка Ð¿Ñ\80ава",
        "userrights-lookup-user": "Изабери корисника",
        "userrights-user-editname": "Корисничко име:",
        "editusergroup": "Учитај корисничке групе",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} ([[Special:NewPages|списак нових страница]])",
        "recentchanges-submit": "Прикажи",
        "rcfilters-activefilters": "Активни филтери",
-       "rcfilters-quickfilters": "Ð\91Ñ\80зе Ð²ÐµÐ·Ðµ",
+       "rcfilters-quickfilters": "СаÑ\87Ñ\83вани Ñ\84илÑ\82еÑ\80и",
        "rcfilters-savedqueries-defaultlabel": "Сачувани филтери",
        "rcfilters-savedqueries-rename": "Преименуј",
        "rcfilters-savedqueries-setdefault": "Постави као подразумевано",
        "rcfilters-savedqueries-unsetdefault": "Уклони као подразумевано",
        "rcfilters-savedqueries-remove": "Уклони",
        "rcfilters-savedqueries-new-name-label": "Име",
-       "rcfilters-savedqueries-apply-label": "Направи брзу везу",
+       "rcfilters-savedqueries-apply-label": "Направи филтер",
        "rcfilters-savedqueries-cancel-label": "Откажи",
-       "rcfilters-savedqueries-add-new-title": "СаÑ\87Ñ\83ваÑ\98 Ñ\84илÑ\82еÑ\80е ÐºÐ°Ð¾ Ð±Ñ\80зÑ\83 Ð²ÐµÐ·Ñ\83",
+       "rcfilters-savedqueries-add-new-title": "СаÑ\87Ñ\83ваÑ\98 Ñ\82Ñ\80енÑ\83Ñ\82не Ð¿Ð¾Ñ\81Ñ\82авке Ñ\84илÑ\82еÑ\80а",
        "rcfilters-restore-default-filters": "Враћање подразумеваних филтера",
        "rcfilters-clear-all-filters": "Уклони све филтере",
        "rcfilters-empty-filter": "Нема активних филтера. Сви доприноси су приказани.",
        "lonelypagestext": "Следеће странице нису повезане с другим страницама, нити су укључене трансклузијом у друге странице.",
        "uncategorizedpages": "Некатегорисане странице",
        "uncategorizedcategories": "Некатегорисане категорије",
-       "uncategorizedimages": "Ð\94аÑ\82оÑ\82еке Ð±ÐµÐ· ÐºÐ°Ñ\82егоÑ\80иÑ\98а",
+       "uncategorizedimages": "Ð\9dекаÑ\82егоÑ\80иÑ\81ане Ð´Ð°Ñ\82оÑ\82еке",
        "uncategorizedtemplates": "Некатегорисани шаблони",
        "unusedcategories": "Некоришћене категорије",
        "unusedimages": "Некоришћене датотеке",
index 8de9d64..0ec336c 100644 (file)
        "lonelypagestext": "Sledeće stranice nisu povezane s drugim stranicama, niti su uključene transkluzijom u druge stranice.",
        "uncategorizedpages": "Nekategorisane stranice",
        "uncategorizedcategories": "Nekategorisane kategorije",
-       "uncategorizedimages": "Datoteke bez kategorija",
+       "uncategorizedimages": "Nekategorisane datoteke",
        "uncategorizedtemplates": "Nekategorisani šabloni",
        "unusedcategories": "Nekorišćene kategorije",
        "unusedimages": "Nekorišćene datoteke",
index 8135189..11242dd 100644 (file)
@@ -78,8 +78,8 @@
        "sat": "Шн",
        "january": "Январ",
        "february": "Феврал",
-       "march": "Ð\9cарт",
-       "april": "Ð\90прел",
+       "march": "март",
+       "april": "апрел",
        "may_long": "май",
        "june": "Июн",
        "july": "Июл",
        "retypenew": "Калимаи нави убурро такроран нависед:",
        "resetpass_submit": "Калимаи убурро танзим карда ба систем вуруд кунед",
        "changepassword-success": "Гузарвожаи шумо бо муваффақият тағйир дода шуд!",
+       "botpasswords-label-cancel": "Пӯшидан",
        "resetpass_forbidden": "Гузарвожаҳоро наметавон тағйир дод",
        "resetpass-no-info": "Барои дастрасии мустақим ба ин саҳифа шумо бояд ба систем ворид шуда бошед.",
        "resetpass-submit-loggedin": "Тағйири гузарвожа",
        "session_fail_preview_html": "'''Бубахшед! Имкони сабти вироиши шумо ба хотир аз даст рафтани иттилоот нишаст.'''\n\n''Бо таваҷҷӯҳ ба ин ки дар {{SITENAME}} имкони HTML хом фаъол аст, пешнамоиши саҳифа пинҳон шуда то имкони зидди ҳамалоти ҶаваСкрипт вуҷуд ндошта бошад.''\n\n'''Агар боварӣ доред, ки ин пешнамоиш як вироиши миҷоз ас, онро такрор кунед. Агар пешнамоиш натиҷае надод, аз систем хориҷ шавед ва дубора ворид шавед'''",
        "token_suffix_mismatch": "'''Вироишҳои шумо захира нашуд, зеро мурургари шумо навиштаҳои нуқтагузориро аз ҳам пошида аст. Вироиши шумо захира нашуд то аз вайрон шудани матни саҳифа пешгирӣ кунад.\nБаъзан ин хато вақте пайдо мешавад, ки шумо аз хизмати (web-based proxy) истифода мекунед.'''",
        "edit_form_incomplete": "<strong>Бархе аз қисмати форми вироиш ба пойгоҳ нарасид; дубора бисанҷед, ки вироишоти шумо дуруст аст ва дубора саъй кунед.</strong>",
-       "editing": "Дар ҳоли вироиш $1",
+       "editing": "Дар ҳоли вироиши $1",
        "creating": "Дар ҳоли эҷоди $1",
        "editingsection": "Дар ҳоли вироиши $1 (қисмат)",
        "editingcomment": "Дар ҳоли вироиши $1 (қисми нав)",
        "revdelete-hide-user": "Номи корбар/нишонаи IP",
        "revdelete-hide-restricted": "Фурунишонии иттилоот барои мудирон ба ҳамроҳи дигарон",
        "revdelete-radio-same": "(тағйир надиҳед)",
-       "revdelete-radio-set": "Пинҳон",
+       "revdelete-radio-set": "Пинҳонкарда",
        "revdelete-radio-unset": "Намоён",
        "revdelete-suppress": "Аз дастрасии мудирон ба додаҳо низ монанди сайри корбарон ҷилавгирӣ ба амал ояд",
        "revdelete-unsuppress": "Хотимаи маҳдудиятҳо дар мавриди нусхаҳои интихобшуда",
        "notextmatches": "Матни ҳеҷ мақолае рост намеояд",
        "prevn": "{{PLURAL:$1|$1-тои}} қаблӣ",
        "nextn": "{{PLURAL:$1|$1-тои}} навбатӣ",
+       "next-page": "саҳифаи навбатӣ",
        "prevn-title": "Қаблӣ $1 {{PLURAL:$1|натиҷа|натоиҷ}}",
        "nextn-title": "Баъдӣ $1 {{PLURAL:$1|натиҷа|натоиҷ}}",
        "shown-title": "Намоиши $1 {{PLURAL:$1|натиҷа|натоиҷ}} дар ҳар саҳифа",
        "recentchanges-label-minor": "Ин вироиши ҷузъи аст",
        "recentchanges-label-bot": "Ин вироишро робот анҷом додааст",
        "recentchanges-label-unpatrolled": "Ин вироиш ҳанӯз гаштзанӣ нашудааст",
+       "rcfilters-savedqueries-cancel-label": "Лағв",
        "rcnotefrom": "Дар зер тағйиротҳои охирин аз <b>$2</b> (то <b>$1</b> нишон дода шудааст).",
        "rclistfrom": "Нишон додани тағйиротҳои нав сар карда аз $3 $2",
        "rcshowhideminor": "$1 вироишҳои хурд",
-       "rcshowhideminor-show": "Намоиш",
-       "rcshowhideminor-hide": "Пинҳон",
+       "rcshowhideminor-show": "Намоиш додани",
+       "rcshowhideminor-hide": "Пинҳон кардани",
        "rcshowhidebots": "$1 ботҳо",
        "rcshowhidebots-show": "Намоиш",
-       "rcshowhidebots-hide": "Пинҳон",
+       "rcshowhidebots-hide": "Пинҳон кардани",
        "rcshowhideliu": "$1 корбарони сабтиномшуда",
        "rcshowhideliu-show": "Намоиш",
-       "rcshowhideliu-hide": "Пинҳон",
+       "rcshowhideliu-hide": "Пинҳон кардани",
        "rcshowhideanons": "$1 корбарони вуруднашуда",
        "rcshowhideanons-show": "Намоиш",
-       "rcshowhideanons-hide": "Пинҳон",
+       "rcshowhideanons-hide": "Пинҳон кардани",
        "rcshowhidepatr": "$1 вироишҳои гаштӣ",
        "rcshowhidepatr-show": "Намоиш",
-       "rcshowhidepatr-hide": "Пинҳон",
+       "rcshowhidepatr-hide": "Пинҳон кардани",
        "rcshowhidemine": "$1 вироишҳои ман",
        "rcshowhidemine-show": "Намоиш",
-       "rcshowhidemine-hide": "Пинҳон",
+       "rcshowhidemine-hide": "Пинҳон кардани",
        "rclinks": "Нишон додани $1 тағйироти охирин дар $2 рӯзи охир",
        "diff": "фарқият",
        "hist": "таърих",
        "statistics-header-hooks": "Дигар омор",
        "statistics-articles": "Саҳифаҳои мӯҳтаво",
        "statistics-pages": "Саҳифаҳо",
+       "statistics-pages-desc": "Тамоми саҳифаҳо дар ин вики-сомона (саҳифаҳои равонакунӣ, баҳсҳо ва ғ.)",
        "statistics-files": "Парвандаҳои боршуда",
+       "statistics-edits": "Шумораи вироишҳо аз замони эҷоди ин {{SITENAME}}",
        "statistics-users": "[[Special:ListUsers|Корбарони]] сабтиномшуда",
        "statistics-users-active": "Корбарони фаъол",
        "pageswithprop-submit": "Бирав",
        "withoutinterwiki": "Саҳифаҳои бидуни пайвандҳои забонӣ",
        "withoutinterwiki-summary": "Саҳифаҳои зерин пайванде ба забони дигар надоранд:",
        "withoutinterwiki-legend": "Пешванд",
-       "withoutinterwiki-submit": "Намоиш",
+       "withoutinterwiki-submit": "Намоиши",
        "fewestrevisions": "Саҳифаҳое, ки шумораи ками нусхаҳо доранд",
        "nbytes": "$1 {{PLURAL:$1|байт|байт}}",
        "ncategories": "$1 {{PLURAL:$1|гурӯҳ|гурӯҳҳо}}",
        "logempty": "Мавриди мутобиқ ба манзури шумо дар гузориш пайдо нашуд.",
        "log-title-wildcard": "Саҳифаҳоеро ҷустуҷӯ кунед, ки унвонашон бо ин матн оғоз мешаванд",
        "allpages": "Ҳамаи саҳифаҳо",
-       "nextpage": "СаҳиÑ\84аи Ð±Ð°Ñ\8aдина ($1)",
+       "nextpage": "СаҳиÑ\84аи Ð½Ð°Ð²Ð±Ð°Ñ\82Ó£ ($1)",
        "prevpage": "Саҳифаи пешина ($1)",
        "allpagesfrom": "Намоиши саҳифаҳо бо шурӯъ аз:",
        "allpagesto": "Намоиши саҳифаҳо бо поёни дар:",
        "mailnologin": "Нишонае аз фиристанда вуҷуд надорад",
        "mailnologintext": "Барои фиристодани почтаи электронӣ барои корбарони дигар бояд [[Special:UserLogin|ба систем ворид шавед]] ва нишонаи почтаи электронии мӯътабар дар [[Special:Preferences|тарҷиҳоти]] худ дошта бошед.",
        "emailuser": "Фиристодани email ба ин корбар",
+       "emailuser-title-target": "Навиштани мактуб ба email-и ин корбар",
        "emailuser-title-notarget": "Фиристодани пайём ба корбар",
        "emailpagetext": "Агар ин корбар нишонаи почтаи электронии мӯътабаре дар тарҷиҳоти ворид карда бошад, форми зерин як пайғоме мефиристад.\nНишонаи почтаи электроние, ки шумо дар тарҷиҳоти корбариатон ворид кардаед, дар нишони фиристандаи нома \"From\" хоҳад омад, то ки гиранда тавонад ба шумо посух диҳад.",
        "defemailsubject": "Пайёми {{SITENAME}} аз корбар \"$1\"",
        "unblocked": "Дастрасии [[User:$1|$1]] боз карда шуд",
        "unblocked-id": "Қатъи дастрасии шумораи $1 хотима ёфт",
        "blocklist": "Корбарони басташуда",
+       "autoblocklist-submit": "Ҷустуҷӯ",
        "ipblocklist": "Корбарони басташуда",
        "ipblocklist-legend": "Ҷустуҷӯи корбари баста шуда",
        "blocklist-target": "Ҳадаф",
        "tooltip-pt-preferences": "Тарҷиҳоти ман",
        "tooltip-pt-watchlist": "Рӯйхати саҳифаҳое, ки тағйиротҳояшонро Шумо назорат мекунед",
        "tooltip-pt-mycontris": "Феҳристи ҳиссагузориҳои шумо",
-       "tooltip-pt-login": "Тавсия мешавад ки ба систем ворид шавад, лекин иҷборӣ нест.",
+       "tooltip-pt-login": "Тавсия мешавад ки ба система ворид шавед, лекин маҷбурӣ нест.",
        "tooltip-pt-logout": "Хуруҷ аз систем",
        "tooltip-ca-talk": "Баҳси матни таркибии ин саҳифа",
        "tooltip-ca-edit": "Шумо ин саҳифаро вироиш карда метавонед. Пеш аз захира кардани саҳифа пешнамоишро истифода баред.",
        "exif-gpsdatestamp": "Таърихи ҶПС",
        "exif-gpsdifferential": "Тасҳеҳи ҷузъии ҶПС",
        "exif-keywords": "Калидвожаҳо",
+       "exif-languagecode": "Забон",
        "exif-compression-1": "Ғайрифишурда",
        "exif-unknowndate": "Таърихи номаълум",
        "exif-orientation-1": "Оддӣ",
        "recreate": "Аз нав созед",
        "confirm_purge_button": "Таъйид",
        "confirm-purge-top": "Пок карадни нусхаи ҳофизаи ниҳонии (Cache) ин саҳифаро таъйид мекунед?",
-       "imgmultipageprev": "← саҳифаи пешин",
-       "imgmultipagenext": "саҳифаи баъд →",
+       "imgmultipageprev": "← саҳифаи пешина",
+       "imgmultipagenext": "саҳифаи баъдӣ →",
        "imgmultigo": "Бирав!",
        "imgmultigoto": "Ба саҳифаи $1 равед",
        "ascending_abbrev": "афзуншаванда",
        "expand_templates_remove_comments": "Ҳазфи тавзеҳот",
        "expand_templates_generate_xml": "Намоиши дарахти таҷзеҳи XML",
        "expand_templates_preview": "Пешнамоиш",
+       "special-characters-group-cyrillic": "Сириллик",
        "randomrootpage": "Саҳифаи решавии тасодуфӣ",
        "changecredentials-submit": "Тағйири ҳисоби корбарӣ",
        "removecredentials-submit": "Хориҷи эътиборнома"
index e771d78..35c0055 100644 (file)
        "recentchanges-legend-heading": "<strong>Аңлатма:</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (шулай ук [[Special:NewPages|яңа битләр исемлеген]] карагыз)",
        "recentchanges-submit": "Күрсәт",
-       "rcfilters-quickfilters": "Сакланган филтр көйләнмәләре",
+       "rcfilters-activefilters": "Актив фильтрлар",
+       "rcfilters-quickfilters": "Сакланган фильтрлар",
        "rcfilters-clear-all-filters": "Барлык филтерләрне чистарту",
+       "rcfilters-search-placeholder": "Фильтрланы соңгы үзгәртү (карау яисә кертүне башлау)",
        "rcfilters-filter-registered-label": "Теркәлгән",
        "rcfilters-filter-registered-description": "Теркәлгән мөхәррирләр.",
        "rcfilters-filter-user-experience-level-experienced-label": "Тәҗрибәле кулланучылар",
index cf6503a..dc61266 100644 (file)
        "confirmrecreate": "在您編輯的同時,使用者 [[User:$1|$1]] ([[User talk:$1|對話]]) 刪除了此頁面,原因為:\n: <em>$2</em>\n請確認您是否真的要重新建立此頁面。",
        "confirmrecreate-noreason": "在您編輯的同時,使用者 [[User:$1|$1]] ([[User talk:$1|對話]]) 刪除了此頁面,請確認您是否真的要重新建立此頁面。",
        "recreate": "重新建立",
-       "confirm-purge-title": "刷新本頁",
+       "confirm-purge-title": "清除此頁快取",
        "confirm_purge_button": "確定",
        "confirm-purge-top": "要清除此頁面的快取嗎?",
        "confirm-purge-bottom": "刷新頁面會清空頁面的快取記錄並強制顯示最近的頁面修訂。",
index 69f4f89..ba8662a 100644 (file)
@@ -35,6 +35,7 @@ class DeleteDefaultMessages extends Maintenance {
                parent::__construct();
                $this->addDescription( 'Deletes all pages in the MediaWiki namespace' .
                        ' which were last edited by "MediaWiki default"' );
+               $this->addOption( 'dry-run', 'Perform a dry run, delete nothing' );
        }
 
        public function execute() {
@@ -52,14 +53,23 @@ class DeleteDefaultMessages extends Maintenance {
                );
 
                if ( $dbr->numRows( $res ) == 0 ) {
-                       # No more messages left
+                       // No more messages left
                        $this->output( "done.\n" );
+                       return;
+               }
 
+               $dryrun = $this->hasOption( 'dry-run' );
+               if ( $dryrun ) {
+                       foreach ( $res as $row ) {
+                               $title = Title::makeTitle( $row->page_namespace, $row->page_title );
+                               $this->output( "\n* [[$title]]" );
+                       }
+                       $this->output( "\n\nRun again without --dry-run to delete these pages.\n" );
                        return;
                }
 
-               # Deletions will be made by $user temporarly added to the bot group
-               # in order to hide it in RecentChanges.
+               // Deletions will be made by $user temporarly added to the bot group
+               // in order to hide it in RecentChanges.
                $user = User::newFromName( 'MediaWiki default' );
                if ( !$user ) {
                        $this->error( "Invalid username", true );
@@ -67,7 +77,7 @@ class DeleteDefaultMessages extends Maintenance {
                $user->addGroup( 'bot' );
                $wgUser = $user;
 
-               # Handle deletion
+               // Handle deletion
                $this->output( "\n...deleting old default messages (this may take a long time!)...", 'msg' );
                $dbw = $this->getDB( DB_MASTER );
 
index 8b2d6e5..c745ce4 100644 (file)
@@ -4,7 +4,7 @@
 
                function syncText() {
                        var value = $( this ).val()
-                               .replace( /[\[\]\{\}|#<>%+? ]/g, '_' )
+                               .replace( /[\[\]{}|#<>%+? ]/g, '_' ) // eslint-disable-line no-useless-escape
                                .replace( /&/, '&amp;' )
                                .replace( /__+/g, '_' )
                                .replace( /^_+/, '' )
                }
 
                // Set up the help system
-               $( '.config-help-field-data' )
-                       .hide()
-                       .closest( '.config-help-field-container' )
-                               .find( '.config-help-field-hint' )
-                                       .show()
-                                       .click( function () {
-                                               $( this )
-                                                       .closest( '.config-help-field-container' )
-                                                               .find( '.config-help-field-data' )
-                                                                       .slideToggle( 'fast' );
-                                       } );
+               $( '.config-help-field-data' ).hide()
+                       .closest( '.config-help-field-container' ).find( '.config-help-field-hint' )
+                       .show()
+                       .click( function () {
+                               $( this ).closest( '.config-help-field-container' ).find( '.config-help-field-data' )
+                                       .slideToggle( 'fast' );
+                       } );
 
                // Show/hide code for DB-specific options
                // FIXME: Do we want slow, fast, or even non-animated (instantaneous) showing/hiding here?
index fe3c910..8507238 100644 (file)
     "grunt-banana-checker": "0.6.0",
     "grunt-contrib-copy": "1.0.0",
     "grunt-contrib-watch": "1.0.0",
-    "grunt-eslint": "19.0.0",
+    "grunt-eslint": "20.0.0",
     "grunt-jsonlint": "1.1.0",
     "grunt-karma": "2.0.0",
-    "grunt-stylelint": "0.7.0",
+    "grunt-stylelint": "0.8.0",
     "grunt-webdriver": "2.0.3",
     "karma": "1.5.0",
     "karma-chrome-launcher": "2.0.0",
@@ -26,6 +26,7 @@
     "karma-qunit": "1.0.0",
     "nodemw": "0.10.1",
     "qunitjs": "1.23.1",
+    "stylelint": "7.8.0",
     "stylelint-config-wikimedia": "0.4.1",
     "wdio-junit-reporter": "0.2.0",
     "wdio-mocha-framework": "0.5.8",
index a8cf91d..afc1c0e 100644 (file)
@@ -1857,6 +1857,8 @@ return [
                        'rcfilters-tag-prefix-namespace',
                        'rcfilters-tag-prefix-namespace-inverted',
                        'rcfilters-tag-prefix-tags',
+                       'rcfilters-exclude-button-off',
+                       'rcfilters-exclude-button-on',
                        'rcfilters-view-tags',
                        'rcfilters-view-namespaces-tooltip',
                        'rcfilters-view-tags-tooltip',
index 7773866..40b3baf 100644 (file)
@@ -64,8 +64,7 @@
                        if ( $badge.length ) {
                                $badge
                                        .toggleClass( 'mw-badge-important', isImportant )
-                                       .find( '.mw-badge-content' )
-                                               .text( text );
+                                       .find( '.mw-badge-content' ).text( text );
                        } else {
                                // Otherwise, create a new badge with the specified text and style
                                $badge = $( '<div class="mw-badge"></div>' )
index 2be1dba..a5b136d 100644 (file)
@@ -42,7 +42,7 @@
 
                        // Look for rgb(num%,num%,num%)
                        // eslint-disable-next-line no-cond-assign
-                       if ( result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec( color ) ) {
+                       if ( result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)%\s*,\s*([0-9]+(?:\.[0-9]+)?)%\s*,\s*([0-9]+(?:\.[0-9]+)?)%\s*\)/.exec( color ) ) {
                                return [
                                        parseFloat( result[ 1 ] ) * 2.55,
                                        parseFloat( result[ 2 ] ) * 2.55,
index 5ce9b1f..aa76d6d 100644 (file)
                        buildDefaultToggleLink = function () {
                                return $( '<a class="mw-collapsible-text"></a>' )
                                        .text( collapseText )
-                                       .wrap( '<span class="mw-collapsible-toggle"></span>' ).parent()
-                                               .attr( {
-                                                       role: 'button',
-                                                       tabindex: 0
-                                               } )
-                                               .prepend( '<span>[</span>' )
-                                               .append( '<span>]</span>' )
-                                               .on( 'click.mw-collapsible keypress.mw-collapsible', actionHandler );
+                                       .wrap( '<span class="mw-collapsible-toggle"></span>' )
+                                       .parent()
+                                       .attr( {
+                                               role: 'button',
+                                               tabindex: 0
+                                       } )
+                                       .prepend( '<span>[</span>' )
+                                       .append( '<span>]</span>' )
+                                       .on( 'click.mw-collapsible keypress.mw-collapsible', actionHandler );
                        };
 
                        // Check if this element has a custom position for the toggle link
index 6d478bd..4bcccdd 100644 (file)
@@ -11,7 +11,7 @@
                },
                trimRight: function ( str ) {
                        return str === null ?
-                                       '' : str.toString().replace( /\s+$/, '' );
+                               '' : str.toString().replace( /\s+$/, '' );
                },
                ucFirst: function ( str ) {
                        return str.charAt( 0 ).toUpperCase() + str.slice( 1 );
        } );
 
        mw.log.deprecate( $, 'escapeRE', function ( str ) {
-               return str.replace( /([\\{}()|.?*+\-\^$\[\]])/g, '\\$1' );
+               return str.replace( /([\\{}()|.?*+\-^$\[\]])/g, '\\$1' ); // eslint-disable-line no-useless-escape
        }, 'Use mediawiki.RegExp instead.' );
 
 }( jQuery, mediaWiki ) );
index 75f1ba6..4f4edc9 100644 (file)
@@ -19,7 +19,6 @@
  * @class jQuery.plugin.suggestions
  */
 
- // jscs:disable checkParamNames
 /**
  * @method suggestions
  * @chainable
@@ -94,7 +93,6 @@
  * @param {boolean} [options.highlightInput=false] Whether to highlight matched portions of the
  *  input or not.
  */
- // jscs:enable checkParamNames
 
 ( function ( $, mw ) {
 
                                                        27, // escape
                                                        13, // enter
                                                        46, // delete
-                                                       8   // backspace
+                                                       8 //   backspace
                                                ];
                                                if ( context.data.keypressedCount === 0 &&
                                                        e.which === context.data.keypressed &&
index 8d019e5..ec91773 100644 (file)
 
                // Build RegEx
                // Any date formated with . , ' - or /
-               ts.dateRegex[ 0 ] = new RegExp( /^\s*(\d{1,2})[\,\.\-\/'\s]{1,2}(\d{1,2})[\,\.\-\/'\s]{1,2}(\d{2,4})\s*?/i );
+               ts.dateRegex[ 0 ] = new RegExp( /^\s*(\d{1,2})[,.\-/'\s]{1,2}(\d{1,2})[,.\-/'\s]{1,2}(\d{2,4})\s*?/i );
 
                // Written Month name, dmy
                ts.dateRegex[ 1 ] = new RegExp(
                        }
 
                        columnToCell = [];
-                       cellsInRow = ( $row[ 0 ].cells.length ) || 0;  // all cells in this row
+                       cellsInRow = ( $row[ 0 ].cells.length ) || 0; // all cells in this row
                        index = 0; // real cell index in this row
                        for ( j = 0; j < columns; index++ ) {
                                if ( index === cellsInRow ) {
                }
                ts.rgx = {
                        IPAddress: [
-                               new RegExp( /^\d{1,3}[\.]\d{1,3}[\.]\d{1,3}[\.]\d{1,3}$/ )
+                               new RegExp( /^\d{1,3}[.]\d{1,3}[.]\d{1,3}[.]\d{1,3}$/ )
                        ],
                        currency: [
                                new RegExp( /(^[£$€¥]|[£$€¥]$)/ ),
                                new RegExp( /(https?|ftp|file):\/\// )
                        ],
                        isoDate: [
-                               new RegExp( /^([-+]?\d{1,4})-([01]\d)-([0-3]\d)([T\s]((([01]\d|2[0-3])(:?[0-5]\d)?|24:?00)?(:?([0-5]\d|60))?([.,]\d+)?)([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?/ ),
+                               new RegExp( /^([-+]?\d{1,4})-([01]\d)-([0-3]\d)([T\s]((([01]\d|2[0-3])(:?[0-5]\d)?|24:?00)?(:?([0-5]\d|60))?([.,]\d+)?)([zZ]|([+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?/ ),
                                new RegExp( /^([-+]?\d{1,4})-([01]\d)-([0-3]\d)/ )
                        ],
                        usLongDate: [
                        return getParserById( id );
                },
 
-               getParsers: function () {  // for table diagnosis
+               getParsers: function () { // for table diagnosis
                        return parsers;
                }
        };
index 53c1fbb..2b6fc9d 100644 (file)
                                        role: 'navigation',
                                        'aria-labelledby': 'p-lang-label'
                                } )
-                               .append( $( '<h3>' ).attr( 'id', 'p-lang-label' ).text( mw.msg( 'otherlanguages' ) ) )
-                               .append( $( '<div>' ).addClass( 'body' ).append( '<ul>' ) )
+                                       .append( $( '<h3>' ).attr( 'id', 'p-lang-label' ).text( mw.msg( 'otherlanguages' ) ) )
+                                       .append( $( '<div>' ).addClass( 'body' ).append( '<ul>' ) )
                        );
                }
 
index b3b0af2..6f49fa6 100644 (file)
@@ -23,8 +23,7 @@ jQuery( function ( $ ) {
                        return true;
                }
 
-               $lis
-               .each( function () {
+               $lis.each( function () {
                        $li = $( this );
                        $inputs = $li.find( 'input[type="radio"]' );
                        $oldidRadio = $inputs.filter( '[name="oldid"]' ).eq( 0 );
@@ -97,8 +96,7 @@ jQuery( function ( $ ) {
                        // Also remove potentially conflicting id attributes that we don't need anyway
                        $copyForm
                                .css( 'display', 'none' )
-                               .find( '[id]' )
-                                       .removeAttr( 'id' )
+                               .find( '[id]' ).removeAttr( 'id' )
                                .end()
                                .insertAfter( $historyCompareForm )
                                .submit();
index a3a82d5..0d000c9 100644 (file)
                $col = $( '<td colspan="2"></td>' );
 
                $link = $( '<a>' )
-               .text( showText )
-               .attr( {
-                       role: 'button',
-                       tabindex: 0
-               } )
-               .on( 'click keypress', function ( e ) {
-                       if (
-                               e.type === 'click' ||
-                               e.type === 'keypress' && e.which === 13
-                       ) {
-                               if ( $table.hasClass( 'collapsed' ) ) {
-                                       $( this ).text( hideText );
-                               } else {
-                                       $( this ).text( showText );
+                       .text( showText )
+                       .attr( {
+                               role: 'button',
+                               tabindex: 0
+                       } )
+                       .on( 'click keypress', function ( e ) {
+                               if (
+                                       e.type === 'click' ||
+                                       e.type === 'keypress' && e.which === 13
+                               ) {
+                                       if ( $table.hasClass( 'collapsed' ) ) {
+                                               $( this ).text( hideText );
+                                       } else {
+                                               $( this ).text( showText );
+                                       }
+                                       $table.toggleClass( 'expanded collapsed' );
                                }
-                               $table.toggleClass( 'expanded collapsed' );
-                       }
-               } );
+                       } );
 
                $col.append( $link );
                $row.append( $col );
index 2307f30..4915803 100644 (file)
 
                        // 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 ?
-                                       // We must store all parameter values as strings '0' or '1'
                                        String( Number( !value ) ) :
                                        '0';
                        } );
                        $.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 ?
-                                       // Flip the definition between the parameter
-                                       // state and the filter state
-                                       // This is what the 'toggleSelected' value of the filter is
                                        !Number( paramValue ) :
                                        // Otherwise, there are no selected items in the
                                        // group, which means the state is false
                        );
                        // Translate the parameter values into a filter selection state
                        this.getItems().forEach( function ( filterItem ) {
+                               // All true (either because all values are written or the term 'all' is written)
+                               // is the same as all filters set to true
                                result[ filterItem.getName() ] = (
-                                               // If it is the word 'all'
-                                               paramValues.length === 1 && paramValues[ 0 ] === 'all' ||
-                                               // All values are written
-                                               paramValues.length === model.getItemCount()
-                                       ) ?
-                                       // All true (either because all values are written or the term 'all' is written)
-                                       // is the same as all filters set to true
+                                       // If it is the word 'all'
+                                       paramValues.length === 1 && paramValues[ 0 ] === 'all' ||
+                                       // All values are written
+                                       paramValues.length === model.getItemCount()
+                               ) ?
                                        true :
                                        // Otherwise, the filter is selected only if it appears in the parameter values
                                        paramValues.indexOf( filterItem.getParamName() ) > -1;
index 75431d9..06fa0aa 100644 (file)
                } );
        };
 
+       /**
+        * Get all selected items
+        *
+        * @return {mw.rcfilters.dm.FilterItem[]} Selected items
+        */
+       mw.rcfilters.dm.FiltersViewModel.prototype.getSelectedItems = function () {
+               var allSelected = [];
+
+               $.each( this.getFilterGroups(), function ( groupName, groupModel ) {
+                       allSelected = allSelected.concat( groupModel.getSelectedItems() );
+               } );
+
+               return allSelected;
+       };
        /**
         * Switch the current view
         *
index 0abfb34..5dd830b 100644 (file)
@@ -15,6 +15,8 @@
                this.baseFilterState = {};
                this.uriProcessor = null;
                this.initializing = false;
+
+               this.prevLoggedItems = [];
        };
 
        /* Initialization */
                        this.filtersModel.toggleFilterSelected( filterName, false );
                        this.updateChangesList();
                        this.filtersModel.reassessFilterInteractions( filterItem );
+
+                       // Log filter grouping
+                       this.trackFilterGroupings( 'removefilter' );
                }
 
                if ( isHighlighted ) {
                        this.filtersModel.reassessFilterInteractions();
 
                        this.updateChangesList();
+
+                       // Log filter grouping
+                       this.trackFilterGroupings( 'savedfilters' );
                }
        };
 
                );
        };
 
+       /**
+        * Track filter grouping usage
+        *
+        * @param {string} action Action taken
+        */
+       mw.rcfilters.Controller.prototype.trackFilterGroupings = function ( action ) {
+               var controller = this,
+                       rightNow = new Date().getTime(),
+                       randomIdentifier = String( mw.user.sessionId() ) + String( rightNow ) + String( Math.random() ),
+                       // Get all current filters
+                       filters = this.filtersModel.getSelectedItems().map( function ( item ) {
+                               return item.getName();
+                       } );
+
+               action = action || 'filtermenu';
+
+               // Check if these filters were the ones we just logged previously
+               // (Don't log the same grouping twice, in case the user opens/closes)
+               // the menu without action, or with the same result
+               if (
+                       // Only log if the two arrays are different in size
+                       filters.length !== this.prevLoggedItems.length ||
+                       // Or if any filters are not the same as the cached filters
+                       filters.some( function ( filterName ) {
+                               return controller.prevLoggedItems.indexOf( filterName ) === -1;
+                       } ) ||
+                       // Or if any cached filters are not the same as given filters
+                       this.prevLoggedItems.some( function ( filterName ) {
+                               return filters.indexOf( filterName ) === -1;
+                       } )
+               ) {
+                       filters.forEach( function ( filterName ) {
+                               mw.track(
+                                       'event.ChangesListFilterGrouping',
+                                       {
+                                               action: action,
+                                               groupIdentifier: randomIdentifier,
+                                               filter: filterName,
+                                               userId: mw.user.getId()
+                                       }
+                               );
+                       } );
+
+                       // Cache the filter names
+                       this.prevLoggedItems = filters;
+               }
+       };
 }( mediaWiki, jQuery ) );
index fb0b93b..2daa76e 100644 (file)
@@ -1,5 +1,6 @@
 .mw-rcfilters-ui-savedLinksListItemWidget {
-       padding: 0.2em 0.7em;
+       padding: 0 0.5em;
+       line-height: normal;
 
        &:hover {
                // Mimicking optionWidget styles
@@ -48,6 +49,7 @@
 
        &-content {
                width: 100%;
+               line-height: normal;
        }
 
 }
index d0ad8d5..da71d70 100644 (file)
                // Invert namespaces button
                this.invertNamespacesButton = new OO.ui.ToggleButtonWidget( {
                        icon: '',
-                       label: mw.msg( 'invert' ),
                        classes: [ 'mw-rcfilters-ui-filterMenuHeaderWidget-invertNamespacesButton' ]
                } );
                this.invertNamespacesButton.toggle( this.model.getCurrentView() === 'namespaces' );
+               this.updateInvertButton( this.model.areNamespacesInverted() );
 
                // Events
                this.backButton.connect( this, { click: 'onBackButtonClick' } );
         * @param {boolean} isInverted Namespaces selection is inverted
         */
        mw.rcfilters.ui.FilterMenuHeaderWidget.prototype.onModelInvertChange = function ( isInverted ) {
+               this.updateInvertButton( isInverted );
+       };
+
+       /**
+        * Update the state of the invert button
+        *
+        * @param {boolean} isInverted Namespaces selection is inverted
+        */
+       mw.rcfilters.ui.FilterMenuHeaderWidget.prototype.updateInvertButton = function ( isInverted ) {
                this.invertNamespacesButton.setActive( isInverted );
+               this.invertNamespacesButton.setLabel(
+                       isInverted ?
+                               mw.msg( 'rcfilters-exclude-button-on' ) :
+                               mw.msg( 'rcfilters-exclude-button-off' )
+               );
        };
 
        mw.rcfilters.ui.FilterMenuHeaderWidget.prototype.onBackButtonClick = function () {
index 9f41712..88479c3 100644 (file)
@@ -72,7 +72,6 @@
                                classes: [ 'mw-rcfilters-ui-filterMenuSectionOptionWidget-whatsThisButton' ],
                                flags: [ 'progressive' ],
                                popup: {
-                                       $autoCloseIgnore: this.$element.add( this.$overlay ),
                                        padded: false,
                                        align: 'center',
                                        position: 'above',
index dcada85..305456a 100644 (file)
                                // Clear the input
                                this.input.setValue( '' );
                        }
+
+                       // Log filter grouping
+                       this.controller.trackFilterGroupings( 'filtermenu' );
                }
 
                this.input.setIcon( isVisible ? 'search' : 'menu' );
index 64b9ac9..07d4506 100644 (file)
@@ -34,8 +34,7 @@
 
                this.inputValue = '';
                this.$overlay = config.$overlay || this.$element;
-               this.$body = $( '<div>' )
-                               .addClass( 'mw-rcfilters-ui-menuSelectWidget-body' );
+               this.$body = $( '<div>' ).addClass( 'mw-rcfilters-ui-menuSelectWidget-body' );
                this.footers = [];
 
                // Parent
index 3f34951..f65a257 100644 (file)
@@ -1,7 +1,12 @@
-/* jshint -W024*/
 ( function ( mw, $ ) {
        $( function () {
-               mw.widgets.DateInputWidget.static.infuse( 'mw-date-start' );
-               mw.widgets.DateInputWidget.static.infuse( 'mw-date-end' );
+               var startInput = mw.widgets.DateInputWidget.static.infuse( 'mw-date-start' ),
+                       endInput = mw.widgets.DateInputWidget.static.infuse( 'mw-date-end' );
+
+               startInput.on( 'deactivate', function ( userSelected ) {
+                       if ( userSelected ) {
+                               endInput.focus();
+                       }
+               } );
        } );
 }( mediaWiki, jQuery ) );
index 84a9a96..63f2d98 100644 (file)
@@ -21,8 +21,8 @@
                                'aria-labelledby': labelFunc
                        } );
                $fieldsets.not( '#mw-prefsection-personal' )
-                               .hide()
-                               .attr( 'aria-hidden', 'true' );
+                       .hide()
+                       .attr( 'aria-hidden', 'true' );
 
                // T115692: The following is kept for backwards compatibility with older skins
                $preferences.addClass( 'jsprefs' );
@@ -72,8 +72,7 @@
                                $tab.attr( {
                                        tabIndex: 0,
                                        'aria-selected': 'true'
-                               } )
-                               .focus()
+                               } ).focus()
                                        .parent().addClass( 'selected' );
 
                                $preferences.children( 'fieldset' ).hide().attr( 'aria-hidden', 'true' );
                function detectHash() {
                        var hash = location.hash,
                                matchedElement, parentSection;
-                       if ( hash.match( /^#mw-prefsection-[\w\-]+/ ) ) {
+                       if ( hash.match( /^#mw-prefsection-[\w-]+/ ) ) {
                                mw.storage.session.remove( 'mwpreferences-prevTab' );
                                switchPrefTab( hash.replace( '#mw-prefsection-', '' ) );
-                       } else if ( hash.match( /^#mw-[\w\-]+/ ) ) {
+                       } else if ( hash.match( /^#mw-[\w-]+/ ) ) {
                                matchedElement = document.getElementById( hash.slice( 1 ) );
                                parentSection = $( matchedElement ).closest( '.prefsection' );
                                if ( parentSection.length ) {
                ) {
                        $( window ).on( 'hashchange', function () {
                                var hash = location.hash;
-                               if ( hash.match( /^#mw-[\w\-]+/ ) ) {
+                               if ( hash.match( /^#mw-[\w-]+/ ) ) {
                                        detectHash();
                                } else if ( hash === '' ) {
                                        switchPrefTab( 'personal', 'noHash' );
                                }
                        } )
-                       // Run the function immediately to select the proper tab on startup.
-                       .trigger( 'hashchange' );
+                               // Run the function immediately to select the proper tab on startup.
+                               .trigger( 'hashchange' );
                // In older browsers we'll bind a click handler as fallback.
                // We must not have onhashchange *and* the click handlers, otherwise
                // the click handler calls switchPrefTab() which sets the hash value,
index 9518283..648bf67 100644 (file)
@@ -56,8 +56,7 @@
                piprop: 'thumbnail',
                pithumbsize: 300,
                formatversion: 2
-       } )
-       .done( function ( resp ) {
+       } ).done( function ( resp ) {
                var results = ( resp.query && resp.query.pages ) ? resp.query.pages : false,
                        multimediaWidgetTemplate;
 
index ad49a42..a7a3bd3 100644 (file)
                if ( v.normalize ) {
                        v = v.normalize();
                }
-               re = new RegExp( '^\\s*' + v.replace( /([\\{}()|.?*+\-\^$\[\]])/g, '\\$1' ), 'i' );
+               re = new RegExp( '^\\s*' + v.replace( /([\\{}()|.?*+\-^$\[\]])/g, '\\$1' ), 'i' ); // eslint-disable-line no-useless-escape
                for ( k in this.values ) {
                        k = +k;
                        if ( !isNaN( k ) && re.test( this.values[ k ] ) ) {
index e42ab25..9233eef 100644 (file)
 
        /* Methods */
 
+       /**
+        * Get the currently focused field, if any
+        *
+        * @private
+        * @return {jQuery}
+        */
+       mw.widgets.datetime.DateTimeInputWidget.prototype.getFocusedField = function () {
+               return this.$fields.find( this.getElementDocument().activeElement );
+       };
+
        /**
         * Convert a date string to a Date
         *
                var delta = 0,
                        spec = $field.data( 'mw-widgets-datetime-dateTimeInputWidget-fieldSpec' );
 
-               if ( this.isDisabled() ) {
+               if ( this.isDisabled() || !this.getFocusedField().length ) {
                        return;
                }
 
         * @inheritdoc
         */
        mw.widgets.datetime.DateTimeInputWidget.prototype.focus = function () {
-               if ( !this.$fields.find( document.activeElement ).length ) {
+               if ( !this.getFocusedField().length ) {
                        this.$fields.find( '.mw-widgets-datetime-dateTimeInputWidget-editField' ).first().focus();
                }
                return this;
         * @inheritdoc
         */
        mw.widgets.datetime.DateTimeInputWidget.prototype.blur = function () {
-               this.$fields.find( document.activeElement ).blur();
+               this.getFocusedField().blur();
                return this;
        };
 
index 1cc168a..08266f0 100644 (file)
                var queryValue = this.query.getValue().trim();
 
                if ( queryValue.match( this.externalLinkUrlProtocolsRegExp ) ) {
-                       queryValue = queryValue.match( /.+\/([^\/]+)/ )[ 1 ];
+                       queryValue = queryValue.match( /.+\/([^/]+)/ )[ 1 ];
                }
                return queryValue;
        };
index c5a2dd4..b91617e 100644 (file)
                                        );
                                        currentMonth.add( 1, 'month' );
                                }
-                               // Shuffle the array to display months in columns rather than rows.
+                               // Shuffle the array to display months in columns rather than rows:
+                               // | Jan | Jul |
+                               // | Feb | Aug |
+                               // | Mar | Sep |
+                               // | Apr | Oct |
+                               // | May | Nov |
+                               // | Jun | Dec |
                                items = [
-                                       items[ 0 ], items[ 6 ],      //  | January  | July      |
-                                       items[ 1 ], items[ 7 ],      //  | February | August    |
-                                       items[ 2 ], items[ 8 ],      //  | March    | September |
-                                       items[ 3 ], items[ 9 ],      //  | April    | October   |
-                                       items[ 4 ], items[ 10 ],     //  | May      | November  |
-                                       items[ 5 ], items[ 11 ]      //  | June     | December  |
+                                       items[ 0 ], items[ 6 ],
+                                       items[ 1 ], items[ 7 ],
+                                       items[ 2 ], items[ 8 ],
+                                       items[ 3 ], items[ 9 ],
+                                       items[ 4 ], items[ 10 ],
+                                       items[ 5 ], items[ 11 ]
                                ];
                                break;
 
index 50a84f8..ce9cf36 100644 (file)
                } );
                this.inCalendar = 0;
                this.inTextInput = 0;
+               this.closing = false;
                this.inputFormat = config.inputFormat;
                this.displayFormat = config.displayFormat;
                this.longDisplayFormat = config.longDisplayFormat;
                this.$handle.on( {
                        click: this.onClick.bind( this ),
                        keypress: this.onKeyPress.bind( this ),
-                       focus: this.activate.bind( this )
+                       focus: this.onFocus.bind( this )
                } );
 
                // Initialization
        OO.inheritClass( mw.widgets.DateInputWidget, OO.ui.TextInputWidget );
        OO.mixinClass( mw.widgets.DateInputWidget, OO.ui.mixin.IndicatorElement );
 
+       /* Events */
+
+       /**
+        * Fired when the widget is deactivated (i.e. the calendar is closed). This can happen because
+        * the user selected a value, or because the user blurred the widget.
+        *
+        * @event deactivate
+        * @param {boolean} userSelected Whether the deactivation happened because the user selected a value
+        */
+
        /* Methods */
 
        /**
         * Deactivate this input field for data entry. Closes the calendar and hides the text field.
         *
         * @private
+        * @param {boolean} [userSelected] Whether we are deactivating because the user selected a value
         */
-       mw.widgets.DateInputWidget.prototype.deactivate = function () {
+       mw.widgets.DateInputWidget.prototype.deactivate = function ( userSelected ) {
                this.$element.removeClass( 'mw-widget-dateInputWidget-active' );
                this.$handle.show();
                this.textInput.toggle( false );
                this.calendar.toggle( false );
                this.setValidityFlag();
+
+               if ( userSelected ) {
+                       // Prevent focusing the handle from reopening the calendar
+                       this.closing = true;
+                       this.$handle.focus();
+                       this.closing = false;
+               }
+
+               this.emit( 'deactivate', !!userSelected );
        };
 
        /**
                }
        };
 
+       /**
+        * Handle focus events.
+        *
+        * @private
+        */
+       mw.widgets.DateInputWidget.prototype.onFocus = function () {
+               if ( !this.closing ) {
+                       this.activate();
+               }
+       };
+
        /**
         * Handle calendar key press events.
         *
         */
        mw.widgets.DateInputWidget.prototype.onCalendarKeyPress = function ( e ) {
                if ( !this.isDisabled() && e.which === OO.ui.Keys.ENTER ) {
-                       this.deactivate();
-                       this.$handle.focus();
+                       this.deactivate( true );
                        return false;
                }
        };
                if (
                        !this.isDisabled() &&
                        e.which === 1 &&
-                       $( e.target ).hasClass( 'mw-widget-calendarWidget-day' )
+                       (
+                               $( e.target ).hasClass( 'mw-widget-calendarWidget-day' ) ||
+                               $( e.target ).hasClass( 'mw-widget-calendarWidget-month' )
+                       )
                ) {
-                       this.deactivate();
-                       this.$handle.focus();
+                       this.deactivate( true );
                        return false;
                }
        };
         * @private
         */
        mw.widgets.DateInputWidget.prototype.onEnter = function () {
-               this.deactivate();
-               this.$handle.focus();
+               this.deactivate( true );
        };
 
        /**
index cdc4dbf..322143d 100644 (file)
@@ -19,8 +19,7 @@
                                title: String( page ),
                                user: user,
                                uselang: mw.config.get( 'wgUserLanguage' )
-                       }, params ) )
-                       .then( function ( data ) {
+                       }, params ) ).then( function ( data ) {
                                return data.rollback;
                        } );
                }
index 219dfb8..4a2895d 100644 (file)
 
                        upload = this.uploadWithFormData( file, data );
                        return upload.then(
-                                       null,
-                                       // If the call fails, we may want to try again...
-                                       retries === 0 ? null : retry,
-                                       function ( fraction ) {
-                                               // Since we're only uploading small parts of a file, we
-                                               // need to adjust the reported progress to reflect where
-                                               // we actually are in the combined upload
-                                               return ( start + fraction * ( end - start ) ) / file.size;
-                                       }
-                               ).promise( { abort: upload.abort } );
+                               null,
+                               // If the call fails, we may want to try again...
+                               retries === 0 ? null : retry,
+                               function ( fraction ) {
+                                       // Since we're only uploading small parts of a file, we
+                                       // need to adjust the reported progress to reflect where
+                                       // we actually are in the combined upload
+                                       return ( start + fraction * ( end - start ) ) / file.size;
+                               }
+                       ).promise( { abort: upload.abort } );
                },
 
                /**
index 3bf75ae..6d3c9fd 100644 (file)
@@ -20,7 +20,7 @@
         */
        function hideIfGetField( $el, name ) {
                var $found, $p, $widget,
-                       suffix = name.replace( /^([^\[]+)/, '[$1]' );
+                       suffix = name.replace( /^([^[]+)/, '[$1]' );
 
                function nameFilter() {
                        return this.name === name ||
index 7a1fa7f..ac2bb02 100644 (file)
         */
        mw.ForeignStructuredUpload.BookletLayout.prototype.saveFile = function () {
                var title = mw.Title.newFromText(
-                               this.getFilename(),
-                               mw.config.get( 'wgNamespaceIds' ).file
-                       );
+                       this.getFilename(),
+                       mw.config.get( 'wgNamespaceIds' ).file
+               );
 
                return this.uploadPromise
                        .then( this.validateFilename.bind( this, title ) )
index 1da4ab4..91cdc2d 100644 (file)
@@ -16,7 +16,7 @@
                 * @return {string} Escaped string
                 */
                escape: function ( str ) {
-                       return str.replace( /([\\{}()|.?*+\-\^$\[\]])/g, '\\$1' );
+                       return str.replace( /([\\{}()|.?*+\-^$\[\]])/g, '\\$1' ); // eslint-disable-line no-useless-escape
                }
        };
 }( mediaWiki ) );
index 253e0ef..398adbb 100644 (file)
                        },
                        // brackets, greater than
                        {
-                               pattern: /[\]\}>]/g,
+                               pattern: /[}\]>]/g,
                                replace: ')',
                                generalRule: true
                        },
                        // brackets, lower than
                        {
-                               pattern: /[\[\{<]/g,
+                               pattern: /[{[<]/g,
                                replace: '(',
                                generalRule: true
                        },
                }
 
                // Any remaining initial :s are illegal.
-               title = title.replace( /^\:+/, '' );
+               title = title.replace( /^:+/, '' );
 
                return Title.newFromText( title, namespace );
        };
                        thumbPhpRegex = /thumb\.php/,
                        regexes = [
                                // Thumbnails
-                               /\/[a-f0-9]\/[a-f0-9]{2}\/([^\s\/]+)\/[^\s\/]+-[^\s\/]*$/,
+                               /\/[a-f0-9]\/[a-f0-9]{2}\/([^\s/]+)\/[^\s/]+-[^\s/]*$/,
 
                                // Full size images
-                               /\/[a-f0-9]\/[a-f0-9]{2}\/([^\s\/]+)$/,
+                               /\/[a-f0-9]\/[a-f0-9]{2}\/([^\s/]+)$/,
 
                                // Thumbnails in non-hashed upload directories
-                               /\/([^\s\/]+)\/[^\s\/]+-(?:\1|thumbnail)[^\s\/]*$/,
+                               /\/([^\s/]+)\/[^\s/]+-(?:\1|thumbnail)[^\s/]*$/,
 
                                // Full-size images in non-hashed upload directories
-                               /\/([^\s\/]+)$/
+                               /\/([^\s/]+)$/
                        ],
 
                        recount = regexes.length;
index 3c1a668..939b841 100644 (file)
                                return $( '<div>' ).prop( {
                                        id: 'mw-debug-' + id,
                                        className: 'mw-debug-bit'
-                               } )
-                               .appendTo( $bits );
+                               } ).appendTo( $bits );
                        }
 
                        /**
                                        id: 'mw-debug-' + id,
                                        className: 'mw-debug-bit mw-debug-panelink'
                                } )
-                               .append( paneLabel( id, text ) )
-                               .appendTo( $bits );
+                                       .append( paneLabel( id, text ) )
+                                       .appendTo( $bits );
                        }
 
                        paneTriggerBitDiv( 'console', 'Console', this.data.log.length );
                                .append( $( '<th>SQL</th>' ) )
                                .append( $( '<th>Time</th>' ).css( 'width', '8em' ) )
                                .append( $( '<th>Call</th>' ).css( 'width', '18em' ) )
-                       .appendTo( $table );
+                               .appendTo( $table );
 
                        for ( i = 0, length = this.data.queries.length; i < length; i += 1 ) {
                                query = this.data.queries[ i ];
                                        .append( $( '<td>' ).text( query.sql ) )
                                        .append( $( '<td class="stats">' ).text( ( query.time * 1000 ).toFixed( 4 ) + 'ms' ) )
                                        .append( $( '<td>' ).text( query[ 'function' ] ) )
-                               .appendTo( $table );
+                                       .appendTo( $table );
                        }
 
                        return $table;
index f0e13b4..2d55094 100644 (file)
         */
        mw.Feedback.Dialog.prototype.validateFeedbackForm = function () {
                var isValid = (
-                               (
-                                       !this.useragentMandatory ||
-                                       this.useragentCheckbox.isSelected()
-                               ) &&
-                               this.feedbackSubjectInput.getValue()
-                       );
+                       (
+                               !this.useragentMandatory ||
+                               this.useragentCheckbox.isSelected()
+                       ) &&
+                       this.feedbackSubjectInput.getValue()
+               );
 
                this.actions.setAbilities( { submit: isValid } );
        };
index 6d3b4f0..e1681fa 100644 (file)
                        // recursive functions seem not to work in all browsers then. (Tested IE6-7, Opera, Safari, FF)
                        // This may be because, to save code, memoization was removed
 
+                       /* eslint-disable no-useless-escape */
                        regularLiteral = makeRegexParser( /^[^{}\[\]$<\\]/ );
                        regularLiteralWithoutBar = makeRegexParser( /^[^{}\[\]$\\|]/ );
                        regularLiteralWithoutSpace = makeRegexParser( /^[^{}\[\]$\s]/ );
                        regularLiteralWithSquareBrackets = makeRegexParser( /^[^{}$\\]/ );
+                       /* eslint-enable no-useless-escape */
 
                        backslash = makeStringParser( '\\' );
                        doubleQuote = makeStringParser( '"' );
                        templateName = transform(
                                // see $wgLegalTitleChars
                                // not allowing : due to the need to catch "PLURAL:$1"
-                               makeRegexParser( /^[ !"$&'()*,.\/0-9;=?@A-Z\^_`a-z~\x80-\xFF+\-]+/ ),
+                               makeRegexParser( /^[ !"$&'()*,./0-9;=?@A-Z^_`a-z~\x80-\xFF+-]+/ ),
                                function ( result ) { return result.toString(); }
                        );
                        function templateParam() {
                                        $el.attr( {
                                                role: 'button',
                                                tabindex: 0
-                                       } )
-                                       .on( 'click keypress', function ( e ) {
+                                       } ).on( 'click keypress', function ( e ) {
                                                if (
                                                        e.type === 'click' ||
                                                        e.type === 'keypress' && e.which === 13
        // Replace the default message parser with jqueryMsg
        oldParser = mw.Message.prototype.parser;
        mw.Message.prototype.parser = function () {
-               if ( this.format === 'plain' || !/\{\{|[\[<>&]/.test( this.map.get( this.key ) ) ) {
+               if ( this.format === 'plain' || !/\{\{|[<>[&]/.test( this.map.get( this.key ) ) ) {
                        // Fall back to mw.msg's simple parser
                        return oldParser.apply( this );
                }
index 18f7f0a..c5989c0 100644 (file)
        function setGlobalMapValue( map, key, value ) {
                map.values[ key ] = value;
                log.deprecate(
-                               window,
-                               key,
-                               value,
-                               // Deprecation notice for mw.config globals (T58550, T72470)
-                               map === mw.config && 'Use mw.config instead.'
+                       window,
+                       key,
+                       value,
+                       // Deprecation notice for mw.config globals (T58550, T72470)
+                       map === mw.config && 'Use mw.config instead.'
                );
        }
 
index d36c4a0..dfacfc6 100644 (file)
@@ -35,7 +35,7 @@
 
                if ( options.tag ) {
                        // Sanitize options.tag before it is used by any code. (Including Notification class methods)
-                       options.tag = options.tag.replace( /[ _\-]+/g, '-' ).replace( /[^\-a-z0-9]+/ig, '' );
+                       options.tag = options.tag.replace( /[ _-]+/g, '-' ).replace( /[^-a-z0-9]+/ig, '' );
                        if ( options.tag ) {
                                $notification.addClass( 'mw-notification-tag-' + options.tag );
                        } else {
@@ -45,7 +45,7 @@
 
                if ( options.type ) {
                        // Sanitize options.type
-                       options.type = options.type.replace( /[ _\-]+/g, '-' ).replace( /[^\-a-z0-9]+/ig, '' );
+                       options.type = options.type.replace( /[ _-]+/g, '-' ).replace( /[^-a-z0-9]+/ig, '' );
                        $notification.addClass( 'mw-notification-type-' + options.type );
                }
 
index 6a6aa15..650092b 100644 (file)
@@ -44,8 +44,7 @@
         *  by that time.
         */
        mw.requestIdleCallback = window.requestIdleCallback ?
-               // Bind because it throws TypeError if context is not window
-               window.requestIdleCallback.bind( window ) :
+               window.requestIdleCallback.bind( window ) : // Bind because it throws TypeError if context is not window
                mw.requestIdleCallbackInternal;
        // Note: Polyfill was previously disabled due to
        // https://bugs.chromium.org/p/chromium/issues/detail?id=647870
index bcb6c33..1c1150e 100644 (file)
@@ -87,9 +87,9 @@
                 */
                function getInputLocation( context ) {
                        return context.config.$region
-                                       .closest( 'form' )
-                                       .find( '[data-search-loc]' )
-                                       .data( 'search-loc' ) || 'header';
+                               .closest( 'form' )
+                               .find( '[data-search-loc]' )
+                               .data( 'search-loc' ) || 'header';
                }
 
                /**
                                var $this = $( this );
                                $this
                                        .data( 'suggestions-context' )
-                                       .data.$container
-                                               .css( 'fontSize', $this.css( 'fontSize' ) );
+                                       .data.$container.css( 'fontSize', $this.css( 'fontSize' ) );
                        } );
 
                // Ensure that the thing is actually present!
index 53e8d93..5e10a5b 100644 (file)
@@ -45,8 +45,8 @@
                                        $tocToggleLink
                                                .wrap( '<span class="toctoggle"></span>' )
                                                .parent()
-                                                       .prepend( '&nbsp;[' )
-                                                       .append( ']&nbsp;' )
+                                               .prepend( '&nbsp;[' )
+                                               .append( ']&nbsp;' )
                                );
 
                                if ( hideToc ) {
index 6d6d46d..d8fb249 100644 (file)
@@ -33,8 +33,7 @@
                                formatversion: 2,
                                action: 'patrol',
                                rcid: rcid
-                       } )
-                       .done( function ( data ) {
+                       } ).done( function ( data ) {
                                var title;
                                // Remove all patrollinks from the page (including any spinners inside).
                                $patrolLinks.closest( '.patrollink' ).remove();
@@ -46,8 +45,7 @@
                                        // This should never happen as errors should trigger fail
                                        mw.notify( mw.msg( 'markedaspatrollederrornotify' ), { type: 'error' } );
                                }
-                       } )
-                       .fail( function ( error ) {
+                       } ).fail( function ( error ) {
                                $spinner.remove();
                                // Restore the patrol link. This allows the user to try again
                                // (or open it in a new window, bypassing this ajax module).
index d94b158..6db518d 100644 (file)
@@ -45,8 +45,8 @@
                                        $( e.delegateTarget ).remove();
                                }, function ( errorCode, data ) {
                                        var message = data && data.error && data.error.messageHtml ?
-                                               $.parseHTML( data.error.messageHtml ) :
-                                               mw.msg( 'rollbackfailed' ),
+                                                       $.parseHTML( data.error.messageHtml ) :
+                                                       mw.msg( 'rollbackfailed' ),
                                                type = errorCode === 'alreadyrolled' ? 'warn' : 'error';
 
                                        mw.notify( message, {
index 9bd5d8d..1015b0b 100644 (file)
@@ -22,7 +22,8 @@
  * @param string $text1
  * @param string $text2
  * @param int $numContextLines
+ * @param int $movedParagraphDetectionCutoff
  * @return string
  */
-function wikidiff2_do_diff( $text1, $text2, $numContextLines ) {
+function wikidiff2_do_diff( $text1, $text2, $numContextLines, $movedParagraphDetectionCutoff = 0 ) {
 }
index 15833dc..31cfa70 100644 (file)
@@ -640,8 +640,8 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
         * The key is added to the array of globals that will be reset afterwards
         * in the tearDown().
         *
-        * @example
-        * <code>
+        * @par Example
+        * @code
         *     protected function setUp() {
         *         $this->setMwGlobals( 'wgRestrictStuff', true );
         *     }
@@ -656,7 +656,7 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
         *     }
         *
         *     function testQuux() {}
-        * </code>
+        * @endcode
         *
         * @param array|string $pairs Key to the global variable, or an array
         *  of key/value pairs.
index e2209eb..4070bc0 100644 (file)
@@ -37,11 +37,8 @@ class ChangesListSpecialPageTest extends AbstractChangesListSpecialPageTestCase
                return $mock;
        }
 
-       /** helper to test SpecialRecentchanges::buildMainQueryConds() */
-       private function assertConditions(
-               $expected,
+       private function buildQuery(
                $requestOptions = null,
-               $message = '',
                $user = null
        ) {
                $context = new RequestContext;
@@ -81,6 +78,18 @@ class ChangesListSpecialPageTest extends AbstractChangesListSpecialPageTestCase
                        'ChangesListSpecialPageTest::filterOutRcTimestampCondition'
                );
 
+               return $queryConditions;
+       }
+
+       /** helper to test SpecialRecentchanges::buildQuery() */
+       private function assertConditions(
+               $expected,
+               $requestOptions = null,
+               $message = '',
+               $user = null
+       ) {
+               $queryConditions = $this->buildQuery( $requestOptions, $user );
+
                $this->assertEquals(
                        self::normalizeCondition( $expected ),
                        self::normalizeCondition( $queryConditions ),
@@ -373,6 +382,104 @@ class ChangesListSpecialPageTest extends AbstractChangesListSpecialPageTestCase
                );
        }
 
+       public function testFilterUserExpLevelAll() {
+               $this->assertConditions(
+                       [
+                               # expected
+                       ],
+                       [
+                               'userExpLevel' => 'registered;unregistered;newcomer;learner;experienced',
+                       ],
+                       "rc conditions: userExpLevel=registered;unregistered;newcomer;learner;experienced"
+               );
+       }
+
+       public function testFilterUserExpLevelRegisteredUnregistered() {
+               $this->assertConditions(
+                       [
+                               # expected
+                       ],
+                       [
+                               'userExpLevel' => 'registered;unregistered',
+                       ],
+                       "rc conditions: userExpLevel=registered;unregistered"
+               );
+       }
+
+       public function testFilterUserExpLevelRegisteredUnregisteredLearner() {
+               $this->assertConditions(
+                       [
+                               # expected
+                       ],
+                       [
+                               'userExpLevel' => 'registered;unregistered;learner',
+                       ],
+                       "rc conditions: userExpLevel=registered;unregistered;learner"
+               );
+       }
+
+       public function testFilterUserExpLevelAllExperienceLevels() {
+               $this->assertConditions(
+                       [
+                               # expected
+                               'rc_user != 0',
+                       ],
+                       [
+                               'userExpLevel' => 'newcomer;learner;experienced',
+                       ],
+                       "rc conditions: userExpLevel=newcomer;learner;experienced"
+               );
+       }
+
+       public function testFilterUserExpLevelRegistrered() {
+               $this->assertConditions(
+                       [
+                               # expected
+                               'rc_user != 0',
+                       ],
+                       [
+                               'userExpLevel' => 'registered',
+                       ],
+                       "rc conditions: userExpLevel=registered"
+               );
+       }
+
+       public function testFilterUserExpLevelUnregistrered() {
+               $this->assertConditions(
+                       [
+                               # expected
+                               'rc_user' => 0,
+                       ],
+                       [
+                               'userExpLevel' => 'unregistered',
+                       ],
+                       "rc conditions: userExpLevel=unregistered"
+               );
+       }
+
+       public function testFilterUserExpLevelRegistreredOrLearner() {
+               $this->assertConditions(
+                       [
+                               # expected
+                               'rc_user != 0',
+                       ],
+                       [
+                               'userExpLevel' => 'registered;learner',
+                       ],
+                       "rc conditions: userExpLevel=registered;learner"
+               );
+       }
+
+       public function testFilterUserExpLevelUnregistreredOrExperienced() {
+               $conds = $this->buildQuery( [ 'userExpLevel' => 'unregistered;experienced' ] );
+
+               $this->assertRegExp(
+                       '/\(rc_user = 0\) OR \(\(user_editcount >= 500\) AND \(user_registration <= \'\d+\'\)\)/',
+                       reset( $conds ),
+                       "rc conditions: userExpLevel=unregistered;experienced"
+               );
+       }
+
        public function testFilterUserExpLevel() {
                $now = time();
                $this->setMwGlobals( [
@@ -438,18 +545,6 @@ class ChangesListSpecialPageTest extends AbstractChangesListSpecialPageTestCase
                        $this->fetchUsers( [ 'learner', 'experienced' ], $now ),
                        'Learner and more experienced'
                );
-
-               // newcomers, learner, and more experienced
-               // TOOD: Fix test.  This needs to test that anons are excluded,
-               // and right now the join fails.
-               /* $this->assertArrayEquals( */
-               /*      [ */
-               /*              'Newcomer1', 'Newcomer2', 'Newcomer3', */
-               /*              'Learner1', 'Learner2', 'Learner3', 'Learner4', */
-               /*              'Experienced1', */
-               /*      ], */
-               /*      $this->fetchUsers( [ 'newcomer', 'learner', 'experienced' ], $now ) */
-               /* ); */
        }
 
        private function createUsers( $specs, $now ) {
@@ -798,7 +893,7 @@ class ChangesListSpecialPageTest extends AbstractChangesListSpecialPageTestCase
                                        "hideliu" => true,
                                        "userExpLevel" => "newcomer",
                                ],
-                               "expectedConflicts" => true,
+                               "expectedConflicts" => false,
                        ],
                        [
                                "parameters" => [
index 7babcac..2f19c81 100644 (file)
@@ -33,6 +33,7 @@
                <testsuite name="skins">
                        <directory>skins</directory>
                        <directory>structure</directory>
+                       <file>suites/ExtensionsTestSuite.php</file>
                        <file>suites/LessTestSuite.php</file>
                </testsuite>
                <!-- As there is a class Maintenance, we cannot use the name "maintenance" directly -->
index d7da5a0..f023ddd 100644 (file)
                var orgModule = QUnit.module;
 
                QUnit.module = function ( name, localEnv, executeNow ) {
+                       if ( QUnit.config.moduleStack.length ) {
+                               // When inside a nested module, don't add our Sinon
+                               // setup/teardown a second time.
+                               return orgModule.apply( this, arguments );
+                       }
+
                        if ( arguments.length === 2 && typeof localEnv === 'function' ) {
                                executeNow = localEnv;
                                localEnv = undefined;
@@ -85,9 +91,7 @@
                                                localEnv.teardown.call( this );
                                        }
 
-                                       if ( this.sandbox ) {
-                                               this.sandbox.verifyAndRestore();
-                                       }
+                                       this.sandbox.verifyAndRestore();
                                }
                        }, executeNow );
                };
index 5c1be67..8555a7e 100644 (file)
        byteLimitTest( {
                description: 'Input filter that increases the length',
                $input: $( '<input>' ).attr( 'type', 'text' )
-               .byteLimit( 10, function ( text ) {
-                       return 'prefix' + text;
-               } ),
+                       .byteLimit( 10, function ( text ) {
+                               return 'prefix' + text;
+                       } ),
                sample: simpleSample,
                // Prefix adds 6 characters, limit is reached after 4
                expected: '1234'
        byteLimitTest( {
                description: 'Input filter of which the base exceeds the limit',
                $input: $( '<input>' ).attr( 'type', 'text' )
-               .byteLimit( 3, function ( text ) {
-                       return 'prefix' + text;
-               } ),
+                       .byteLimit( 3, function ( text ) {
+                               return 'prefix' + text;
+                       } ),
                sample: simpleSample,
                hasLimit: true,
                limit: 6, // 'prefix' length
index 53d29cf..0c91e43 100644 (file)
 
        QUnit.test( 'mw-made-collapsible data added', function ( assert ) {
                var $collapsible = prepareCollapsible(
-                               '<div>' + loremIpsum + '</div>'
-                       );
+                       '<div>' + loremIpsum + '</div>'
+               );
 
                assert.equal( $collapsible.data( 'mw-made-collapsible' ), true, 'mw-made-collapsible data present' );
        } );
 
        QUnit.test( 'mw-collapsible added when missing', function ( assert ) {
                var $collapsible = prepareCollapsible(
-                               '<div>' + loremIpsum + '</div>'
-                       );
+                       '<div>' + loremIpsum + '</div>'
+               );
 
                assert.assertTrue( $collapsible.hasClass( 'mw-collapsible' ), 'mw-collapsible class present' );
        } );
        QUnit.test( 'mw-collapsed added when missing', function ( assert ) {
                var $collapsible = prepareCollapsible(
                        '<div>' + loremIpsum + '</div>',
-                               { collapsed: true }
-                       );
+                       { collapsed: true }
+               );
 
                assert.assertTrue( $collapsible.hasClass( 'mw-collapsed' ), 'mw-collapsed class present' );
        } );
index 854e4b1..4ee8038 100644 (file)
                                'action=options&format=json&formatversion=2&optionname=foo%7Cbar%3Dquux&token=%2B%5C'
                        ] ) !== -1 ) {
                                assert.ok( true, 'Repond to ' + request.requestBody );
-                               request.respond( 200, { 'Content-Type': 'application/json' },
-                                               '{ "options": "success" }' );
+                               request.respond(
+                                       200,
+                                       { 'Content-Type': 'application/json' },
+                                       '{ "options": "success" }'
+                               );
                        } else {
                                assert.ok( false, 'Unexpected request: ' + request.requestBody );
                        }
index e10a7fa..2361f70 100644 (file)
                                        'X-Foo': 'Bar'
                                }
                        }
-               )
-               .then( function () {
+               ).then( function () {
                        assert.equal( test.server.requests[ 0 ].requestHeaders[ 'X-Foo' ], 'Bar', 'Header sent' );
 
                        return api.postWithToken( 'csrf',
                                        assert.ok( false, 'This parameter cannot be a callback' );
                                }
                        );
-               } )
-               .then( function ( data ) {
+               } ).then( function ( data ) {
                        assert.equal( data.example, 'quux' );
 
                        assert.equal( test.server.requests.length, 2, 'Request made' );
index 3c77a00..db51fb3 100644 (file)
                );
                assert.equal(
                        formatParse( 'external-link-plural', 2, 'http://example.org' ),
-                       'Foo <a href=\"http://example.org\">two</a> things.',
+                       'Foo <a href="http://example.org">two</a> things.',
                        'Link is expanded inside an explicit plural form and is not escaped html'
                );
                assert.equal(
index 30654fa..985ff92 100644 (file)
                mw.loader.implement( 'test.promise', [ QUnit.fixurl( mw.config.get( 'wgScriptPath' ) + '/tests/qunit/data/mwLoaderTestCallback.js' ) ] );
 
                return mw.loader.using( 'test.promise' )
-               .done( function () {
-                       assert.strictEqual( isAwesomeDone, true, 'test.promise module should\'ve caused isAwesomeDone to be true' );
-                       delete mw.loader.testCallback;
-               } )
-               .fail( function () {
-                       assert.ok( false, 'Error callback fired while loader.using "test.promise" module' );
-               } );
+                       .done( function () {
+                               assert.strictEqual( isAwesomeDone, true, 'test.promise module should\'ve caused isAwesomeDone to be true' );
+                               delete mw.loader.testCallback;
+                       } )
+                       .fail( function () {
+                               assert.ok( false, 'Error callback fired while loader.using "test.promise" module' );
+                       } );
        } );
 
        // Covers mw.loader#sortDependencies (with native Set if available)
                                assert.ok( /Circular/.test( String( e ) ), 'Detect circular dependency' );
                        }
                )
-               .always( done );
+                       .always( done );
        } );
 
        // @covers mw.loader#sortDependencies (with fallback shim)
                                assert.ok( /Circular/.test( String( e ) ), 'Detect circular dependency' );
                        }
                )
-               .always( done );
+                       .always( done );
        } );
 
        QUnit.test( '.load() - Error: Circular dependency', function ( assert ) {
                                }
                        };
                } );
-               return mw.loader.using( [ 'test.require1', 'test.require2', 'test.require3', 'test.require4' ] )
-               .then( function ( require ) {
+               return mw.loader.using( [ 'test.require1', 'test.require2', 'test.require3', 'test.require4' ] ).then( function ( require ) {
                        var module1, module2, module3, module4;
 
                        module1 = require( 'test.require1' );
index c8fb8a7..f3e4877 100644 (file)
@@ -1,7 +1,6 @@
-/* eslint comma-dangle: 0 */
-/* eslint no-undef: "error" */
-/* eslint no-console: 0 */
 /* eslint-env node */
+/* eslint no-undef: "error" */
+/* eslint-disable no-console, comma-dangle */
 'use strict';
 
 const path = require( 'path' );
@@ -114,12 +113,12 @@ exports.config = {
        // with "/", then the base url gets prepended.
        baseUrl: (
                process.env.MW_SERVER === undefined ?
-               'http://127.0.0.1:8080' :
-               process.env.MW_SERVER
+                       'http://127.0.0.1:8080' :
+                       process.env.MW_SERVER
        ) + (
                process.env.MW_SCRIPT_PATH === undefined ?
-               '/w' :
-               process.env.MW_SCRIPT_PATH
+                       '/w' :
+                       process.env.MW_SCRIPT_PATH
        ),
        //
        // Default timeout for all waitFor* commands.