Merge "Re-introduce use of mime_content_type()"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Thu, 27 Jul 2017 03:05:45 +0000 (03:05 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Thu, 27 Jul 2017 03:05:45 +0000 (03:05 +0000)
115 files changed:
RELEASE-NOTES-1.30
autoload.php
includes/SiteStats.php
includes/Title.php
includes/api/i18n/he.json
includes/api/i18n/ko.json
includes/changes/EnhancedChangesList.php
includes/changetags/ChangeTags.php
includes/config/EtcdConfig.php
includes/diff/DifferenceEngine.php
includes/htmlform/HTMLForm.php
includes/installer/DatabaseUpdater.php
includes/installer/i18n/csb.json
includes/installer/i18n/ms.json
includes/jobqueue/JobQueueFederated.php
includes/jobqueue/JobQueueSecondTestQueue.php [new file with mode: 0644]
includes/libs/objectcache/MultiWriteBagOStuff.php
includes/libs/rdbms/database/Database.php
includes/logging/BlockLogFormatter.php
includes/page/WikiPage.php
includes/specialpage/LoginSignupSpecialPage.php
includes/specials/SpecialRecentchanges.php
includes/specials/SpecialRecentchangeslinked.php
includes/specials/SpecialUndelete.php
includes/specials/SpecialWatchlist.php
languages/data/Names.php
languages/i18n/ast.json
languages/i18n/ba.json
languages/i18n/be-tarask.json
languages/i18n/be.json
languages/i18n/bg.json
languages/i18n/bho.json
languages/i18n/bn.json
languages/i18n/bs.json
languages/i18n/ckb.json
languages/i18n/cs.json
languages/i18n/cy.json
languages/i18n/da.json
languages/i18n/de.json
languages/i18n/es.json
languages/i18n/et.json
languages/i18n/eu.json
languages/i18n/fr.json
languages/i18n/frr.json
languages/i18n/gl.json
languages/i18n/gor.json
languages/i18n/gsw.json
languages/i18n/gu.json
languages/i18n/he.json
languages/i18n/hi.json
languages/i18n/hr.json
languages/i18n/hu.json
languages/i18n/hy.json
languages/i18n/inh.json
languages/i18n/it.json
languages/i18n/jv.json
languages/i18n/ko.json
languages/i18n/lb.json
languages/i18n/li.json
languages/i18n/mk.json
languages/i18n/ms.json
languages/i18n/nap.json
languages/i18n/nb.json
languages/i18n/nl.json
languages/i18n/nn.json
languages/i18n/pl.json
languages/i18n/pnb.json
languages/i18n/pt-br.json
languages/i18n/pt.json
languages/i18n/rif.json
languages/i18n/rm.json
languages/i18n/ro.json
languages/i18n/roa-tara.json
languages/i18n/ru.json
languages/i18n/sd.json
languages/i18n/shi.json
languages/i18n/skr-arab.json
languages/i18n/sl.json
languages/i18n/sr-ec.json
languages/i18n/sr-el.json
languages/i18n/sv.json
languages/i18n/te.json
languages/i18n/uk.json
languages/i18n/vi.json
languages/i18n/yi.json
languages/i18n/yue.json
languages/i18n/zh-hans.json
languages/i18n/zh-hant.json
languages/messages/MessagesSkr.php [new file with mode: 0644]
languages/messages/MessagesSkr_arab.php [new file with mode: 0644]
maintenance/addRFCandPMIDInterwiki.php
maintenance/populatePPSortKey.php [new file with mode: 0644]
resources/lib/qunitjs/qunit.css
resources/lib/qunitjs/qunit.js
resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FiltersViewModel.js
resources/src/mediawiki.rcfilters/mw.rcfilters.Controller.js
resources/src/mediawiki.rcfilters/mw.rcfilters.init.js
resources/src/mediawiki.rcfilters/styles/mw.rcfilters.less
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.ChangesListWrapperWidget.js
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.DateButtonWidget.js
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.DatePopupWidget.js
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FormWrapperWidget.js
tests/parser/ParserTestRunner.php
tests/parser/parserTests.txt
tests/phpunit/MediaWikiTestCase.php
tests/phpunit/includes/SanitizerTest.php
tests/phpunit/includes/SiteStatsTest.php [new file with mode: 0644]
tests/phpunit/includes/changetags/ChangeTagsTest.php [new file with mode: 0644]
tests/phpunit/includes/config/EtcdConfigTest.php
tests/phpunit/includes/libs/objectcache/MultiWriteBagOStuffTest.php
tests/phpunit/includes/libs/rdbms/database/DatabaseSQLTest.php
tests/phpunit/includes/libs/rdbms/database/DatabaseTest.php
tests/phpunit/includes/specials/SpecialRecentchangesTest.php
tests/qunit/data/testrunner.js
tests/qunit/suites/resources/jquery/jquery.tablesorter.test.js

index 51f9764..c5ab81a 100644 (file)
@@ -46,6 +46,7 @@ section).
 === Languages updated in 1.30 ===
 
 * Support for kbp (Kabɩyɛ / Kabiyè) was added.
+* Support for skr (Saraiki, سرائیکی) was added.
 
 === External library changes in 1.30 ===
 
index a6128a4..510eeee 100644 (file)
@@ -679,6 +679,7 @@ $wgAutoloadLocalClasses = [
        'JobQueueMemory' => __DIR__ . '/includes/jobqueue/JobQueueMemory.php',
        'JobQueueReadOnlyError' => __DIR__ . '/includes/jobqueue/JobQueue.php',
        'JobQueueRedis' => __DIR__ . '/includes/jobqueue/JobQueueRedis.php',
+       'JobQueueSecondTestQueue' => __DIR__ . '/includes/jobqueue/JobQueueSecondTestQueue.php',
        'JobRunner' => __DIR__ . '/includes/jobqueue/JobRunner.php',
        'JobSpecification' => __DIR__ . '/includes/jobqueue/JobSpecification.php',
        'JpegHandler' => __DIR__ . '/includes/media/Jpeg.php',
@@ -1118,6 +1119,7 @@ $wgAutoloadLocalClasses = [
        'PopulateInterwiki' => __DIR__ . '/maintenance/populateInterwiki.php',
        'PopulateLogSearch' => __DIR__ . '/maintenance/populateLogSearch.php',
        'PopulateLogUsertext' => __DIR__ . '/maintenance/populateLogUsertext.php',
+       'PopulatePPSortKey' => __DIR__ . '/maintenance/populatePPSortKey.php',
        'PopulateParentId' => __DIR__ . '/maintenance/populateParentId.php',
        'PopulateRecentChangesSource' => __DIR__ . '/maintenance/populateRecentChangesSource.php',
        'PopulateRevisionLength' => __DIR__ . '/maintenance/populateRevisionLength.php',
index df3e305..6a2d0e2 100644 (file)
@@ -34,9 +34,6 @@ class SiteStats {
        /** @var bool */
        private static $loaded = false;
 
-       /** @var int */
-       private static $jobs;
-
        /** @var int[] */
        private static $pageCount = [];
 
@@ -213,17 +210,24 @@ class SiteStats {
        }
 
        /**
+        * Total number of jobs in the job queue.
         * @return int
         */
        static function jobs() {
-               if ( !isset( self::$jobs ) ) {
-                       try{
-                               self::$jobs = array_sum( JobQueueGroup::singleton()->getQueueSizes() );
-                       } catch ( JobQueueError $e ) {
-                               self::$jobs = 0;
-                       }
-               }
-               return self::$jobs;
+               $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
+               return $cache->getWithSetCallback(
+                       $cache->makeKey( 'SiteStats', 'jobscount' ),
+                       $cache::TTL_MINUTE,
+                       function ( $oldValue, &$ttl, array &$setOpts ) {
+                               try{
+                                       $jobs = array_sum( JobQueueGroup::singleton()->getQueueSizes() );
+                               } catch ( JobQueueError $e ) {
+                                       $jobs = 0;
+                               }
+                               return $jobs;
+                       },
+                       [ 'pcTTL' => $cache::TTL_PROC_LONG ]
+               );
        }
 
        /**
index 6538538..edfdaca 100644 (file)
@@ -3172,7 +3172,7 @@ class Title implements LinkTarget {
                if ( $limit > -1 ) {
                        $options['LIMIT'] = $limit;
                }
-               $this->mSubpages = TitleArray::newFromResult(
+               return TitleArray::newFromResult(
                        $dbr->select( 'page',
                                [ 'page_id', 'page_namespace', 'page_title', 'page_is_redirect' ],
                                $conds,
@@ -3180,7 +3180,6 @@ class Title implements LinkTarget {
                                $options
                        )
                );
-               return $this->mSubpages;
        }
 
        /**
index 6fea718..8918620 100644 (file)
        "apihelp-query+langlinks-param-lang": "להחזיר רק קישורי שפה עם קוד השפה הזה.",
        "apihelp-query+langlinks-param-title": "קישור לחיפוש. חובה להשתמש עם <var>$1lang</var>.",
        "apihelp-query+langlinks-param-dir": "באיזה כיוון לרשום.",
-       "apihelp-query+langlinks-param-inlanguagecode": "ק×\95×\93 ×©×¤×\94 ×©שמות שפות מתורגמות.",
+       "apihelp-query+langlinks-param-inlanguagecode": "ק×\95×\93 ×©×¤×\94 ×\91ש×\91×\99×\9c שמות שפות מתורגמות.",
        "apihelp-query+langlinks-example-simple": "קבלת קישורים בין־לשוניים מהדף <kbd>Main Page</kbd>.",
        "apihelp-query+links-summary": "החזרת כל הקישורים מהדפים שצוינו.",
        "apihelp-query+links-param-namespace": "להציג קישורים רק במרחבי השם האלה.",
        "apihelp-query+siteinfo-param-filteriw": "החזרה רק של עיולים מקומיים או רק של עיולים לא מקומיים ממפת הבינוויקי.",
        "apihelp-query+siteinfo-param-showalldb": "רשימת כל שרתי מסד הנתונים, לא רק אלה שהכי מתעכבים.",
        "apihelp-query+siteinfo-param-numberingroup": "רשימת מספרי משתמשים בקבוצות משתמשים.",
-       "apihelp-query+siteinfo-param-inlanguagecode": "ק×\95×\93 ×©×¤×\94 ×©שמות שפות מתורגמות (מאמץ טוב ביותר) ושמות עיצובים.",
+       "apihelp-query+siteinfo-param-inlanguagecode": "ק×\95×\93 ×©×¤×\94 ×\91ש×\91×\99×\9c שמות שפות מתורגמות (מאמץ טוב ביותר) ושמות עיצובים.",
        "apihelp-query+siteinfo-example-simple": "איזור מידע על האתר.",
        "apihelp-query+siteinfo-example-interwiki": "אחזור תחיליות בינוויקי מקומיות.",
        "apihelp-query+siteinfo-example-replag": "בדיקת שיהוי השכפול הנוכחי.",
index 36b580e..391c06c 100644 (file)
        "apihelp-setpagelanguage-param-lang": "문서를 변경할 언어의 언어 코드입니다. 문서를 위키의 기본 콘텐츠 언어로 재설정하려면 <kbd>default</kbd>를 사용하십시오.",
        "apihelp-setpagelanguage-param-reason": "변경 이유.",
        "apihelp-setpagelanguage-example-language": "<kbd>Main Page</kbd>의 언어를 바스크어로 변경합니다.",
+       "apihelp-stashedit-summary": "공유된 캐시에서 편집을 준비합니다.",
        "apihelp-stashedit-param-sectiontitle": "새 문단을 위한 제목.",
        "apihelp-stashedit-param-text": "문서 내용.",
        "apihelp-stashedit-param-contentmodel": "새 콘텐츠의 콘텐츠 모델.",
index 30c6995..55cb9ed 100644 (file)
@@ -102,15 +102,17 @@ class EnhancedChangesList extends ChangesList {
                        $rc->mAttribs['rc_timestamp'],
                        $this->getUser()
                );
+               if ( $this->lastdate === '' ) {
+                       $this->lastdate = $date;
+               }
 
                $ret = '';
 
-               # If it's a new day, add the headline and flush the cache
-               if ( $date != $this->lastdate ) {
-                       # Process current cache
+               # If it's a new day, flush the cache and update $this->lastdate
+               if ( $date !== $this->lastdate ) {
+                       # Process current cache (uses $this->lastdate to generate a heading)
                        $ret = $this->recentChangesBlock();
                        $this->rc_cache = [];
-                       $ret .= Xml::element( 'h4', null, $date ) . "\n";
                        $this->lastdate = $date;
                }
 
@@ -763,7 +765,11 @@ class EnhancedChangesList extends ChangesList {
                        }
                }
 
-               return '<div>' . $blockOut . '</div>';
+               if ( $blockOut === '' ) {
+                       return '';
+               }
+               // $this->lastdate is kept up to date by recentChangesLine()
+               return Xml::element( 'h4', null, $this->lastdate ) . "\n<div>" . $blockOut . '</div>';
        }
 
        /**
index c9b5f96..2eb9b22 100644 (file)
@@ -643,24 +643,32 @@ class ChangeTags {
         * Handles selecting tags, and filtering.
         * Needs $tables to be set up properly, so we can figure out which join conditions to use.
         *
+        * WARNING: If $filter_tag contains more than one tag, this function will add DISTINCT,
+        * which may cause performance problems for your query unless you put the ID field of your
+        * table at the end of the ORDER BY, and set a GROUP BY equal to the ORDER BY. For example,
+        * if you had ORDER BY foo_timestamp DESC, you will now need GROUP BY foo_timestamp, foo_id
+        * ORDER BY foo_timestamp DESC, foo_id DESC.
+        *
         * @param string|array $tables Table names, see Database::select
         * @param string|array $fields Fields used in query, see Database::select
         * @param string|array $conds Conditions used in query, see Database::select
         * @param array $join_conds Join conditions, see Database::select
-        * @param array $options Options, see Database::select
-        * @param bool|string $filter_tag Tag to select on
+        * @param string|array $options Options, see Database::select
+        * @param string|array $filter_tag Tag(s) to select on
         *
         * @throws MWException When unable to determine appropriate JOIN condition for tagging
         */
        public static function modifyDisplayQuery( &$tables, &$fields, &$conds,
-                                                                               &$join_conds, &$options, $filter_tag = false ) {
-               global $wgRequest, $wgUseTagFilter;
+                                                                               &$join_conds, &$options, $filter_tag = '' ) {
+               global $wgUseTagFilter;
 
-               if ( $filter_tag === false ) {
-                       $filter_tag = $wgRequest->getVal( 'tagfilter' );
-               }
+               // Normalize to arrays
+               $tables = (array)$tables;
+               $fields = (array)$fields;
+               $conds = (array)$conds;
+               $options = (array)$options;
 
-               // Figure out which conditions can be done.
+               // Figure out which ID field to use
                if ( in_array( 'recentchanges', $tables ) ) {
                        $join_cond = 'ct_rc_id=rc_id';
                } elseif ( in_array( 'logging', $tables ) ) {
@@ -683,7 +691,13 @@ class ChangeTags {
 
                        $tables[] = 'change_tag';
                        $join_conds['change_tag'] = [ 'INNER JOIN', $join_cond ];
-                       $conds['ct_tag'] = explode( '|', $filter_tag );
+                       $conds['ct_tag'] = $filter_tag;
+                       if (
+                               is_array( $filter_tag ) && count( $filter_tag ) > 1 &&
+                               !in_array( 'DISTINCT', $options )
+                       ) {
+                               $options[] = 'DISTINCT';
+                       }
                }
        }
 
@@ -962,7 +976,7 @@ class ChangeTags {
 
                // tags cannot contain commas (used as a delimiter in tag_summary table),
                // pipe (used as a delimiter between multiple tags in
-               // modifyDisplayQuery), or slashes (would break tag description messages in
+               // SpecialRecentchanges and friends), or slashes (would break tag description messages in
                // MediaWiki namespace)
                if ( strpos( $tag, ',' ) !== false || strpos( $tag, '|' ) !== false
                        || strpos( $tag, '/' ) !== false ) {
index c57eba7..6605c38 100644 (file)
@@ -243,7 +243,7 @@ class EtcdConfig implements Config, LoggerAwareInterface {
 
                $info = json_decode( $rbody, true );
                if ( $info === null || !isset( $info['node']['nodes'] ) ) {
-                       return [ null, $rcode, "Unexpected JSON response; missing 'nodes' list.", false ];
+                       return [ null, "Unexpected JSON response; missing 'nodes' list.", false ];
                }
 
                $config = [];
index d4bee29..7f9af60 100644 (file)
@@ -1174,17 +1174,17 @@ class DifferenceEngine extends ContextSource {
 
                if ( !$diff && !$otitle ) {
                        $header .= "
-                       <tr style='vertical-align: top;' lang='{$userLang}'>
-                       <td class='diff-ntitle'>{$ntitle}</td>
+                       <tr style=\"vertical-align: top;\" lang=\"{$userLang}\">
+                       <td class=\"diff-ntitle\">{$ntitle}</td>
                        </tr>";
                        $multiColspan = 1;
                } else {
                        if ( $diff ) { // Safari/Chrome show broken output if cols not used
                                $header .= "
-                               <col class='diff-marker' />
-                               <col class='diff-content' />
-                               <col class='diff-marker' />
-                               <col class='diff-content' />";
+                               <col class=\"diff-marker\" />
+                               <col class=\"diff-content\" />
+                               <col class=\"diff-marker\" />
+                               <col class=\"diff-content\" />";
                                $colspan = 2;
                                $multiColspan = 4;
                        } else {
@@ -1193,20 +1193,20 @@ class DifferenceEngine extends ContextSource {
                        }
                        if ( $otitle || $ntitle ) {
                                $header .= "
-                               <tr style='vertical-align: top;' lang='{$userLang}'>
-                               <td colspan='$colspan' class='diff-otitle'>{$otitle}</td>
-                               <td colspan='$colspan' class='diff-ntitle'>{$ntitle}</td>
+                               <tr style=\"vertical-align: top;\" lang=\"{$userLang}\">
+                               <td colspan=\"$colspan\" class=\"diff-otitle\">{$otitle}</td>
+                               <td colspan=\"$colspan\" class=\"diff-ntitle\">{$ntitle}</td>
                                </tr>";
                        }
                }
 
                if ( $multi != '' ) {
-                       $header .= "<tr><td colspan='{$multiColspan}' style='text-align: center;' " .
-                               "class='diff-multi' lang='{$userLang}'>{$multi}</td></tr>";
+                       $header .= "<tr><td colspan=\"{$multiColspan}\" style=\"text-align: center;\" " .
+                               "class=\"diff-multi\" lang=\"{$userLang}\">{$multi}</td></tr>";
                }
                if ( $notice != '' ) {
-                       $header .= "<tr><td colspan='{$multiColspan}' style='text-align: center;' " .
-                               "lang='{$userLang}'>{$notice}</td></tr>";
+                       $header .= "<tr><td colspan=\"{$multiColspan}\" style=\"text-align: center;\" " .
+                               "lang=\"{$userLang}\">{$notice}</td></tr>";
                }
 
                return $header . $diff . "</table>";
index 61d0d89..d4351e0 100644 (file)
@@ -400,7 +400,13 @@ class HTMLForm extends ContextSource {
 
                if ( !in_array( $format, $this->availableDisplayFormats, true ) ) {
                        throw new MWException( 'Display format must be one of ' .
-                               print_r( $this->availableDisplayFormats, true ) );
+                               print_r(
+                                       array_merge(
+                                               $this->availableDisplayFormats,
+                                               $this->availableSubclassDisplayFormats
+                                       ),
+                                       true
+                               ) );
                }
 
                // Evil hack for mobile :(
index 7e43fb5..7b6ac5e 100644 (file)
@@ -83,6 +83,7 @@ abstract class DatabaseUpdater {
                FixDefaultJsonContentPages::class,
                CleanupEmptyCategories::class,
                AddRFCAndPMIDInterwiki::class,
+               PopulatePPSortKey::class
        ];
 
        /**
index 27963bd..defaf1b 100644 (file)
        "config-restart": "Jo, zrëszë znowa",
        "config-env-php": "PHP $1 je wjinastalowóné",
        "config-env-hhvm": "HHVM $1 je wjinastalowóné",
+       "config-memory-raised": "Paraméter PHP <code>memory_limit</code> $1 òstôł zwikszony do $2.",
+       "config-xcache": "[Http://trac.lighttpd.net/xcache/ XCache] je wjinstalowóny",
+       "config-apc": "[Http://www.php.net/apc APC] je wjinstalowóny",
+       "config-apcu": "[http://www.php.net/apcu APCu] je wjinstalowóny",
+       "config-wincache": "[http://www.iis.net/download/WinCacheForPhp WinCache] je wjinstalowóny",
+       "config-diff3-bad": "Felënk GNU diff3.",
+       "config-mysql-innodb": "InnoDB",
+       "config-mysql-myisam": "MyISAM",
+       "config-mysql-binary": "binarny",
+       "config-mysql-utf8": "UTF‐8",
+       "config-site-name": "Miono wiki:",
+       "config-site-name-blank": "Wpiszë miono starnów.",
+       "config-ns-other-default": "MòjôWiki",
        "config-admin-box": "Kònto sprôwnika",
        "config-admin-name": "Twòjé miono brëkòwnika:",
        "config-admin-password": "Parola:",
index 71b50de..61acd3e 100644 (file)
@@ -6,7 +6,8 @@
                        "SNN95",
                        "MaxSem",
                        "Aviator",
-                       "Macofe"
+                       "Macofe",
+                       "Jeluang Terluang"
                ]
        },
        "config-desc": "Pemasang MediaWiki",
@@ -56,7 +57,7 @@
        "config-outdated-sqlite": "<strong>Amaran:</strong> anda mempunyai SQLite $1 yang lebih rendah daripada versi keperluan minimum $1. SQLite tidak akan disediakan.",
        "config-no-fts3": "<strong>Amaran:</strong> SQLite disusun tanpa [//sqlite.org/fts3.html modil FTS3], maka ciri-ciri pencarian tidak akan disediakan pada backend ini.",
        "config-pcre-old": "<strong>Amaran keras:</strong> PCRE $1 ke atas diperlukan.\nBinari PHP anda berpaut dengan PCRE $2.\n[https://www.mediawiki.org/wiki/Manual:Errors_and_symptoms/PCRE Keterangan lanjut].",
-       "config-memory-bad": "<strong>Amaran:</strong> <code>memory_limit</code> (Had memori) PHP adalah $1.\nIni mungkin terlalu rendah.\nPemasangan mungkin akan gagal!",
+       "config-memory-bad": "<strong>Amaran:</strong> <code>memory_limit</code> (Had memori) PHP ialah $1.\nIni mungkin terlalu rendah.\nPemasangan mungkin akan gagal!",
        "config-xcache": "[http://xcache.lighttpd.net/ XCache] dipasang",
        "config-apc": "[http://www.php.net/apc APC] dipasang",
        "config-wincache": "[http://www.iis.net/download/WinCacheForPhp WinCache] dipasang",
@@ -97,7 +98,7 @@
        "config-mysql-engine": "Enjin storan:",
        "config-mysql-innodb": "InnoDB",
        "config-mysql-myisam": "MyISAM",
-       "config-mysql-only-myisam-dep": "<strong>Amaran:</strong> MyISAM adalah satu-satunya enjin storan yang terdapat untuk MySQL di mesin ini, dan penggunaannya dengan MediaWiki tidak digalakkan kerana:\n* ia tidak menyokong keserempakan (''concurrency'') disebabkan penguncian jadual\n* ia lebih terdedah kepada korupsi daripada enjin-enjin lain\n* pangkalan kod MediaWiki tidak sentiasa mengendalikan MyISAM seperti yang diharapkan\n\nPemasangan MySQL anda tidak menyokong InnoDB. Mungkin tiba masanya untuk naik taraf.",
+       "config-mysql-only-myisam-dep": "<strong>Amaran:</strong> MyISAM ialah satu-satunya enjin storan yang terdapat untuk MySQL di mesin ini, dan penggunaannya dengan MediaWiki tidak digalakkan kerana:\n* ia tidak menyokong keserempakan (''concurrency'') disebabkan penguncian jadual\n* ia lebih terdedah kepada korupsi daripada enjin-enjin lain\n* pangkalan kod MediaWiki tidak sentiasa mengendalikan MyISAM seperti yang diharapkan\n\nPemasangan MySQL anda tidak menyokong InnoDB. Mungkin tiba masanya untuk naik taraf.",
        "config-mysql-charset": "Peranggu aksara pangkalan data:",
        "config-mysql-binary": "Perduaan",
        "config-mysql-utf8": "UTF-8",
index 7fdd617..44721d9 100644 (file)
@@ -28,7 +28,7 @@
  * For example, one can set $wgJobTypeConf['refreshLinks'] to point to a
  * JobQueueFederated instance, which itself would consist of three JobQueueRedis
  * instances, each using their own redis server. This would allow for the jobs
- * to be split (evenly or based on weights) accross multiple servers if a single
+ * to be split (evenly or based on weights) across multiple servers if a single
  * server becomes impractical or expensive. Different JobQueue classes can be mixed.
  *
  * The basic queue configuration (e.g. "order", "claimTTL") of a federated queue
diff --git a/includes/jobqueue/JobQueueSecondTestQueue.php b/includes/jobqueue/JobQueueSecondTestQueue.php
new file mode 100644 (file)
index 0000000..a1935df
--- /dev/null
@@ -0,0 +1,280 @@
+<?php
+
+/**
+ * A wrapper for the JobQueue that delegates all the method calls to a single,
+ * main queue, and also pushes all the jobs to a second job queue that's being
+ * debugged.
+ *
+ * This class was temporary added to test transitioning to the JobQueueEventBus
+ * and will removed after the transition is completed. This code is only needed
+ * while we are testing the new infrastructure to be able to compare the results
+ * between the queue implementations and make sure that they process the same jobs,
+ * deduplicate correctly, compare the delays, backlogs and make sure no jobs are lost.
+ * When the new infrastructure is well tested this will not be needed any more.
+ *
+ * @deprecated since 1.30
+ * @since 1.30
+ */
+class JobQueueSecondTestQueue extends JobQueue {
+
+       /**
+        * @var JobQueue
+        */
+       private $mainQueue;
+
+       /**
+        * @var JobQueue
+        */
+       private $debugQueue;
+
+       protected function __construct( array $params ) {
+               if ( !isset( $params['mainqueue'] ) ) {
+                       throw new MWException( "mainqueue parameter must be provided to the debug queue" );
+               }
+
+               if ( !isset( $params['debugqueue'] ) ) {
+                       throw new MWException( "debugqueue parameter must be provided to the debug queue" );
+               }
+
+               $conf = [ 'wiki' => $params['wiki'], 'type' => $params['type'] ];
+               $this->mainQueue = JobQueue::factory( $params['mainqueue'] + $conf );
+               $this->debugQueue = JobQueue::factory( $params['debugqueue'] + $conf );
+
+               // We need to construct parent after creating the main and debug queue
+               // because super constructor calls some methods we delegate to the main queue.
+               parent::__construct( $params );
+       }
+
+       /**
+        * Get the allowed queue orders for configuration validation
+        *
+        * @return array Subset of (random, timestamp, fifo, undefined)
+        */
+       protected function supportedOrders() {
+               return $this->mainQueue->supportedOrders();
+       }
+
+       /**
+        * Get the default queue order to use if configuration does not specify one
+        *
+        * @return string One of (random, timestamp, fifo, undefined)
+        */
+       protected function optimalOrder() {
+               return $this->mainQueue->optimalOrder();
+       }
+
+       /**
+        * Find out if delayed jobs are supported for configuration validation
+        *
+        * @return bool Whether delayed jobs are supported
+        */
+       protected function supportsDelayedJobs() {
+               return $this->mainQueue->supportsDelayedJobs();
+       }
+
+       /**
+        * @see JobQueue::isEmpty()
+        * @return bool
+        */
+       protected function doIsEmpty() {
+               return $this->mainQueue->doIsEmpty();
+       }
+
+       /**
+        * @see JobQueue::getSize()
+        * @return int
+        */
+       protected function doGetSize() {
+               return $this->mainQueue->doGetSize();
+       }
+
+       /**
+        * @see JobQueue::getAcquiredCount()
+        * @return int
+        */
+       protected function doGetAcquiredCount() {
+               return $this->mainQueue->doGetAcquiredCount();
+       }
+
+       /**
+        * @see JobQueue::getDelayedCount()
+        * @return int
+        */
+       protected function doGetDelayedCount() {
+               return $this->mainQueue->doGetDelayedCount();
+       }
+
+       /**
+        * @see JobQueue::getAbandonedCount()
+        * @return int
+        */
+       protected function doGetAbandonedCount() {
+               return $this->mainQueue->doGetAbandonedCount();
+       }
+
+       /**
+        * @see JobQueue::batchPush()
+        * @param IJobSpecification[] $jobs
+        * @param int $flags
+        */
+       protected function doBatchPush( array $jobs, $flags ) {
+               $this->mainQueue->doBatchPush( $jobs, $flags );
+
+               try {
+                       $this->debugQueue->doBatchPush( $jobs, $flags );
+               } catch ( Exception $exception ) {
+                       MWExceptionHandler::logException( $exception );
+               }
+       }
+
+       /**
+        * @see JobQueue::pop()
+        * @return Job|bool
+        */
+       protected function doPop() {
+               return $this->mainQueue->doPop();
+       }
+
+       /**
+        * @see JobQueue::ack()
+        * @param Job $job
+        */
+       protected function doAck( Job $job ) {
+               return $this->mainQueue->doAck( $job );
+       }
+
+       /**
+        * @see JobQueue::deduplicateRootJob()
+        * @param IJobSpecification $job
+        * @throws MWException
+        * @return bool
+        */
+       protected function doDeduplicateRootJob( IJobSpecification $job ) {
+               return $this->mainQueue->doDeduplicateRootJob( $job );
+       }
+
+       /**
+        * @see JobQueue::isRootJobOldDuplicate()
+        * @param Job $job
+        * @return bool
+        */
+       protected function doIsRootJobOldDuplicate( Job $job ) {
+               return $this->mainQueue->doIsRootJobOldDuplicate( $job );
+       }
+
+       /**
+        * @param string $signature Hash identifier of the root job
+        * @return string
+        */
+       protected function getRootJobCacheKey( $signature ) {
+               return $this->mainQueue->getRootJobCacheKey( $signature );
+       }
+
+       /**
+        * @see JobQueue::delete()
+        * @throws MWException
+        */
+       protected function doDelete() {
+               return $this->mainQueue->doDelete();
+       }
+
+       /**
+        * @see JobQueue::waitForBackups()
+        * @return void
+        */
+       protected function doWaitForBackups() {
+               $this->mainQueue->doWaitForBackups();
+       }
+
+       /**
+        * @see JobQueue::flushCaches()
+        * @return void
+        */
+       protected function doFlushCaches() {
+               $this->mainQueue->doFlushCaches();
+       }
+
+       /**
+        * Get an iterator to traverse over all available jobs in this queue.
+        * This does not include jobs that are currently acquired or delayed.
+        * Note: results may be stale if the queue is concurrently modified.
+        *
+        * @return Iterator
+        * @throws JobQueueError
+        */
+       public function getAllQueuedJobs() {
+               return $this->mainQueue->getAllQueuedJobs();
+       }
+
+       /**
+        * Get an iterator to traverse over all delayed jobs in this queue.
+        * Note: results may be stale if the queue is concurrently modified.
+        *
+        * @return Iterator
+        * @throws JobQueueError
+        * @since 1.22
+        */
+       public function getAllDelayedJobs() {
+               return $this->mainQueue->getAllDelayedJobs();
+       }
+
+       /**
+        * Get an iterator to traverse over all claimed jobs in this queue
+        *
+        * Callers should be quick to iterator over it or few results
+        * will be returned due to jobs being acknowledged and deleted
+        *
+        * @return Iterator
+        * @throws JobQueueError
+        * @since 1.26
+        */
+       public function getAllAcquiredJobs() {
+               return $this->mainQueue->getAllAcquiredJobs();
+       }
+
+       /**
+        * Get an iterator to traverse over all abandoned jobs in this queue
+        *
+        * @return Iterator
+        * @throws JobQueueError
+        * @since 1.25
+        */
+       public function getAllAbandonedJobs() {
+               return $this->mainQueue->getAllAbandonedJobs();
+       }
+
+       /**
+        * Do not use this function outside of JobQueue/JobQueueGroup
+        *
+        * @return string
+        * @since 1.22
+        */
+       public function getCoalesceLocationInternal() {
+               return $this->mainQueue->getCoalesceLocationInternal();
+       }
+
+       /**
+        * @see JobQueue::getSiblingQueuesWithJobs()
+        * @param array $types List of queues types
+        * @return array|null (list of queue types) or null if unsupported
+        */
+       protected function doGetSiblingQueuesWithJobs( array $types ) {
+               return $this->mainQueue->doGetSiblingQueuesWithJobs( $types );
+       }
+
+       /**
+        * @see JobQueue::getSiblingQueuesSize()
+        * @param array $types List of queues types
+        * @return array|null (list of queue types) or null if unsupported
+        */
+       protected function doGetSiblingQueueSizes( array $types ) {
+               return $this->mainQueue->doGetSiblingQueueSizes( $types );
+       }
+
+       /**
+        * @throws JobQueueReadOnlyError
+        */
+       protected function assertNotReadOnly() {
+               $this->mainQueue->assertNotReadOnly();
+       }
+}
index 687c67c..d94578d 100644 (file)
@@ -181,6 +181,12 @@ class MultiWriteBagOStuff extends BagOStuff {
                $ret = true;
                $args = array_slice( func_get_args(), 3 );
 
+               if ( $count > 1 && $asyncWrites ) {
+                       // Deep-clone $args to prevent misbehavior when something writes an
+                       // object to the BagOStuff then modifies it afterwards, e.g. T168040.
+                       $args = unserialize( serialize( $args ) );
+               }
+
                foreach ( $this->caches as $i => $cache ) {
                        if ( $i >= $count ) {
                                break; // ignore the lower tiers
index 723a4a6..b8b44e6 100644 (file)
@@ -2664,10 +2664,13 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
        }
 
        final public function onTransactionPreCommitOrIdle( callable $callback, $fname = __METHOD__ ) {
-               if ( $this->mTrxLevel ) {
+               if ( $this->mTrxLevel || $this->getFlag( self::DBO_TRX ) ) {
+                       // As long as DBO_TRX is set, writes will accumulate until the load balancer issues
+                       // an implicit commit of all peer databases. This is true even if a transaction has
+                       // not yet been triggered by writes; make sure $callback runs *after* any such writes.
                        $this->mTrxPreCommitCallbacks[] = [ $callback, $fname ];
                } else {
-                       // If no transaction is active, then make one for this callback
+                       // No transaction is active nor will start implicitly, so make one for this callback
                        $this->startAtomic( __METHOD__ );
                        try {
                                call_user_func( $callback );
index a0bfb59..1ed18cd 100644 (file)
@@ -60,7 +60,7 @@ class BlockLogFormatter extends LogFormatter {
                        // is shown on the correct side of the tooltip text.
                        $durationTooltip = '&lrm;' . htmlspecialchars( $params[4] );
                        $params[4] = Message::rawParam(
-                               "<span class='blockExpiry' title='$durationTooltip'>" .
+                               "<span class=\"blockExpiry\" title=\"$durationTooltip\">" .
                                $this->context->getLanguage()->translateBlockExpiry(
                                        $params[4],
                                        $this->context->getUser(),
index 9168482..c05ba45 100644 (file)
@@ -50,7 +50,7 @@ class WikiPage implements Page, IDBAccessObject {
        public $mLatest = false;             // !< Integer (false means "not loaded")
        /**@}}*/
 
-       /** @var stdClass Map of cache fields (text, parser output, ect) for a proposed/new edit */
+       /** @var PreparedEdit Map of cache fields (text, parser output, ect) for a proposed/new edit */
        public $mPreparedEdit = false;
 
        /**
@@ -782,7 +782,7 @@ class WikiPage implements Page, IDBAccessObject {
         * Determine whether a page would be suitable for being counted as an
         * article in the site_stats table based on the title & its content
         *
-        * @param object|bool $editInfo (false): object returned by prepareTextForEdit(),
+        * @param PreparedEdit|bool $editInfo (false): object returned by prepareTextForEdit(),
         *   if false, the current database state will be used
         * @return bool
         */
@@ -1607,7 +1607,7 @@ class WikiPage implements Page, IDBAccessObject {
                $meta = [
                        'bot' => ( $flags & EDIT_FORCE_BOT ),
                        'minor' => ( $flags & EDIT_MINOR ) && $user->isAllowed( 'minoredit' ),
-                       'serialized' => $editInfo->pst,
+                       'serialized' => $pstContent->serialize( $serialFormat ),
                        'serialFormat' => $serialFormat,
                        'baseRevId' => $baseRevId,
                        'oldRevision' => $old_revision,
@@ -1961,7 +1961,9 @@ class WikiPage implements Page, IDBAccessObject {
 
        /**
         * Prepare content which is about to be saved.
-        * Returns a stdClass with source, pst and output members
+        *
+        * Prior to 1.30, this returned a stdClass object with the same class
+        * members.
         *
         * @param Content $content
         * @param Revision|int|null $revision Revision object. For backwards compatibility, a
index 5c048a2..0600642 100644 (file)
@@ -761,7 +761,7 @@ abstract class LoginSignupSpecialPage extends AuthManagerSpecialPage {
                if ( $this->showCreateAccountLink() ) {
                        # Pass any language selection on to the mode switch link
                        if ( $this->mLanguage ) {
-                               $linkq .= '&uselang=' . $this->mLanguage;
+                               $linkq .= '&uselang=' . urlencode( $this->mLanguage );
                        }
                        // Supply URL, login template creates the button.
                        $template->set( 'createOrLoginHref', $titleObj->getLocalURL( $linkq ) );
@@ -1149,7 +1149,7 @@ abstract class LoginSignupSpecialPage extends AuthManagerSpecialPage {
                                $linkq = $this->getReturnToQueryStringFragment();
                                // Pass any language selection on to the mode switch link
                                if ( $this->mLanguage ) {
-                                       $linkq .= '&uselang=' . $this->mLanguage;
+                                       $linkq .= '&uselang=' . urlencode( $this->mLanguage );
                                }
                                $loggedIn = $this->getUser()->isLoggedIn();
 
index c9c2475..f0c2bc4 100644 (file)
@@ -315,7 +315,7 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
                $opts = parent::getDefaultOptions();
                $user = $this->getUser();
 
-               $opts->add( 'days', $user->getIntOption( 'rcdays' ) );
+               $opts->add( 'days', $user->getIntOption( 'rcdays' ), FormOptions::FLOAT );
                $opts->add( 'limit', $user->getIntOption( 'rclimit' ) );
                $opts->add( 'from', '' );
 
@@ -359,7 +359,7 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
                        if ( preg_match( '/^limit=(\d+)$/', $bit, $m ) ) {
                                $opts['limit'] = $m[1];
                        }
-                       if ( preg_match( '/^days=(\d+)$/', $bit, $m ) ) {
+                       if ( preg_match( '/^days=(\d+(?:\.\d+)?)$/', $bit, $m ) ) {
                                $opts['days'] = $m[1];
                        }
                        if ( preg_match( '/^namespace=(.*)$/', $bit, $m ) ) {
@@ -373,6 +373,7 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
 
        public function validateOptions( FormOptions $opts ) {
                $opts->validateIntBounds( 'limit', 0, 5000 );
+               $opts->validateBounds( 'days', 0, $this->getConfig()->get( 'RCMaxAge' ) / ( 3600 * 24 ) );
                parent::validateOptions( $opts );
        }
 
@@ -387,8 +388,7 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
                        $query_options, $join_conds, $opts );
 
                // Calculate cutoff
-               $cutoff_unixtime = time() - ( $opts['days'] * 86400 );
-               $cutoff_unixtime = $cutoff_unixtime - ( $cutoff_unixtime % 86400 );
+               $cutoff_unixtime = time() - $opts['days'] * 3600 * 24;
                $cutoff = $dbr->timestamp( $cutoff_unixtime );
 
                $fromValid = preg_match( '/^[0-9]{14}$/', $opts['from'] );
@@ -430,13 +430,14 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
                $fields[] = 'page_latest';
                $join_conds['page'] = [ 'LEFT JOIN', 'rc_cur_id=page_id' ];
 
+               $tagFilter = $opts['tagfilter'] ? explode( '|', $opts['tagfilter'] ) : [];
                ChangeTags::modifyDisplayQuery(
                        $tables,
                        $fields,
                        $conds,
                        $join_conds,
                        $query_options,
-                       $opts['tagfilter']
+                       $tagFilter
                );
 
                if ( !$this->runMainQueryHook( $tables, $fields, $conds, $query_options, $join_conds,
@@ -449,13 +450,24 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
                        return false;
                }
 
+               $orderByAndLimit = [
+                       'ORDER BY' => 'rc_timestamp DESC',
+                       'LIMIT' => $opts['limit']
+               ];
+               if ( in_array( 'DISTINCT', $query_options ) ) {
+                       // ChangeTags::modifyDisplayQuery() adds DISTINCT when filtering on multiple tags.
+                       // In order to prevent DISTINCT from causing query performance problems,
+                       // we have to GROUP BY the primary key. This in turn requires us to add
+                       // the primary key to the end of the ORDER BY, and the old ORDER BY to the
+                       // start of the GROUP BY
+                       $orderByAndLimit['ORDER BY'] = 'rc_timestamp DESC, rc_id DESC';
+                       $orderByAndLimit['GROUP BY'] = 'rc_timestamp, rc_id';
+               }
                // array_merge() is used intentionally here so that hooks can, should
                // they so desire, override the ORDER BY / LIMIT condition(s); prior to
                // MediaWiki 1.26 this used to use the plus operator instead, which meant
                // that extensions weren't able to change these conditions
-               $query_options = array_merge( [
-                       'ORDER BY' => 'rc_timestamp DESC',
-                       'LIMIT' => $opts['limit'] ], $query_options );
+               $query_options = array_merge( $orderByAndLimit, $query_options );
                $rows = $dbr->select(
                        $tables,
                        $fields,
index b3b9210..fee336e 100644 (file)
@@ -103,15 +103,33 @@ class SpecialRecentChangesLinked extends SpecialRecentChanges {
                        $join_conds['page'] = [ 'LEFT JOIN', 'rc_cur_id=page_id' ];
                        $select[] = 'page_latest';
                }
+
+               $tagFilter = $opts['tagfilter'] ? explode( '|', $opts['tagfilter'] ) : [];
                ChangeTags::modifyDisplayQuery(
                        $tables,
                        $select,
                        $conds,
                        $join_conds,
                        $query_options,
-                       $opts['tagfilter']
+                       $tagFilter
                );
 
+               if ( $dbr->unionSupportsOrderAndLimit() ) {
+                       if ( count( $tagFilter ) > 1 ) {
+                               // ChangeTags::modifyDisplayQuery() will have added DISTINCT.
+                               // To prevent this from causing query performance problems, we need to add
+                               // a GROUP BY, and add rc_id to the ORDER BY.
+                               $order = [
+                                       'GROUP BY' => 'rc_timestamp, rc_id',
+                                       'ORDER BY' => 'rc_timestamp DESC, rc_id DESC'
+                               ];
+                       } else {
+                               $order = [ 'ORDER BY' => 'rc_timestamp DESC' ];
+                       }
+               } else {
+                       $order = [];
+               }
+
                if ( !$this->runMainQueryHook( $tables, $select, $conds, $query_options, $join_conds,
                        $opts )
                ) {
@@ -181,12 +199,6 @@ class SpecialRecentChangesLinked extends SpecialRecentChanges {
                                }
                        }
 
-                       if ( $dbr->unionSupportsOrderAndLimit() ) {
-                               $order = [ 'ORDER BY' => 'rc_timestamp DESC' ];
-                       } else {
-                               $order = [];
-                       }
-
                        $query = $dbr->selectSQLText(
                                array_merge( $tables, [ $link_table ] ),
                                $select,
index 810f8fb..e7478ee 100644 (file)
@@ -669,13 +669,7 @@ class SpecialUndelete extends SpecialPage {
 
                $archive = new PageArchive( $this->mTargetObj, $this->getConfig() );
                Hooks::run( 'UndeleteForm::showHistory', [ &$archive, $this->mTargetObj ] );
-               /*
-               $text = $archive->getLastRevisionText();
-               if( is_null( $text ) ) {
-                       $out->addWikiMsg( 'nohistory' );
-                       return;
-               }
-               */
+
                $out->addHTML( '<div class="mw-undelete-history">' );
                if ( $this->mAllowed ) {
                        $out->addWikiMsg( 'undeletehistory' );
@@ -858,11 +852,12 @@ class SpecialUndelete extends SpecialPage {
                        $misc = Html::hidden( 'target', $this->mTarget );
                        $misc .= Html::hidden( 'wpEditToken', $this->getUser()->getEditToken() );
                        $history .= $misc;
-               }
-
-               $form->appendContent( new OOUI\HtmlSnippet( $history ) );
 
-               $out->addHTML( $form );
+                       $form->appendContent( new OOUI\HtmlSnippet( $history ) );
+                       $out->addHTML( $form );
+               } else {
+                       $out->addHTML( $history );
+               }
 
                return true;
        }
index 65131ec..549362f 100644 (file)
@@ -34,6 +34,8 @@ use Wikimedia\Rdbms\IDatabase;
 class SpecialWatchlist extends ChangesListSpecialPage {
        public function __construct( $page = 'Watchlist', $restriction = 'viewmywatchlist' ) {
                parent::__construct( $page, $restriction );
+
+               $this->maxDays = $this->getConfig()->get( 'RCMaxAge' ) / ( 3600 * 24 );
        }
 
        public function doesWrites() {
@@ -173,6 +175,11 @@ class SpecialWatchlist extends ChangesListSpecialPage {
                return $opts;
        }
 
+       public function validateOptions( FormOptions $opts ) {
+               $opts->validateBounds( 'days', 0, $this->maxDays );
+               parent::validateOptions( $opts );
+       }
+
        /**
         * Get all custom filters
         *
@@ -255,7 +262,7 @@ class SpecialWatchlist extends ChangesListSpecialPage {
                // Calculate cutoff
                if ( $opts['days'] > 0 ) {
                        $conds[] = 'rc_timestamp > ' .
-                               $dbr->addQuotes( $dbr->timestamp( time() - intval( $opts['days'] * 86400 ) ) );
+                               $dbr->addQuotes( $dbr->timestamp( time() - $opts['days'] * 3600 * 24 ) );
                }
        }
 
@@ -499,7 +506,7 @@ class SpecialWatchlist extends ChangesListSpecialPage {
                if ( $opts['days'] > 0 ) {
                        $days = $opts['days'];
                } else {
-                       $days = $this->getConfig()->get( 'RCMaxAge' ) / ( 3600 * 24 );
+                       $days = $this->maxDays;
                }
                $timestamp = wfTimestampNow();
                $wlInfo = $this->msg( 'wlnote' )->numParams( $numRows, round( $days * 24 ) )->params(
@@ -599,7 +606,7 @@ class SpecialWatchlist extends ChangesListSpecialPage {
                        $days[] = $userWatchlistOption;
                }
 
-               $maxDays = (string)( $this->getConfig()->get( 'RCMaxAge' ) / ( 3600 * 24 ) );
+               $maxDays = (string)$this->maxDays;
                // add the maximum possible value, if it isn't available already
                if ( !in_array( $maxDays, $days ) ) {
                        $days[] = $maxDays;
index 00d91ce..19ff2a4 100644 (file)
@@ -383,6 +383,8 @@ class Names {
                'si' => 'සිංහල', # Sinhalese
                'simple' => 'Simple English', # Simple English
                'sk' => 'slovenčina', # Slovak
+               'skr' => 'سرائیکی', # Saraiki (multiple scripts - defaults to Arabic)
+               'skr-arab' => 'سرائیکی', # Saraiki (Arabic script)
                'sl' => 'slovenščina', # Slovenian
                'sli' => 'Schläsch', # Lower Selisian
                'sm' => 'Gagana Samoa', # Samoan
index 37fadac..c098ef2 100644 (file)
        "fileduplicatesearch-noresults": "Nun s'alcontró dengún ficheru nomáu «$1».",
        "specialpages": "Páxines especiales",
        "specialpages-note-top": "Lleenda",
-       "specialpages-note": "* Páxines especiales normales.\n* <span class=\"mw-specialpagerestricted\">Páxines especiales restrinxíes.</span>",
        "specialpages-group-maintenance": "Informes de mantenimientu",
        "specialpages-group-other": "Otres páxines especiales",
        "specialpages-group-login": "Entrar / crear cuenta",
index d59b3d3..246ddaa 100644 (file)
        "recentchangeslinked-feed": "Бәйле үҙгәртеүҙәр",
        "recentchangeslinked-toolbox": "Бәйле үҙгәртеүҙәр",
        "recentchangeslinked-title": "\"$1\" битенә бәйле үҙгәртеүҙәр",
-       "recentchangeslinked-summary": "Был күрһәтелгән бит һылтанма яһаған (йәки күрһәтелгән категорияға кергән) һуңғы үҙгәртеүҙәр исемлеге.\n[[Special:Watchlist|Күҙәтеү исемлегегеҙгә]] керә торған биттәр '''ҡалын''' итеп күрһәтелгән.",
+       "recentchangeslinked-summary": "Был күрһәтелгән бит һылтанма яһаған (йәки күрһәтелгән категорияға кергән) һуңғы үҙгәртеүҙәр исемлеге.\n[[Special:Watchlist|Күҙәтеү исемлегегеҙгә]] керә торған биттәр '''ҡалын''' итеп күрһәтелгән.",
        "recentchangeslinked-page": "Бит исеме:",
        "recentchangeslinked-to": "Киреһенсә, был биткә һылтанма яһаған биттәрҙәге үҙгәртеүҙәрҙе күрһәтергә",
        "recentchanges-page-added-to-category": "[[:$1]] категорияға өҫтәлгән",
        "tooltip-invert": "Һайланған исемдәр арауығындағы (һәм бәйле исемдәр арауығындағы, әгәр күрһәтелһә) биттәрҙәге үҙгәртеүҙәрҙе йәшерер өсөн был билдәне ҡуйығыҙ.",
        "tooltip-whatlinkshere-invert": "Был тамғаны һайланған исемдәр арауығындағы һылтанмаларҙы йәшереү өсөн ҡуйығыҙ.",
        "namespace_association": "Бәйле арауыҡ",
-       "tooltip-namespace_association": "Һайланған исемдәр арауығы менән бәйле әңгәмә(йәки тема) исем арауыҡтарын ҡушыр өсөн был билдәне ҡуйығыҙ.",
+       "tooltip-namespace_association": "Һайланған исемдәр арауығы менән бәйле әңгәмә (йәки тема) исем арауыҡтарын ҡушыр өсөн был билдәне ҡуйығыҙ.",
        "blanknamespace": "(Төп)",
        "contributions": "{{GENDER:$1|Ҡатнашыусы}} башҡарған эш",
        "contributions-title": "$1 исемле ҡатнашыусы башҡарған эш",
        "fileduplicatesearch-noresults": "\"$1\" исемле файл табылманы",
        "specialpages": "Махсус биттәр",
        "specialpages-note-top": "Легенда",
-       "specialpages-note": "* Ябай махсус биттәр.\n* <span class=\"mw-specialpagerestricted\">Сикле махсус биттәр.</span>\n* <span class=\"mw-specialpagecached\">Кешланған махсус биттәр (иҫкергән булыуы мөмкин).</span>",
        "specialpages-group-maintenance": "Техник хеҙмәтләндереү хисапламалары",
        "specialpages-group-other": "Башҡа махсус биттәр",
        "specialpages-group-login": "Танылыу йәки теркәлеү",
        "logentry-patrol-patrol": "$1 $3 битенең $4 версияһын {{GENDER:$2|тикшерҙе}}.",
        "logentry-patrol-patrol-auto": "$1 $3 битенең $4 версияһын автоматик рәүештә {{GENDER:$2|тикшерҙе}}.",
        "logentry-newusers-newusers": " {{GENDER:$2|ҡатнашыусы}} $1 иҫәп яҙмаһы булдырҙы",
-       "logentry-newusers-create": "{{GENDER:$2|ҡатнашыусы}} $1 иҫәп яҙмаһы булдырҙы.",
+       "logentry-newusers-create": "{{GENDER:$2|ҡатнашыусы}} $1 иҫәп яҙмаһы булдырҙы",
        "logentry-newusers-create2": "$1 {{GENDER:$2|ҡатнашыусы}} $3 иҫәп яҙмаһын булдырҙы",
        "logentry-newusers-byemail": "$1 {{GENDER:$2|}} $3 иҫәп яҙмаһын булдырҙы һәм серһүҙ электрон почта аша ебәрелде",
        "logentry-newusers-autocreate": "Автоматик рәүештә {{GENDER:$2| ҡатнашыусының}} $1 иҫәп яҙмаһы яһалды",
index 7587f79..7ecb3bf 100644 (file)
        "rcfilters-days-title": "Апошнія дні",
        "rcfilters-hours-title": "Апошнія гадзіны",
        "rcfilters-days-show-days": "$1 {{PLURAL:$1|дзень|дні|дзён}}",
+       "rcfilters-days-show-hours": "$1 {{PLURAL:$1|гадзіна|гадзіны|гадзінаў}}",
        "rcfilters-quickfilters": "Захаваныя фільтры",
        "rcfilters-quickfilters-placeholder-title": "Спасылкі яшчэ не захаваныя",
        "rcfilters-quickfilters-placeholder-description": "Каб захаваць налады вашага фільтру і выкарыстаць іх пазьней, націсьніце на выяву закладкі ў зоне актыўнага фільтру ніжэй.",
        "rcfilters-filter-user-experience-level-unregistered-label": "Незарэгістраваныя",
        "rcfilters-filter-user-experience-level-unregistered-description": "Рэдактары, якія не ўвайшлі ў сыстэму",
        "rcfilters-filter-user-experience-level-newcomer-label": "Навічкі",
-       "rcfilters-filter-user-experience-level-newcomer-description": "Ð\9cенÑ\88 Ð·Ð° 10 Ð¿Ñ\80авак Ñ\96 4 Ð´Ð½і актыўнасьці.",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Ð\97аÑ\80Ñ\8dгÑ\96Ñ\81Ñ\82Ñ\80аванÑ\8bÑ\8f Ñ\80Ñ\8dдакÑ\82аÑ\80Ñ\8b Ð·Ñ\8c Ð¼ÐµÐ½Ñ\88 Ñ\87Ñ\8bм 10 Ð¿Ñ\80аÑ\9eкамÑ\96 Ñ\96 4 Ð´Ð½Ñ\8fмі актыўнасьці.",
        "rcfilters-filter-user-experience-level-learner-label": "Вучні",
-       "rcfilters-filter-user-experience-level-learner-description": "Ð\91олÑ\8cÑ\88 Ð´Ð¾Ñ\81Ñ\8cведÑ\83, чым у «навічкоў», але меней чым у «дасьведчаных удзельнікаў».",
+       "rcfilters-filter-user-experience-level-learner-description": "Ð\97аÑ\80Ñ\8dгÑ\96Ñ\81Ñ\82Ñ\80аванÑ\8bÑ\8f Ñ\80Ñ\8dдакÑ\82аÑ\80Ñ\8b, Ñ\87Ñ\8bй Ð´Ð¾Ñ\81Ñ\8cвед Ð±Ð¾Ð»Ñ\8cÑ\88 чым у «навічкоў», але меней чым у «дасьведчаных удзельнікаў».",
        "rcfilters-filter-user-experience-level-experienced-label": "Дасьведчаныя ўдзельнікі",
-       "rcfilters-filter-user-experience-level-experienced-description": "Ð\91олÑ\8cÑ\88 Ð·Ð° 30 Ð´Ð·Ñ\91н Ð°ÐºÑ\82Ñ\8bÑ\9eнаÑ\81Ñ\8cÑ\86Ñ\96 Ñ\96 500 Ð¿Ñ\80авак.",
+       "rcfilters-filter-user-experience-level-experienced-description": "Ð\97аÑ\80Ñ\8dгÑ\96Ñ\81Ñ\82Ñ\80аванÑ\8bÑ\8f Ñ\9eдзелÑ\8cнÑ\96кÑ\96 Ð· Ð±Ð¾Ð»Ñ\8cÑ\88 Ñ\87Ñ\8bм 500 Ð¿Ñ\80аÑ\9eкамÑ\96 Ñ\96 30 Ð´Ð½Ñ\8fмÑ\96 Ð°ÐºÑ\82Ñ\8bÑ\9eнаÑ\81Ñ\8cÑ\86Ñ\96.",
        "rcfilters-filtergroup-automated": "Аўтаматычны ўнёсак",
        "rcfilters-filter-bots-label": "Робат",
        "rcfilters-filter-bots-description": "Праўкі, зробленыя з дапамогай аўтаматызаваных інструмэнтаў.",
        "rcfilters-hideminor-conflicts-typeofchange-global": "Фільтар «Дробныя праўкі» канфліктуе з адным ці некалькімі фільтрамі «Тыпаў зьменаў», бо некаторыя тыпы зьменаў ня могуць быць вызначаныя як «дробныя». Канфліктныя фільтры пазначаныя ў разьдзеле актыўных фільтраў вышэй.",
        "rcfilters-hideminor-conflicts-typeofchange": "Некаторыя тыпы зьменаў ня могуць быць вызначаныя як «дробныя», таму гэты фільтар канфліктуе з наступнымі фільтрамі «Тыпаў зьменаў»: $1",
        "rcfilters-typeofchange-conflicts-hideminor": "Гэты фільтар тыпаў зьменаў канфліктуе зь фільтрам «Дробныя праўкі». Некаторыя тыпы зьменаў ня могуць быць вызначаныя як «дробныя».",
-       "rcfilters-filtergroup-lastRevision": "ЦÑ\8fпеÑ\80аÑ\88нÑ\8fÑ\8f Ð²Ñ\8dÑ\80Ñ\81Ñ\96Ñ\8f",
+       "rcfilters-filtergroup-lastRevision": "ЦÑ\8fпеÑ\80аÑ\88нÑ\96Ñ\8f Ð²Ñ\8dÑ\80Ñ\81Ñ\96Ñ\96",
        "rcfilters-filter-lastrevision-label": "Апошняя вэрсія",
        "rcfilters-filter-lastrevision-description": "Апошняя зьмена на старонцы.",
        "rcfilters-filter-previousrevision-label": "Ранейшыя вэрсіі",
        "fileduplicatesearch-noresults": "Файл з назвай «$1» ня знойдзены.",
        "specialpages": "Спэцыяльныя старонкі",
        "specialpages-note-top": "Легенда",
-       "specialpages-note": "* Звычайныя спэцыяльныя старонкі.\n* <strong class=\"mw-specialpagerestricted\">Спэцыяльныя старонкі з абмежаваным доступам.</strong>",
        "specialpages-group-maintenance": "Тэхнічныя справаздачы",
        "specialpages-group-other": "Іншыя спэцыяльныя старонкі",
        "specialpages-group-login": "Уваход / стварэньне рахунку",
index 238c450..9a6dd50 100644 (file)
        "fileduplicatesearch-noresults": "Не знойдзены файл з іменем «$1».",
        "specialpages": "Адмысловыя старонкі",
        "specialpages-note-top": "Легенда",
-       "specialpages-note": "* Звычайныя адмысловыя старонкі.\n* <span class=\"mw-specialpagerestricted\">Адмысловыя старонкі з абмежаваным доступам.</span>\n* <span class=\"mw-specialpagecached\">Закэшаваныя адмысловыя старонкі (могуць быць састарэлымі).</span>",
        "specialpages-group-maintenance": "Звесткі аб працы",
        "specialpages-group-other": "Іншыя адмысловыя старонкі",
        "specialpages-group-login": "Прадстаўленне / рэгістрацыя",
index e1efe59..a919e62 100644 (file)
        "recentchanges-label-plusminus": "Размерът на страницата е променен с този брой байтове",
        "recentchanges-legend-heading": "<strong>Легенда:</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (вижте също [[Special:NewPages|списъка с нови страници]])",
-       "recentchanges-submit": "Покажи",
+       "recentchanges-submit": "Показване",
+       "rcfilters-legend-heading": "<strong>Списък на съкращенията:</strong>",
        "rcfilters-activefilters": "Активни филтри",
        "rcfilters-quickfilters": "Запазени филтри",
        "rcfilters-quickfilters-placeholder-title": "Няма запазени препратки",
        "recentchanges-page-added-to-category-bundled": "[[:$1]] е добавена към категория, [[Special:WhatLinksHere/$1|към страницата сочат други страници]]",
        "recentchanges-page-removed-from-category": "[[:$1]] е премахната от категория",
        "upload": "Качи файл",
-       "uploadbtn": "Качване",
-       "reuploaddesc": "Връщане към формуляра за качване.",
+       "uploadbtn": "Качване на файл",
+       "reuploaddesc": "Връщане към формуляра за качване",
        "upload-tryagain": "Съхраняване на промененото описание на файла",
        "uploadnologin": "Не сте влезли",
        "uploadnologintext": "За да могат да бъдат качвани файлове е необходимо $1 в системата.",
        "upload_directory_missing": "Директорията за качване ($1) липсва и не може да бъде създадена на сървъра.",
-       "upload_directory_read_only": "СÑ\8aÑ\80вÑ\8aÑ\80Ñ\8aÑ\82 Ð½Ñ\8fма Ð´Ð¾Ñ\81Ñ\82Ñ\8aп за писане в директорията за качване „$1“.",
+       "upload_directory_read_only": "СÑ\8aÑ\80вÑ\8aÑ\80Ñ\8aÑ\82 Ð½Ñ\8fма Ð¿Ñ\80ава за писане в директорията за качване „$1“.",
        "uploaderror": "Грешка при качване",
        "upload-recreate-warning": "<strong>Внимание: Файл с това име вече е бил изтрит или преместен.</strong>\n\nЗа повече информация можете да прегледате записите от дневниците на изтриванията и преместванията:",
        "uploadtext": "Формулярът по-долу служи за качване на файлове, които ще могат да се използват в страниците.\nЗа преглеждане и търсене на вече качените файлове, може да се използва [[Special:FileList|списъка с качени файлове]]. Качванията се записват в [[Special:Log/upload|дневника на качванията]], а изтриванията &mdash; в [[Special:Log/delete|дневник на изтриванията]].\n\nЗа включване на файл в страница, може да се използва една от следния синтаксис: \n* <strong><code><nowiki>[[</nowiki>{{ns:file}}<nowiki>:File.jpg]]</nowiki></code></strong> за използване пълната версия на файла\n* <strong><code><nowiki>[[</nowiki>{{ns:file}}<nowiki>:File.png|200px|thumb|left|alt text]]</nowiki></code></strong> за определяне на широчина от 200 пиксела, ляво позициониране и „alt text“ за описание\n* <strong><code><nowiki>[[</nowiki>{{ns:media}}<nowiki>:File.ogg]]</nowiki></code></strong> за директна препратка, без файлът да бъде показван",
        "upload-preferred": "{{PLURAL:$2|Предпочитан файлов формат|Предпочитани файлови формати}}: $1.",
        "upload-prohibited": "{{PLURAL:$2|Непозволен файлов формат|Непозволени файлови формати}}: $1.",
        "uploadlogpage": "Дневник на качванията",
-       "uploadlogpagetext": "Списък на последните качвания.",
+       "uploadlogpagetext": "Списък на последните качвания.\nВижте [[Special:NewFiles|галерията на новите файлове]] за визуален преглед.",
        "filename": "Име на файл",
        "filedesc": "Описание",
        "fileuploadsummary": "Описание:",
        "filereuploadsummary": "Промени по файла:",
        "filestatus": "Авторско право:",
        "filesource": "Изходен код:",
-       "ignorewarning": "Съхраняване на файла въпреки предупреждението.",
+       "ignorewarning": "Съхраняване на файла въпреки предупреждението",
        "ignorewarnings": "Пренебрегване на всякакви предупреждения",
        "minlength1": "Имената на файловете трябва да съдържат поне един знак.",
        "illegalfilename": "Името на файла „$1“ съдържа знаци, които не са позволени в заглавия на страници. Преименувайте файла и се опитайте да го качите отново.",
        "fileduplicatesearch-noresults": "Не беше открит файл с име „$1“.",
        "specialpages": "Специални страници",
        "specialpages-note-top": "Легенда",
-       "specialpages-note": "* Обикновени специални страници.\n* <strong class=\"mw-specialpagerestricted\">Специални страници с ограничения.</strong>",
        "specialpages-group-maintenance": "Доклади по поддръжката",
        "specialpages-group-other": "Други специални страници",
        "specialpages-group-login": "Влизане / създаване на сметка",
index 15d7df2..074c99f 100644 (file)
        "rollbacklinkcount": "रोलबैक $1 {{PLURAL:$1|संपादन|संपादन सब}}",
        "protectlogpage": "सुरक्षा लॉग",
        "protectlogtext": "नीचे पन्ना सुरक्षा में भइल बदलावकुल के सूची बा।\nहाल में सुरक्षित पन्नन के सूची खातिर [[Special:ProtectedPages|सुरक्षित पन्नन के सूची]] देखीं।",
+       "protectedarticle": "\"[[$1]]\" सुरक्षित कइल गइल",
        "restriction-move": "स्थानांतरण",
        "restriction-create": "बनावे पर",
        "restriction-upload": "अपलोड",
        "contributions-title": " $1 खातिर प्रयोगकर्ता योगदान",
        "mycontris": "योगदान",
        "anoncontribs": "योगदान",
+       "contribsub2": "{{GENDER:$3|$1}} ($2) खातिर",
        "nocontribs": "ई मानदंड से मिलत जुलत कौनो बदलाव ना मिलल।",
        "uctop": "(वर्तमान)",
        "month": "महीना से (आ ओ से पहिले):",
        "sp-contributions-newbies-title": "नया खाता खातिर प्रयोगकर्ता के योगदान।",
        "sp-contributions-blocklog": "ब्लॉक लॉग",
        "sp-contributions-deleted": "नष्ट प्रयोगकर्ता के योगदान।",
+       "sp-contributions-uploads": "अपलोड",
        "sp-contributions-logs": "लॉग",
        "sp-contributions-talk": "बातचीत",
        "sp-contributions-userrights": "प्रयोगकर्ता अधिकार प्रबन्धन",
        "sp-contributions-blocked-notice": "ई प्रयोगकर्ता के ई समय निष्क्रीय करल गईल बा।\nनविनतम नष्ट लौग प्रविष्टी उद्धरण खातिर निचे दिहल बा:",
+       "sp-contributions-search": "योगदान खातिर खोज करीं",
+       "sp-contributions-username": "आइपी पता भा प्रयोगकर्तानाँव:",
+       "sp-contributions-newonly": "खाली उहे संपादन देखीं जेकरा से नया पन्ना बनल होखे",
+       "sp-contributions-submit": "खोजीं",
        "whatlinkshere": "इहाँ का जुड़ल बा",
        "whatlinkshere-title": "पन्ना जेवन \"$1\" से जुड़ल बा",
        "whatlinkshere-page": "पन्ना:",
        "tooltip-ca-delete": "ई पन्ना मिटाईं",
        "tooltip-ca-move": "एह पन्ना के स्थानांतरण करीं",
        "tooltip-ca-watch": "ए पन्ना के अपनी धियानसूची में जोड़ीं",
+       "tooltip-ca-unwatch": "ई पन्ना अपना धियानसूची से हटाईं",
        "tooltip-search": "{{SITENAME}} में खोजीं",
        "tooltip-search-go": "अगर ठीक एही नाँव के पन्ना मौजूद होखे तब ओहपर जाईं",
        "tooltip-search-fulltext": "अइसन पन्ना खोजीं जिनहन में ई पाठ (शब्द भा वाक्य) बाटे",
        "tooltip-undo": "\"वापस लीं\" ए संपादन के पलट देला आ संपादन फार्म के झलक देखावे वाला मोड में खोलेला। ई छोट सारांश में कारण जोड़े के मोका देला।",
        "tooltip-summary": "संछेप में एगो सारांश लिखीं",
        "simpleantispam-label": "स्पैम-बिरोधी रोक (Anti-spam check)\nएके <strong>मत</strong> भरीं!",
+       "pageinfo-length": "पन्ना लंबाई (बाइट में)",
        "pageinfo-toolboxlink": "पन्ना से जुड़ल जानकारी",
        "previousdiff": "← पुरान संपादन",
        "nextdiff": "नया संपादन →",
index 2af08f2..6015fa9 100644 (file)
        "rcfilters-filter-editsbyself-description": "আপনার নিজস্ব অবদান।",
        "rcfilters-filter-editsbyother-label": "অন্যদের দ্বারা পরিবর্তিত",
        "rcfilters-filter-editsbyother-description": "আপনার নিজস্বগুলি ছাড়া সকল পরিবর্তন।",
-       "rcfilters-filtergroup-userExpLevel": "নিবন্ধন ও অভিজ্ঞতা",
+       "rcfilters-filtergroup-userExpLevel": "বà§\8dযবহারà¦\95ারà§\80 à¦¨à¦¿à¦¬à¦¨à§\8dধন à¦\93 à¦\85ভিà¦\9cà§\8dà¦\9eতা",
        "rcfilters-filter-user-experience-level-registered-label": "নিবন্ধিত",
        "rcfilters-filter-user-experience-level-registered-description": "প্রবেশকৃত সম্পাদকবৃন্দ।",
        "rcfilters-filter-user-experience-level-unregistered-label": "অনিবন্ধিত",
        "rcfilters-typeofchange-conflicts-hideminor": "এই \"পরিবর্তনের ধরন\"-সংক্রান্ত ছাঁকনিটির সাথে \"অনুল্লেখ্য সম্পাদনা\" ছাঁকনিটির সংঘর্ষ আছে। কিছু নির্দিষ্ট ধরনের সম্পাদনা \"অনুল্লেখ্য\" হিসেবে চিহ্নিত করা সম্ভব নয়।",
        "rcfilters-filtergroup-lastRevision": "সর্বশেষ সংস্করণ",
        "rcfilters-filter-lastrevision-label": "সর্বশেষ সংশোধন",
-       "rcfilters-filter-lastrevision-description": "একটি পাতার সর্বশেষ সাম্প্রতিক পরিবর্তন।",
-       "rcfilters-filter-previousrevision-label": "পà§\82রà§\8dববরà§\8dতà§\80 à¦¸à¦\82শà§\8bধন",
-       "rcfilters-filter-previousrevision-description": "সব পরিবর্তন যা একটি পাতার সর্বশেষ সাম্প্রতিক পরিবর্তন নয়।",
+       "rcfilters-filter-lastrevision-description": "শà§\81ধà§\81মাতà§\8dর à¦\8fà¦\95à¦\9fি à¦ªà¦¾à¦¤à¦¾à¦° à¦¸à¦°à§\8dবশà§\87ষ à¦¸à¦¾à¦®à§\8dপà§\8dরতিà¦\95 à¦ªà¦°à¦¿à¦¬à¦°à§\8dতন।",
+       "rcfilters-filter-previousrevision-label": "সরà§\8dবশà§\87ষ à¦¸à¦\82শà§\8bধন à¦¨à¦¯à¦¼",
+       "rcfilters-filter-previousrevision-description": "সব পরিবর্তন যা \"সর্বশেষ সংশোধন\" নয়।",
        "rcfilters-filter-excluded": "বর্জিত",
+       "rcfilters-tag-prefix-namespace-inverted": "$1 <strong>:নয়</strong>",
        "rcfilters-view-tags": "ট্যাগকৃত সম্পাদনা",
+       "rcfilters-view-tags-tooltip": "সম্পাদনা ট্যাগ ব্যবহার করে ফলাফল ছাঁকুন",
        "rcfilters-view-return-to-default-tooltip": "মূল ছাঁকনির মেনুতে ফিরুন",
        "rcfilters-liveupdates-button": "সরাসরি হালনাগাদ",
        "rcnotefrom": "<strong>$2</strong>টা থেকে সংঘটিত পরিবর্তনগুলি (সর্বোচ্চ <strong>$1টি</strong> দেখানো হয়েছে)।",
        "delete-warning-toobig": "এই পাতাটির একটি বৃহৎ সম্পাদনা ইতিহাস রয়েছে, যা $1 {{PLURAL:$1|সংস্করণেরও|সংস্করণেরও}} বেশি।\nএই পাতাটি মুছে ফেললে তা {{SITENAME}} সাইটের ডেটাবেজ সমস্যার কারণ হতে পারে;\nসাবধানতার সাথে এগিয়ে যান।",
        "deleteprotected": "আপনি এই পাতাটি মুছে ফেলতে পারবেন না কারণ এটি সুরক্ষিত করা হয়েছে।",
        "deleting-backlinks-warning": "<strong>সতর্কীকরণ:</strong> আপনি যেটি মুছে ফেলতে যাচ্ছেন তা [[Special:WhatLinksHere/{{FULLPAGENAME}}|অন্যান্য পাতাসমূহে]] সংযুক্ত অথবা অন্তর্ভুক্ত রয়েছে।",
+       "deleting-subpages-warning": "<strong>সতর্কীকরণ:</strong> আপনি যে পাতাটি মুছে ফেলতে যাচ্ছেন তাঁর [[Special:PrefixIndex/{{FULLPAGENAME}}/|{{PLURAL:$1|একটি উপপাতা|$1টি উপপাতা|51=৫০টির বেশী}}]] রয়েছে।",
        "rollback": "সম্পাদনা ফিরিয়ে নিন",
        "rollbacklink": "পুনর্বহাল",
        "rollbacklinkcount": "$1টি {{PLURAL:$1|সম্পাদনা}} রোলব্যাক করুন",
        "undeletepage": "মুছে ফেলা পাতাগুলি দেখুন ও ফিরিয়ে আনুন",
        "undeletepagetitle": "'''[[:$1|$1]] এর অপসারিত সংস্করণগুলোর সমন্বয়ে দেখানো হচ্ছে'''।",
        "viewdeletedpage": "মুছে ফেলা হয়েছে, এমন পাতাগুলো দেখুন",
-       "undeletepagetext": "নিচের {{PLURAL:$1|পাতাটি মুছে ফেলা হয়েছে কিন্তু এটি|$1 পাতাগুলি মুছে ফেলা হয়েছে কিন্তু এগুলি}} এখনও আর্কাইভে আছে ও পুনরুদ্ধার করা সম্ভব। আর্কাইভ পর্যায়ক্রমিকভাবে পরিষ্কার করা হতে পারে।",
+       "undeletepagetext": "নিচের {{PLURAL:$1|পাতাটি মুছে ফেলা হয়েছে কিন্তু এটি|$1টি পাতা মুছে ফেলা হয়েছে কিন্তু এগুলি}} এখনও আর্কাইভে আছে ও পুনরুদ্ধার করা সম্ভব। আর্কাইভ পর্যায়ক্রমিকভাবে পরিষ্কার করা হতে পারে।",
        "undelete-fieldset-title": "সংশোধন পুনরুদ্ধার",
        "undeleteextrahelp": "সম্পূর্ণ পাতাটি পুনরুদ্ধার করার জন্য সবগুলি টিকবাক্স অনির্বাচিত করুন এবং '''''{{int:undeletebtn}}''''' বোতামে ক্লিক করুন।\nনির্বাচিত পুনরুদ্ধারের জন্য যেসব সংশোধন পুনরুদ্ধার করতে চান, তার পাশের বাক্সে টিক দিন এবং '''''{{int:undeletebtn}}''''' বোতামে ক্লিক করুন।",
        "undeleterevisions": "$1{{PLURAL:$1|টি সংশোধন}} অপসারিত",
        "fileduplicatesearch-noresults": "\"$1\" নামের কোনো ফাইল খুঁজে পাওয়া যায়নি।",
        "specialpages": "বিশেষ পাতাসমূহ",
        "specialpages-note-top": "ব্যাখ্যা",
-       "specialpages-note": "* সাধারণ বিশেষ পাতাসমূহ।\n* <span class=\"mw-specialpagerestricted\">সীমাবদ্ধ বিশেষ পাতা।</span>",
+       "specialpages-note-restricted": "* সাধারণ বিশেষ পাতাসমূহ।\n* <span class=\"mw-specialpagerestricted\">সীমাবদ্ধ বিশেষ পাতাসমূহ।</span>",
        "specialpages-group-maintenance": "রক্ষণাবেক্ষণের কার্যবিবরণীসমূহ",
        "specialpages-group-other": "অন্যান্য বিশেষ পাতাসমূহ",
        "specialpages-group-login": "প্রবেশ/নতুন অ্যাকাউন্ট",
index ec68b16..7c89fbd 100644 (file)
        "gender-unknown": "Kad Vas spominje, softver će pokušati izbjegavati rod kad god je to moguće",
        "gender-male": "On uređuje wiki stranice",
        "gender-female": "Ona uređuje wiki stranice",
-       "prefs-help-gender": "Postavljanje ovih podešavanja nije obavezno.\nSoftver koristi ove vrijednosti za vaše naslovljanje i ispravke gramatičkog roda u porukama softvera. Ova će informacija biti javna.",
+       "prefs-help-gender": "Ova postavka nije obavezna.\nSoftver koristi datu vrijednost da bi Vam se obratio i spomenuo Vas drugima koristeći odgovarajući gramatički rod.\nPodatak će biti javan.",
        "email": "E-pošta",
        "prefs-help-realname": "Pravo ime nije obavezno.\nAko izaberete da date ime, biće korišteno za pripisivanje vašem radu.",
        "prefs-help-email": "Adresa e-pošte nije obavezna, ali je potrebna u slučaju ponovnog postavljanja šifre, ako je zaboravite.",
        "move-page": "Premjesti $1",
        "move-page-legend": "Premjesti stranicu",
        "movepagetext": "Korištenjem ovog formulara možete preimenovati stranicu, premještajući cijelu historiju na novo ime.\nČlanak pod starim imenom postat će stranica koja preusmjerava na članak pod novim imenom. \nMožete automatski izmijeniti preusmjerenje do izvornog naslova.\nAko se ne odlučite na to, provjerite [[Special:DoubleRedirects|dvostruka]] ili [[Special:BrokenRedirects|neispravna preusmjeravanja]].\nDužni ste provjeriti da svi linkovi i dalje nastave voditi na prave stranice.\n\nImajte na umu da članak <strong>neće</strong> biti premješten ako već postoji članak pod imenom na koje ga namjeravate preusmjeriti osim u slučaju stranice za preusmjeravanje koja nema nikakvih starih izmjena.\nTo znači da možete vratiti stranicu na prethodno mjesto ako pogriješite, ali ne možete zamijeniti postojeću stranicu.\n\n<strong>Napomena:</strong>\nOvo može biti drastična i neočekivana promjena kad su u pitanju popularne stranice.\nMolimo da dobro razmislite prije no što premjestite stranicu.",
-       "movepagetext-noredirectfixer": "Koristeći donji obrazac, preimenovat ćete stranicu i premjestiti cijelu njenu historiju na novi naziv.\nStari naziv postat će preusmjerenje na novi naziv.\nMolimo da provjerite postoje li [[Special:DoubleRedirects|dvostruka]] ili [[Special:BrokenRedirects|nedovršena preusmjerenja]].\nVi ste za to odgovorni te morate provjeriti jesu li linkovi ispravni i vode li tamo kamo bi trebali voditi.\n\nImajte na umu da stranica '''neće''' biti premještena ako već postoji stranica s tim imenom, osim ako je prazna ili je preusmjerenje ili nema ranije historije.\nOvo znači da možete preimenovati stranicu nazad gdje je ranije bila preimenovana ako ste pogriješili, ali ne možete ponovo preimenovati postojeću stranicu.\n\n<strong>Napomena:</strong>\nImajte na umu da premještanje popularnog članka može biti\ndrastična i neočekivana promjena za korisnike; molimo da budete sigurni da ste shvatili posljedice prije no što nastavite.",
+       "movepagetext-noredirectfixer": "Koristeći donji obrazac, preimenovat ćete stranicu i premjestiti cijelu njenu historiju na novi naziv.\nStari naziv postat će preusmjerenje na novi naziv.\nMolimo da provjerite postoje li [[Special:DoubleRedirects|dvostruka]] ili [[Special:BrokenRedirects|nedovršena preusmjerenja]].\nVi ste za to odgovorni te morate provjeriti jesu li linkovi ispravni i vode li tamo kamo bi trebali voditi.\n\nImajte na umu da stranica '''neće''' biti premještena ako već postoji stranica s tim imenom, osim ako je prazna ili je preusmjerenje ili nema ranije historije.\nOvo znači da možete preimenovati stranicu nazad gdje je ranije bila preimenovana ako ste pogriješili, ali ne možete ponovo preimenovati postojeću stranicu.\n\n<strong>Napomena:</strong>\nOvo može biti drastična i neočekivana promjena za popularnu stranicu;\ndobro razmislite o posljedicama prije nego što nastavite.",
        "movepagetalktext": "Ako označite ovu kutijicu, odgovarajuća stranica za razgovor, ako postoji, automatski će biti premještena na novi naziv, osim ako već postoji sadržaj na odredišnoj stranici za razgovor.\n\nU tom slučaju, morat ćete ručno premjestiti ili prekopirati stranicu ako to želite.",
        "moveuserpage-warning": "<strong>Upozorenje:</strong> Premještate korisničku stranicu. Imajte u vidu da će samo stranica biti premještena, a sam korisnik <em>neće</em> biti preimenovan.",
        "movecategorypage-warning": "<strong>Upozorenje:</strong> Premještate stranicu kategorije. Imajte na umu da će samo stranica biti premještena i da sve stranice u staroj kategoriji <em>neće</em> biti ponovo kategorirane u novu kategoriju.",
        "fileduplicatesearch-noresults": "Nije pronađena datoteka s imenom \"$1\".",
        "specialpages": "Posebne stranice",
        "specialpages-note-top": "Legenda",
-       "specialpages-note": "* Normalne posebne stranice.\n* <strong class=\"mw-specialpagerestricted\">Zaštićene posebne stranice.</strong>",
        "specialpages-group-maintenance": "Izvještaji za održavanje",
        "specialpages-group-other": "Ostale posebne stranice",
        "specialpages-group-login": "Prijava / otvaranje računa",
        "htmlform-user-not-exists": "<strong>$1</strong> ne postoji.",
        "htmlform-user-not-valid": "<strong>$1</strong> nije ispravno korisničko ime.",
        "logentry-delete-delete": "$1 {{GENDER:$2|obrisao|obrisala}} je stranicu $3",
-       "logentry-delete-delete_redir": "$1 {{GENDER:$2|obrisao|obrisala}} je preusmjerenje $3 prepisivanjem",
+       "logentry-delete-delete_redir": "$1 {{GENDER:$2|obrisao|obrisala}} je preusmjerenje $3 presnimavanjem",
        "logentry-delete-restore": "$1 {{GENDER:$2|vratio|vratila}} je stranicu $3 ($4)",
        "logentry-delete-restore-nocount": "$1 {{GENDER:$2|vratio|vratila}} je stranicu $3",
        "restore-count-revisions": "{{PLURAL:$1|1 izmjena|$1 izmjene|$1 izmjena}}",
index 117b558..2c6e63f 100644 (file)
        "parser-template-loop-warning": "ئەڵقەی داڕێژە دۆزرایەوە: [[$1]]",
        "parser-template-recursion-depth-warning": "سنووری قووڵی گەڕانەوەی داڕێژە تێپەڕیوە ($1)",
        "undo-success": "دەکرێ دەستکاریەکە پووچەڵبکرێتەوە.\nتکایە چاو لەو هەڵسەنگاندنەی خوارەوە بکە تا دڵنیا بیت ئەمە ئەوەیە کە‌ دەتویست بیکەی و دواتر گۆڕانکارییەکانی خوارەوە پاشەکەوت بکە بۆ تەواوکردنی پووچەڵکردنەوەکە.",
-       "undo-failure": "Ù\84Û\95بÛ\95ر Ú©Û\8eØ´Û\95Û\8c Ø¯Û\95ستâ\80\8cتÛ\8eâ\80\8cÙ\88Û\95رداÙ\86Ø\8c Ù\86اتÙ\88اÙ\86Û\8c Ø¯Û\95ستکارÛ\8cÛ\95Ú©Û\95 Ø¦Û\95Ù\86جاÙ\85â\80\8cÙ\86Û\95دراÙ\88 Ø¨Ú©Û\95Û\8cت.",
+       "undo-failure": "Ù\86Û\95تÙ\88اÙ\86درا Ø¯Û\95ستکارÛ\8cÛ\8cÛ\95Ú©Û\95 Ù¾Ù\88Ù\88Ú\86Û\95Úµ Ø¨Ú©Ø±Û\8eتÛ\95Ù\88Û\95 Ù\84Û\95بÛ\95ر Ú©Û\8eØ´Û\95Û\8c Ø¯Û\95ستتÛ\8eÙ\88Û\95رداÙ\86.",
        "undo-norev": "ناتوانی دەستکاریەکە ئەنجام‌نەدراو بکەی لەبەر ئەوەی بوونی نیە یا سڕدراوەتەوە.",
        "undo-nochange": "وا دیارە دەستکارییەکە پووچەڵ کراوەتەوە.",
        "undo-summary": "گەڕاندنەوەی پێداچوونەوەی $1 لە لایەن [[Special:Contributions/$2|$2]] ([[User talk:$2|لێدوان]])",
        "rcfilters-filterlist-title": "فیلتەرەکان",
        "rcfilters-filterlist-whatsthis": "ئەمە چییە؟",
        "rcfilters-highlightmenu-title": "ڕەنگێکی نوێ ھەڵبژێرە",
-       "rcfilters-filter-registered-label": "تۆمارکراو",
-       "rcfilters-filter-registered-description": "ئەو بەکارھێنەرانەی لە ژوورەوەن",
-       "rcfilters-filter-unregistered-label": "تۆمارنەکراوەکان",
-       "rcfilters-filter-unregistered-description": "ئەو بەکارھێنەرانەی لە ژوورەوە نین",
        "rcfilters-filter-editsbyself-label": "مافەکانی خۆت",
        "rcfilters-filter-editsbyself-description": "دەستکارییەکانی خۆت.",
        "rcfilters-filter-editsbyother-label": "دەستکارییەکانی کەسانی تر",
        "rcfilters-filter-editsbyother-description": "ھەموو گۆڕانکارییەکان بێجگە لەوەی خۆت",
+       "rcfilters-filter-user-experience-level-registered-label": "تۆمارکراو",
+       "rcfilters-filter-user-experience-level-registered-description": "ئەو بەکارھێنەرانەی لە ژوورەوەن",
+       "rcfilters-filter-user-experience-level-unregistered-label": "تۆمارنەکراوەکان",
+       "rcfilters-filter-user-experience-level-unregistered-description": "ئەو بەکارھێنەرانەی لە ژوورەوە نین",
        "rcfilters-filter-user-experience-level-newcomer-label": "تازەکاران",
        "rcfilters-filter-user-experience-level-newcomer-description": "کەمتر لە ١٠ دەستکاری و ٤ ڕۆژ لە چالاک بوون",
        "rcfilters-filter-user-experience-level-experienced-label": "بەکارھێنەرانی پێشکەوتوو",
        "fileduplicatesearch-result-n": "پەڕگەی «$1» {{PLURAL:$2|١ دووپاتکراوەی کوتوموتی|$2 دووپاتکراوەی کوتوموتی}} ھەیە.",
        "fileduplicatesearch-noresults": "پەڕگەیەک بە ناوی «$1» نەدۆزرایەوە.",
        "specialpages": "پەڕە تایبەتەکان",
-       "specialpages-note": "* پەڕە تایبەتە ئاساییەکان.\n* <span class=\"mw-specialpagerestricted\">پەڕە تایبەتە بەرگریلێکراوەکان.</span>",
        "specialpages-group-maintenance": "ڕاپۆرتەکانی چاکسازی",
        "specialpages-group-other": "پەڕە تایبەتەکانی دیکە",
        "specialpages-group-login": "چوونەژوورەوە / دروستکردنی ھەژمار",
        "logentry-newusers-byemail": "ھەژماری بەکارھێنەریی $3 لە لایەن $1 {{GENDER:$2|دروست کرا}} و تێپەڕوشە بە ئیمەیل نێردرا",
        "logentry-newusers-autocreate": "ھەژماری بەکارھێنەریی $1 بە شێوەی خۆگەڕ {{GENDER:$2|دروست کرا}}",
        "logentry-protect-move_prot": "$1 {{GENDER:$2}} ڕێکخستنەکانی پاراستنی گۆڕی لە $4 بۆ $3",
+       "logentry-protect-unprotect": "$1 {{GENDER:$2|پاراستنی}} لەسەر $3 لابرد",
        "logentry-protect-protect": "$1 $3ی {{GENDER:$2|پاراست}} $4",
        "logentry-protect-modify": "$1 ئاستی پاراستنی $3ی {{GENDER:$2|گۆڕی}} $4",
        "logentry-rights-rights": "$1 ئەندامێتیی {{GENDER:$6|$3}}ی لە $4 بۆ $5 {{GENDER:$2|گۆڕی}}",
index 007f9ef..bc4e002 100644 (file)
        "fileduplicatesearch-noresults": "Žádný soubor s názvem „$1“ nebyl nalezen.",
        "specialpages": "Speciální stránky",
        "specialpages-note-top": "Legenda",
-       "specialpages-note": "* Normální speciální stránky\n* <span class=\"mw-specialpagerestricted\">Speciální stránky s&nbsp;vyhrazeným přístupem</span>",
        "specialpages-group-maintenance": "Údržba",
        "specialpages-group-other": "Ostatní",
        "specialpages-group-login": "Přihlášení / vytvoření účtu",
index de353e4..d4ad69f 100644 (file)
        "fileduplicatesearch-noresults": "Ni ddaethpwyd o hyd i ffeil o'r enw \"$1\".",
        "specialpages": "Tudalennau arbennig",
        "specialpages-note-top": "Allwedd",
-       "specialpages-note": "* Tudalennau arbennig ar gael i bawb.\n* <span class=\"mw-specialpagerestricted\">Tudalennau arbennig cyfyngedig.</span>",
        "specialpages-group-maintenance": "Adroddiadau cynnal a chadw",
        "specialpages-group-other": "Eraill",
        "specialpages-group-login": "Mewngofnodi / creu cyfrif",
index 5ec41ab..2c080f6 100644 (file)
        "fileduplicatesearch-noresults": "Ingen fil med navnet \"$1\" blev fundet.",
        "specialpages": "Specialsider",
        "specialpages-note-top": "Forklaring",
-       "specialpages-note": "* Normale specialsider.\n* <span class=\"mw-specialpagerestricted\">Specialsider med begrænset adgang.</span>",
        "specialpages-group-maintenance": "Vedligeholdelsesside",
        "specialpages-group-other": "Andre specialsider",
        "specialpages-group-login": "Log på / opret bruger",
index 0778133..0969376 100644 (file)
        "fileduplicatesearch-noresults": "Es wurde keine Datei namens „$1“ gefunden.",
        "specialpages": "Spezialseiten",
        "specialpages-note-top": "Legende",
-       "specialpages-note": "* Reguläre Spezialseiten\n* <span class=\"mw-specialpagerestricted\">Zugriffsbeschränkte Spezialseiten</span>",
+       "specialpages-note-restricted": "* Reguläre Spezialseiten\n* <span class=\"mw-specialpagerestricted\">Zugriffsbeschränkte Spezialseiten</span>",
        "specialpages-group-maintenance": "Wartungslisten",
        "specialpages-group-other": "Andere Spezialseiten",
        "specialpages-group-login": "Benutzerkonto",
index c361acd..965bc54 100644 (file)
        "fileduplicatesearch-noresults": "Ningún archivo con el nombre «$1» encontrado.",
        "specialpages": "Páginas especiales",
        "specialpages-note-top": "Leyenda",
-       "specialpages-note": "* Páginas especiales normales.\n* <span class=\"mw-specialpagerestricted\">Páginas especiales restringidas.</span>",
        "specialpages-group-maintenance": "Informes de mantenimiento",
        "specialpages-group-other": "Otras páginas especiales",
        "specialpages-group-login": "Acceder/crear cuenta",
index b3bed3d..a8d1e96 100644 (file)
        "recentchanges-legend-heading": "<strong>Seletus:</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (vaata ka [[Special:NewPages|uute lehekülgede loendit]])",
        "recentchanges-submit": "Näita",
+       "rcfilters-legend-heading": "<strong>Lühendite loetelu:</strong>",
        "rcfilters-activefilters": "Aktiivsed filtrid",
        "rcfilters-advancedfilters": "Täpsemad filtrid",
+       "rcfilters-limit-title": "Näita nii mitut muudatust",
+       "rcfilters-limit-shownum": "Näita viimast $1 muudatust",
+       "rcfilters-days-title": "Viimased päevad",
+       "rcfilters-hours-title": "Viimased tunnid",
+       "rcfilters-days-show-days": "$1 {{PLURAL:$1|päev|päeva}}",
+       "rcfilters-days-show-hours": "$1 {{PLURAL:$1|tund|tundi}}",
        "rcfilters-quickfilters": "Salvestatud filtrid",
        "rcfilters-quickfilters-placeholder-title": "Linke pole veel salvestatud",
        "rcfilters-quickfilters-placeholder-description": "Et filtri sätted salvestada ja et neid hiljem uuesti kasutada, klõpsa alloleva aktiivsete filtrite loendi juures järjehoidjaikooni.",
        "rcfilters-invalid-filter": "Vigane filter",
        "rcfilters-empty-filter": "Aktiivsed filtrid puuduvad. Näidatakse kogu kaastööd.",
        "rcfilters-filterlist-title": "Filtrid",
-       "rcfilters-filterlist-whatsthis": "Mis see on?",
+       "rcfilters-filterlist-whatsthis": "Kuidas see töötab?",
        "rcfilters-filterlist-feedbacklink": "Anna uute filtrite beetaversiooni kohta tagasisidet",
        "rcfilters-highlightbutton-title": "Tulemuste esiletõst",
        "rcfilters-highlightmenu-title": "Vali värvus",
        "rcfilters-noresults-conflict": "Tulemusi ei leitud, sest otsikriteeriumid on konfliktsed.",
        "rcfilters-state-message-subset": "See filter ei tee midagi, sest selle tulemused on kaasatud {{PLURAL:$2|järgmise laiema filtri|järgmiste laiemate filtrite}} tulemustes (tulemuste eristamiseks proovi esiletõstu): $1",
        "rcfilters-state-message-fullcoverage": "Ühe rühma kõigi filtrite valimine on samaväärne mitte ühegi filtri valimisega, mistõttu ei tee see filter midagi. Rühmas on: $1",
-       "rcfilters-filtergroup-registration": "Registreerumine",
-       "rcfilters-filter-registered-label": "Registreerunud",
-       "rcfilters-filter-registered-description": "Sisse logitud kasutajad.",
-       "rcfilters-filter-unregistered-label": "Registreerumata",
-       "rcfilters-filter-unregistered-description": "Kasutajad, kes pole sisse logitud.",
-       "rcfilters-filter-unregistered-conflicts-user-experience-level": "See filter on konfliktis {{PLURAL:$2|järgmise kogemustaseme filtriga|järgmiste kogemustasemete filtritega}}, mis {{PLURAL:$2|leiab|leiavad}} ainult registreerunud kasutajaid: $1",
        "rcfilters-filtergroup-authorship": "Kaastöö autorsus",
        "rcfilters-filter-editsbyself-label": "Enda muudatused",
        "rcfilters-filter-editsbyself-description": "Sinu enda muudatused.",
        "rcfilters-filter-editsbyother-label": "Teiste muudatused",
        "rcfilters-filter-editsbyother-description": "Kõik muudatused peale sinu enda omade.",
-       "rcfilters-filtergroup-userExpLevel": "Kogemustase (ainult registreerunud kasutajate puhul)",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "Kogemustaseme filtrid leiavad ainult registreerunud kasutajaid, mistõttu on see filter konfliktis filtriga \"{{int:rcfilters-filter-unregistered-label}}\".",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "Filter \"{{int:rcfilters-filter-unregistered-label}}\" on konfliktis vähemalt ühe kogemustaseme filtriga, mis leiab ainult registreerunud kasutajaid. Konfliktsed filtrid on ära märgitud ülal aktiivsete filtrite loendis.",
+       "rcfilters-filtergroup-userExpLevel": "Registreerumine ja kasutaja kogemus",
+       "rcfilters-filter-user-experience-level-registered-label": "Registreerunud",
+       "rcfilters-filter-user-experience-level-registered-description": "Sisse logitud kasutajad.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Registreerumata",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Kasutajad, kes pole sisse logitud.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Äsjaalustanud",
-       "rcfilters-filter-user-experience-level-newcomer-description": "Alla 10 muudatuse või tegutsenud alla 4 päeva.",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Registreerunud toimetajad, teinud alla 10 muudatuse või tegutsenud alla 4 päeva.",
        "rcfilters-filter-user-experience-level-learner-label": "Tutvujad",
-       "rcfilters-filter-user-experience-level-learner-description": "Rohkem kogemust kui äsjaalustanutel, aga vähem kui kogenud kasutajatel.",
+       "rcfilters-filter-user-experience-level-learner-description": "Registreerunud toimetajad, kellel on rohkem kogemust kui äsjaalustanutel, aga vähem kui kogenud kasutajatel.",
        "rcfilters-filter-user-experience-level-experienced-label": "Kogenud kasutajad",
-       "rcfilters-filter-user-experience-level-experienced-description": "Üle 500 muudatuse ja tegutsenud üle 30 päeva.",
+       "rcfilters-filter-user-experience-level-experienced-description": "Registreerunud toimetajad, teinud üle 500 muudatuse ja tegutsenud üle 30 päeva.",
        "rcfilters-filtergroup-automated": "Automaatne kaastöö",
        "rcfilters-filter-bots-label": "Robot",
        "rcfilters-filter-bots-description": "Automaattööriistade tehtud muudatused.",
        "rcfilters-hideminor-conflicts-typeofchange-global": "Filter \"{{int:rcfilters-filter-minor-label}}\" on konfliktis vähemalt ühe muudatuste tüübifiltriga, sest teatud tüüpi muudatusi ei saa märkida pisimuudatusteks. Konfliktsed filtrid on ära märgitud ülal aktiivsete filtrite loendis.",
        "rcfilters-hideminor-conflicts-typeofchange": "Teatud tüüpi muudatusi ei saa märkida pisimuudatusteks. Seetõttu on see filter konfliktis järgmiste tüübifiltritega: $1",
        "rcfilters-typeofchange-conflicts-hideminor": "See muudatuste tüübifilter on konfliktis filtriga \"{{int:rcfilters-filter-minor-label}}\". Teatud tüüpi muudatusi ei saa märkida pisimuudatusteks.",
-       "rcfilters-filtergroup-lastRevision": "Viimane redaktsioon",
+       "rcfilters-filtergroup-lastRevision": "Viimased redaktsioonid",
        "rcfilters-filter-lastrevision-label": "Viimane redaktsioon",
-       "rcfilters-filter-lastrevision-description": "Muudatus, mis on leheküljel kõige viimane.",
-       "rcfilters-filter-previousrevision-label": "Varasemad redaktsioonid",
-       "rcfilters-filter-previousrevision-description": "Kõik muudatused, mis pole leheküljel kõige viimased.",
+       "rcfilters-filter-lastrevision-description": "Ainult muudatus, mis on leheküljel kõige viimane.",
+       "rcfilters-filter-previousrevision-label": "Pole viimane redaktsioon",
+       "rcfilters-filter-previousrevision-description": "Kõik muudatused, mis pole kõige viimased.",
        "rcfilters-filter-excluded": "Välja arvatud",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:mitte</strong> $1",
+       "rcfilters-exclude-button-off": "Jäta valitud välja",
+       "rcfilters-exclude-button-on": "Valitud välja jäetud",
        "rcfilters-view-tags": "Märgistatud muudatused",
+       "rcfilters-view-namespaces-tooltip": "Filtri tulemusi nimeruumide lõikes",
+       "rcfilters-view-tags-tooltip": "Filtri tulemusi muudatusmärgiste lõikes",
+       "rcfilters-view-return-to-default-tooltip": "Naase filtri peamenüüsse",
+       "rcfilters-liveupdates-button": "Uuendused reaalajas",
        "rcnotefrom": "Allpool on toodud {{PLURAL:$5|muudatus|muudatused}} alates: <strong>$3, kell $4</strong> (näidatakse kuni <strong>$1</strong> muudatust)",
        "rclistfromreset": "Lähtesta kuupäeva valik",
        "rclistfrom": "Näita muudatusi alates: $3, kell $2",
        "delete-warning-toobig": "See lehekülg on pika redigeerimislooga – üle {{PLURAL:$1|ühe muudatuse|$1 muudatuse}}.\nEttevaatust, selle kustutamine võib esile kutsuda häireid {{GRAMMAR:genitive|{{SITENAME}}}} andmebaasi töös.",
        "deleteprotected": "Seda lehekülge ei saa kustutada, sest see on kaitstud.",
        "deleting-backlinks-warning": "<strong>Hoiatus:</strong> [[Special:WhatLinksHere/{{FULLPAGENAME}}|Teised leheküljed]] viitavad leheküljele, mida oled kustutamas, või see lehekülg on kasutuses mallina.",
+       "deleting-subpages-warning": "<strong>Hoiatus:</strong> Oled kustutamas lehekülge, millel on [[Special:PrefixIndex/{{FULLPAGENAME}}/|{{PLURAL:$1|alamlehekülg|$1 alamlehekülge|51=üle 50 alamlehekülje}}]].",
        "rollback": "Tühista muudatused",
        "rollbacklink": "tühista",
        "rollbacklinkcount": "tühista {{PLURAL:$1|üks muudatus|$1 muudatust}}",
        "undelete-search-title": "Kustutatud lehekülgede otsimine",
        "undelete-search-box": "Kustutatud lehekülgede otsimine",
        "undelete-search-prefix": "Näita lehekülgi, mille pealkiri algab nii:",
+       "undelete-search-full": "Näita leheküljepealkirju, milles sisaldub:",
        "undelete-search-submit": "Otsi",
        "undelete-no-results": "Kustutatud lehekülgede arhiivist sellist lehekülge ei leidunud.",
        "undelete-filename-mismatch": "Failiversiooni ajatempliga $1 ei saa taastada, sest failinimed ei klapi.",
        "fileduplicatesearch-noresults": "Faili nimega \"$1\" ei leidu.",
        "specialpages": "Erileheküljed",
        "specialpages-note-top": "Seletus",
-       "specialpages-note": "* Harilikud erileheküljed.\n* <span class=\"mw-specialpagerestricted\">Piiranguga erileheküljed.</span>",
        "specialpages-group-maintenance": "Hooldusaruanded",
        "specialpages-group-other": "Teised erileheküljed",
        "specialpages-group-login": "Sisselogimine ja konto loomine",
index cf4f5db..12c7fbd 100644 (file)
        "fileduplicatesearch-noresults": "Ez da aurkitu \"$1\" izeneko fitxategirik.",
        "specialpages": "Orri bereziak",
        "specialpages-note-top": "Azalpenak",
-       "specialpages-note": "* Orri berezi arruntak.\n* <strong class=\"mw-specialpagerestricted\">Mugatutako orri bereziak.</strong>",
+       "specialpages-note-restricted": "* Orrialde arrunt bereziak.\n* <span class=\"mw-specialpagerestricted\">Restricted special pages.</span>",
        "specialpages-group-maintenance": "Mantentze-oharrak",
        "specialpages-group-other": "Beste orri berezi batzuk",
        "specialpages-group-login": "Hasi saioa / sortu kontua",
index 6f51de3..900871b 100644 (file)
        "fileduplicatesearch-noresults": "Aucun fichier nommé « $1 » n'a été trouvé.",
        "specialpages": "Pages spéciales",
        "specialpages-note-top": "Légende",
-       "specialpages-note": "* Pages spéciales normales.\n* <span class=\"mw-specialpagerestricted\">Pages spéciales restreintes.</span>",
+       "specialpages-note-restricted": "* Pages spéciales normales.\n* <span class=\"mw-specialpagerestricted\">Pas spéciales restreintes.</span>",
        "specialpages-group-maintenance": "Rapports de maintenance",
        "specialpages-group-other": "Autres pages spéciales",
        "specialpages-group-login": "S'identifier / s'inscrire",
index 2f8a9ab..f2fe856 100644 (file)
        "fileduplicatesearch-noresults": "Nian datei mä di nööm „$1“ fünjen.",
        "specialpages": "Spezial-sidjen",
        "specialpages-note-top": "Legend",
-       "specialpages-note": "* Normool spezial-sidjen\n* <span class=\"mw-specialpagerestricted\">Spezial-sidjen mä tugripsrochten</span>",
        "specialpages-group-maintenance": "Werksteedsidjen",
        "specialpages-group-other": "Ööder spezial-sidjen",
        "specialpages-group-login": "Melde di uun of skriiw di iin",
index 7f040a0..012e8f8 100644 (file)
        "fileduplicatesearch-noresults": "Non se atopou ningún ficheiro chamado \"$1\".",
        "specialpages": "Páxinas especiais",
        "specialpages-note-top": "Lenda",
-       "specialpages-note": "* Páxinas especiais normais.\n* <span class=\"mw-specialpagerestricted\">Páxinas especiais restrinxidas.</span>",
+       "specialpages-note-restricted": "* Páxinas especiais normais.\n* <span class=\"mw-specialpagerestricted\">Páxinas especiais restrinxidas.</span>",
        "specialpages-group-maintenance": "Informes de mantemento",
        "specialpages-group-other": "Outras páxinas especiais",
        "specialpages-group-login": "Rexistro",
index 75b5744..b1c3880 100644 (file)
        "linkstoimage": "{{PLURAL:$1|halaman lapatiyoma'o}} o wumbuta ode berkas botiye:",
        "nolinkstoimage": "Diya'a halaman u owumbuta ode berkas botiye",
        "sharedupload-desc-here": "Berkas botiye lonto $1 wawu hepohunaliyo to poroyek uweewo.\nDeskripsi lonto [$2 halaman deskripsiliyo] woluwo to tibawa botiya.",
+       "filepage-nofile": "Diya'a berkas lo tanggula botiye",
        "upload-disallowed-here": "Yi'o diila mowali modeehe berkas botiye",
        "randompage": "Halaman totonula",
        "statistics": "Statistik",
        "namespacesall": "nga'amila",
        "monthsall": "nga'amila",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|bisala]])",
+       "redirect-submit": "Ntali",
+       "redirect-lookup": "Yilolohu",
+       "redirect-page": "ID Halaman",
+       "redirect-revision": "Halaman biloli'o",
+       "redirect-file": "Tanggulo berkas",
        "specialpages": "Halaman Spesial",
        "tag-filter": "[[Special:Tags|Tag]]filter:",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Tag}}]]: $2)",
index 889ed94..9306bff 100644 (file)
        "fileduplicatesearch-noresults": "S isch kei Datei mit em Name „$1“ gfunde wore.",
        "specialpages": "Spezialsytene",
        "specialpages-note-top": "Zeichenerklärig:",
-       "specialpages-note": "* Normali Spezialsyte.\n* <span class=\"mw-specialpagerestricted\">Spezialsyte mit bschränktem Zuegang.</span>",
        "specialpages-group-maintenance": "Wartigslischte",
        "specialpages-group-other": "Andri Spezialsyte",
        "specialpages-group-login": "Aamälde/Konto aalege",
index fe96f31..e5f3045 100644 (file)
        "showdiff": "ફેરફારો દર્શાવો",
        "anoneditwarning": "<strong>ચેતવણી:</strong> તમે તમારા સભ્ય નામથી પ્રવેશ કર્યો નથી.\nઆ પાનાનાં ઇતિહાસમાં તમારૂં આઇ.પી. (IP) એડ્રેસ નોંધવામાં આવશે અને તમારૂં આઈ.પી. લોકો જાહેર રીતે જોઈ શકશે. માટે <strong>[$1 પ્રવેશ કરો]</strong> અથવા તમે <strong>[$2 ખાતું બનાવો]</strong> તો ફેરફારો તમારા સભ્યનામ હેઠળ થશે અને અન્ય ફાયદાઓ પણ મળશે.",
        "anonpreviewwarning": "તમે સભ્યનામથી પ્રવેશ કર્યો નથી,આ પાનું ઈતિહાસમાંતમારા IP સરનામાના નામે  સાચવવામાં આવશે",
-       "missingsummary": "'''ચેતવણી:''' તમે ફેરફારનો સારંશ નથી આપ્યો.\nજો તમે \"$1\"  પર ક્લીક કરશો તો તમરો ફેરફારા સારાઁશાવગરાસાચવવામાં આવશે",
+       "missingsummary": "<strong>ચેતવણી:</strong> તમે ફેરફારોનો સારાંશ આપ્યો\nજો તમે \"$1\"  પર ફરી ક્લિક કરશો તો તમારા ફેરફારો સાચવવામાં આવશે.",
        "missingcommenttext": "કૃપા કરી નીચે ટીપ્પણી લખો.",
        "missingcommentheader": "'''યાદ દેવડાવું:'''તમે તમારી ટિપ્પણી ને શીર્ષક/મથાળુ આપ્યું નથી. \nજો તમે  \"$1\" પર ફરીથી ક્લિક કરશો, તો તમારા ફેરરારો મથાળા વગર સચવાશે.",
        "summary-preview": "સંપાદન સારાંશ પૂર્વાવલોકન:",
        "fileduplicatesearch-result-n": "\"$1\" ફાઇલની તેની સમાન {{PLURAL:$2|1 નકલ |$2 નકલો }} છે.",
        "fileduplicatesearch-noresults": " \"$1\" નામ ધરાવતી કોઇ ફાઇલ ન મળી",
        "specialpages": "ખાસ પાનાંઓ",
-       "specialpages-note": "* નિયમિત ખાસ પાનાં.\n* <span class=\"mw-specialpagerestricted\">પ્રતિબંધિત ખાસ પાનાં.</span>",
        "specialpages-group-maintenance": "સમારકામ અહેવાલ",
        "specialpages-group-other": "અન્ય ખાસ પાનાઓ",
        "specialpages-group-login": "પ્રવેશ / ખાતુ બનાવો",
index d9472a2..a60f65b 100644 (file)
        "fileduplicatesearch-noresults": "לא נמצא קובץ בשם \"$1\".",
        "specialpages": "דפים מיוחדים",
        "specialpages-note-top": "מקרא",
-       "specialpages-note": "* דפים מיוחדים רגילים.\n* <span class=\"mw-specialpagerestricted\">דפים מיוחדים מוגבלים.</span>",
+       "specialpages-note-restricted": "* דפים מיוחדים רגילים.\n* <span class=\"mw-specialpagerestricted\">דפים מיוחדים מוגבלים.</span>",
        "specialpages-group-maintenance": "דיווחי תחזוקה",
        "specialpages-group-other": "דפים מיוחדים אחרים",
        "specialpages-group-login": "כניסה לחשבון / הרשמה",
index a0a6768..5f2ba29 100644 (file)
        "fileduplicatesearch-noresults": "कोई फ़ाइल नाम \"$1\" मिला नहीं ।",
        "specialpages": "विशेष पृष्ठ",
        "specialpages-note-top": "कुंजी",
-       "specialpages-note": "* साधारण विशेष पृष्ठ।\n* <span class=\"mw-specialpagerestricted\">प्रतिबंधित विशेष पृष्ठ।</span>",
        "specialpages-group-maintenance": "अनुरक्षण रिपोर्ट",
        "specialpages-group-other": "अन्य विशेष पृष्ठ",
        "specialpages-group-login": "सत्र आरम्भ / खाता खोलें",
index 71d2828..0362e77 100644 (file)
        "fileduplicatesearch-noresults": "Nije pronađena datoteka s imenom \"$1\".",
        "specialpages": "Posebne stranice",
        "specialpages-note-top": "Legenda",
-       "specialpages-note": "* Normalne posebne stranice\n* <span class=\"mw-specialpagerestricted\">Posebne stranice s ograničenim pristupom.</span>",
        "specialpages-group-maintenance": "Izvješća održavanja",
        "specialpages-group-other": "Ostale posebne stranice",
        "specialpages-group-login": "Prijava/otvaranje računa",
index a6c70ed..0c75f3a 100644 (file)
        "rcfilters-legend-heading": "<strong>Rövidítések listája:</strong>",
        "rcfilters-activefilters": "Aktív szűrők",
        "rcfilters-advancedfilters": "Haladó szűrők",
+       "rcfilters-limit-title": "Megjelenítendő változtatások",
+       "rcfilters-limit-shownum": "Utolsó $1 változtatás megjelenítése",
+       "rcfilters-days-show-days": "$1 nap",
+       "rcfilters-days-show-hours": "$1 óra",
        "rcfilters-quickfilters": "Mentett szűrők",
        "rcfilters-quickfilters-placeholder-title": "Nincs mentett hivatkozás",
        "rcfilters-savedqueries-defaultlabel": "Mentett szűrők",
        "undelete-search-title": "Törölt lapok keresése",
        "undelete-search-box": "Törölt lapok keresése",
        "undelete-search-prefix": "A megadott szavakkal kezdődő oldalak megjelenítése:",
+       "undelete-search-full": "A következőt tartalmazó címek keresése:",
        "undelete-search-submit": "Keresés",
        "undelete-no-results": "Nem található a keresési feltételeknek megfelelő oldal a törlési naplóban.",
        "undelete-filename-mismatch": "Nem állítható helyre a(z) $1 időbélyeggel ellátott változat: a fájlnév nem egyezik meg",
        "fileduplicatesearch-noresults": "Nincs „$1” nevű fájl.",
        "specialpages": "Speciális lapok",
        "specialpages-note-top": "Jelmagyarázat",
-       "specialpages-note": "* Mindenki számára elérhető speciális lapok.\n* <span class=\"mw-specialpagerestricted\">Korlátozott hozzáférésű speciális lapok.</span>",
        "specialpages-group-maintenance": "Állapotjelentések",
        "specialpages-group-other": "További speciális lapok",
        "specialpages-group-login": "Bejelentkezés / fiók létrehozása",
index 1ef5576..bd41893 100644 (file)
        "fileduplicatesearch-result-n": "$1 նիշքն ունի {{PLURAL:$2|1 նույնական կրկնօրինակ|$2 նույնական կրկնօրինակ}}.",
        "fileduplicatesearch-noresults": "$1 անունով նիշք չի գտնվել",
        "specialpages": "Սպասարկող էջեր",
-       "specialpages-note": "* Հասարակ հատուկ էջեր։\n* <span class=\"mw-specialpagerestricted\">Սահմանափակված հատուկ էջեր։</span>",
        "specialpages-group-maintenance": "Տեխնիկական սպասարկման տեղեկատուներ",
        "specialpages-group-other": "Այլ հատուկ էջեր",
        "specialpages-group-login": "Մտնել / Գրանցվել",
index 37b4749..1e87766 100644 (file)
        "badtitle": "Мегаш йоаца цӀи",
        "badtitletext": "Езаш йола оагӀон цӀи нийса яц, яьсса я, е харцахь йоалаяй меттий юкъера цIи е интервики цӀи. Иштта, цӀера юкъе оттаде мегаш доаца хьаракаш нийсаденна хила мегаш да.",
        "viewsource": "Хьажар",
+       "viewsource-title": "Оагӏон $1 духхьара текстага хьажар",
        "actionthrottled": "Сухалах доазув дар",
        "protectedpagetext": "Ер оагIув лораяь я цу тIа хувцамаш е кхы дола ардамаш дергдоацаш.",
        "virus-unknownscanner": "йовзанза антивирус:",
        "newarticletext": "Шо тIахьожаяргаца дехьадаьннад йоаца оагӀон тӀа.\nИз хьакхолларгьйолаш кӀалхагӀа доала корачу текст Iочуязде (нагахьа санна кхетаде хала дале [$1 новкъосталара оагӀонга] хьажа).\nЦа ховш укхаза нийсденнадале, шоай браузера '''Юха''' (назад) тоIаер тӀа пӀелг тоӀабе.",
        "noarticletext": "ХIанза укх оагӀон тӀа текст яц.\nШун аьттув ба [[Special:Search/{{PAGENAME}}|цу тайпара цӀи хьоаяр кораде]] кхыйола оагIонаш тIа, иштта\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} тара дола тептарий дIаяздаьраш], е\n'''[{{fullurl:{{FULLPAGENAME}}|action=edit}} изза мо цӀи йолаш оагӀув хьакхолла]'''</span>.",
        "noarticletext-nopermission": "ХIанз укх оагӀон тӀа текст яц.\nШун аьттув ба [[Special:Search/{{PAGENAME}}|цу тайпара цӀи белгалъяр хьалаха]] кхыйола оагIонаш тIа, иштта\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} тара дола тептарай дIаяздаьраш].</span> Ер оагӀув хьакхолла Хьа бокъо яц.",
+       "userpage-userdoesnotexist-view": "«$1» яха дагара йоазув дац.",
        "note": "'''Белгалдоахар:'''",
        "previewnote": "'''Теркам бе, ер хьалххе бIаргтохар мара бац.'''\nХьа хувцамаш хIанза а дIаяздаь дац!",
        "continue-editing": "Хувцар кхы дIахо де",
        "rcshowhidebots-show": "Хьахьокха",
        "rcshowhidebots-hide": "Къайладаккха",
        "rcshowhideliu": "$1 бовзийтарчара доакъашхой",
+       "rcshowhideliu-show": "Хьахьокха",
        "rcshowhideliu-hide": "Къайлабаха",
        "rcshowhideanons": "$1 цIияккханза доакъашхой",
        "rcshowhideanons-show": "Хьахьокха",
        "htmlform-selectorother-other": "Кхыдар",
        "logentry-delete-delete": "$1 {{GENDER:$2|дIаяккхай}} оагIув $3",
        "logentry-move-move": "$1 {{GENDER:$2|цӀи хийцай}} $3 → $4",
+       "logentry-move-move-noredirect": "$1 {{GENDER:$2|цӀи хийцай}} $3 → $4 дӀа-сахьожадар ца дуташ.",
        "logentry-newusers-create": "{{GENDER:$2|Доакъашхочо хьакхеллад}} дагара йоазув $1",
        "logentry-upload-upload": "$1 {{GENDER:$2|чуяьккхай}} $3",
        "rightsnone": "(яц)",
index 0d5f58a..08a9911 100644 (file)
        "template-loop-category-desc": "La pagina contiene un template che richiama sé stesso, cioè un template in cui è incluso lo stesso template.",
        "parser-template-recursion-depth-warning": "È stato raggiunto il limite di ricorsione nel template ($1)",
        "language-converter-depth-warning": "Limite di profondità del convertitore di lingua superato ($1)",
-       "node-count-exceeded-category": "Pagine dove viene superato il numero di nodi",
+       "node-count-exceeded-category": "Pagine che superano il numero di nodi",
        "node-count-exceeded-category-desc": "La pagina supera il numero massimo di nodi.",
        "node-count-exceeded-warning": "Questa pagina ha superato il numero di nodi",
        "expansion-depth-exceeded-category": "Pagine nelle quali è superata la profondità di espansione",
        "revdelete-text-file": "Le versioni di file cancellati appariranno ancora nella cronologia del file, ma parti del loro contenuto sarà inaccessibile al pubblico.",
        "logdelete-text": "Gli eventi cancellati appariranno ancora nei registri, ma parti del loro contenuto sarà inaccessibile al pubblico.",
        "revdelete-text-others": "Altri amministratori saranno ancora in grado di accedere ai contenuti nascosti e potranno ripristinarli, se non sono state impostate restrizioni aggiuntive.",
-       "revdelete-confirm": "Per favore conferma che questo è quanto intendi fare, che sei consapevole delle conseguenze, e che stai facendo questo nel rispetto delle [[{{MediaWiki:Policy-url}}|linee guida]].",
+       "revdelete-confirm": "Per favore conferma che questo è quanto intendi fare, che sei consapevole delle conseguenze, e che stai facendo ciò nel rispetto delle [[{{MediaWiki:Policy-url}}|linee guida]].",
        "revdelete-suppress-text": "La rimozione dovrebbe essere utilizzata '''unicamente''' nei seguenti casi:\n* informazioni potenzialmente diffamatorie\n* dati personali inopportuni\n*: ''indirizzi, numeri di telefono, codici fiscali, ecc.''",
        "revdelete-legend": "Imposta le seguenti limitazioni sulle versioni cancellate:",
        "revdelete-hide-text": "Testo della versione",
        "yourlanguage": "Lingua dell'interfaccia:",
        "yourvariant": "Variante della lingua:",
        "prefs-help-variant": "La variante o grafia in cui preferisci che le pagine del wiki ti siano mostrate.",
-       "yournick": "Soprannome (nickname):",
+       "yournick": "Nuova firma:",
        "prefs-help-signature": "I commenti nelle pagine di discussione devono essere firmati con \"<nowiki>~~~~</nowiki>\" che verrà convertito nella propria firma seguita dalla data.",
        "badsig": "Errore nella firma non standard, verificare i tag HTML.",
        "badsiglength": "La firma scelta è troppo lunga, non deve superare $1 {{PLURAL:$1|carattere|caratteri}}.",
        "rcfilters-view-tags": "Modifiche etichettate",
        "rcnotefrom": "Di seguito {{PLURAL:$5|è elencata la modifica apportata|sono elencate le modifiche apportate}} a partire da <strong>$3, $4</strong> (mostrate fino a <strong>$1</strong>).",
        "rclistfromreset": "Reimposta la selezione della data",
-       "rclistfrom": "Mostra le modifiche apportate a partire da $3 $2",
+       "rclistfrom": "Mostra le nuove modifiche a partire daː $2, $3",
        "rcshowhideminor": "$1 le modifiche minori",
        "rcshowhideminor-show": "Mostra",
        "rcshowhideminor-hide": "Nascondi",
        "undeletepage": "Visualizza e recupera le pagine cancellate",
        "undeletepagetitle": "'''Quanto segue è composto da versioni cancellate di [[:$1|$1]]'''.",
        "viewdeletedpage": "Visualizza le pagine cancellate",
-       "undeletepagetext": "{{PLURAL:$1|La pagina indicata di seguito è stata cancellata, ma è ancora in archivio e pertanto può essere recuperata|Le pagine indicate di seguito sono state cancellate, ma sono ancora in archivio e pertanto possono essere recuperate}}. L'archivio può essere svuotato periodicamente.",
+       "undeletepagetext": "{{PLURAL:$1|La pagina indicata di seguito è stata cancellata, ma è ancora in archivio e pertanto può essere ripristinata|Le pagine indicate di seguito sono state cancellate, ma sono ancora in archivio e pertanto possono essere ripristinate}}. L'archivio può essere svuotato periodicamente.",
        "undelete-fieldset-title": "Ripristina versioni",
        "undeleteextrahelp": "Per recuperare l'intera cronologia della pagina, lasciare tutte le caselle deselezionate e fare clic su '''''{{int:undeletebtn}}'''''.\nPer effettuare un ripristino selettivo, selezionare le caselle corrispondenti alle versioni da ripristinare e fare clic su '''''{{int:undeletebtn}}'''''.",
        "undeleterevisions": "{{PLURAL:$1|Una versione cancellata|$1 versioni cancellate}}",
-       "undeletehistory": "Recuperando questa pagina, tutte le sue versioni saranno ripristinate nella relativa cronologia.\nSe dopo la cancellazione è stata creata una nuova pagina con lo stesso titolo, le versioni recuperate saranno inserite nella cronologia precedente.",
+       "undeletehistory": "Ripristinando questa pagina, tutte le sue versioni saranno recuperate nella relativa cronologia.\nSe dopo la cancellazione è stata creata una nuova pagina con lo stesso titolo, le versioni recuperate saranno inserite nella cronologia precedente.",
        "undeleterevdel": "Il ripristino non verrà effettuato se determina la cancellazione parziale della versione attuale della pagina o del file interessato. In tal caso, è necessario rimuovere il segno di spunta o l'oscuramento dalle versioni cancellate più recenti.",
        "undeletehistorynoadmin": "Questa pagina è stata cancellata.\nIl motivo della cancellazione è mostrato qui sotto, assieme ai dettagli dell'utente che ha modificato questa pagina prima della cancellazione.\nIl testo contenuto nelle versioni cancellate è disponibile solo agli amministratori.",
        "undelete-revision": "Versione cancellata della pagina $1, inserita il $4 alle $5 da $3:",
        "undeleteinvert": "Inverti selezione",
        "undeletecomment": "Motivo:",
        "cannotundelete": "Alcuni o tutti i ripristini non riusciti:\n$1",
-       "undeletedpage": "'''La pagina $1 è stata recuperata'''\n\nConsulta il [[Special:Log/delete|registro delle cancellazioni]] per vedere le cancellazioni e i recuperi più recenti.",
+       "undeletedpage": "'''La pagina $1 è stata ripristinata'''\n\nConsulta il [[Special:Log/delete|registro delle cancellazioni]] per vedere le cancellazioni e i ripristini più recenti.",
        "undelete-header": "Consulta il [[Special:Log/delete|registro delle cancellazioni]] per vedere le cancellazioni più recenti.",
        "undelete-search-title": "Ricerca nelle pagine cancellate",
        "undelete-search-box": "Ricerca le pagine cancellate",
        "exif-personinimage": "Persona raffigurata",
        "exif-originalimageheight": "Altezza dell'immagine prima che fosse ritagliata",
        "exif-originalimagewidth": "Larghezza dell'immagine prima che fosse ritagliata",
-       "exif-compression-1": "Nessuno",
+       "exif-compression-1": "Senza compressione",
        "exif-compression-2": "CCITT gruppo 3 monodimensionale - codifica run length di Huffman modificata",
        "exif-compression-3": "Codifica fax CCITT Group 3",
        "exif-compression-4": "Codifica fax CCITT gruppo 4",
        "fileduplicatesearch-noresults": "Nessun file di nome \"$1\" trovato.",
        "specialpages": "Pagine speciali",
        "specialpages-note-top": "Legenda",
-       "specialpages-note": "* Pagine speciali non riservate.\n* <span class=\"mw-specialpagerestricted\">Pagine speciali riservate ad alcune categorie di utenti.</span>",
        "specialpages-group-maintenance": "Resoconti di manutenzione",
        "specialpages-group-other": "Altre pagine speciali",
        "specialpages-group-login": "Accesso / creazione utenze",
        "feedback-back": "Indietro",
        "feedback-bugcheck": "Ottimo! Verifica che non sia già fra i [$1 bug conosciuti].",
        "feedback-bugnew": "Controllo effettuato. Segnala un nuovo bug",
-       "feedback-bugornote": "Se si è in grado di descrivere il problema tecnico riscontrato in maniera precisa, [$1 segnalate il bug]. In alternativa, si può usare il modulo semplificato sottostante. Il commento inserito sarà aggiunto alla pagina \"[$3 $2]\", insieme al proprio nome utente e al browser in uso.",
+       "feedback-bugornote": "Se sei in grado di descrivere il problema tecnico riscontrato in maniera precisa, [$1 segnala il bug]. In alternativa, si può usare il modulo semplificato sottostante. Il commento inserito sarà aggiunto alla pagina \"[$3 $2]\", insieme al proprio nome utente.",
        "feedback-cancel": "Annulla",
        "feedback-close": "Fatto",
        "feedback-external-bug-report-button": "Documenta un problema tecnico",
        "limitreport-templateargumentsize-value": "$1/$2 {{PLURAL:$2|byte}}",
        "limitreport-expansiondepth": "Massima profondità di espansione",
        "limitreport-expensivefunctioncount": "Numero funzioni parser dispendiose",
-       "expandtemplates": "Espansione dei template",
+       "expandtemplates": "Espandi i template",
        "expand_templates_intro": "Questa pagina speciale elabora un testo espandendo tutti i template presenti.\nCalcola inoltre il risultato delle funzioni supportate dal parser come\n<code><nowiki>{{</nowiki>#language:…}}</code> e delle variabili di sistema quali\n<code><nowiki>{{</nowiki>CURRENTDAY}}</code>,\nvale a dire praticamente tutto ciò che si trova tra doppie parentesi graffe.",
        "expand_templates_title": "Contesto (per {{FULLPAGENAME}} ecc.):",
        "expand_templates_input": "Testo da espandere:",
        "expand_templates_xml_output": "Output in formato XML",
        "expand_templates_html_output": "Risultato HTML",
        "expand_templates_ok": "OK",
-       "expand_templates_remove_comments": "Ignora i commenti",
+       "expand_templates_remove_comments": "Rimuovi i commenti",
        "expand_templates_remove_nowiki": "Elimina il tag <nowiki> nel risultato",
        "expand_templates_generate_xml": "Mostra albero sintattico XML",
        "expand_templates_generate_rawhtml": "Mostra HTML",
index af33996..023ca90 100644 (file)
        "searchbutton": "Golèk",
        "go": "Menyang",
        "searcharticle": "Menyang",
-       "history": "Sujarah kaca",
-       "history_short": "Sujarah",
-       "history_small": "sujarah",
+       "history": "Sajarah kaca",
+       "history_short": "Sajarah",
+       "history_small": "sajarah",
        "updatedmarker": "wis dianyari kawit tekaku mréné pungkasan",
        "printableversion": "Vèrsi cap-capan",
        "permalink": "Pranala permanèn",
        "showpreview": "Deleng pratuduh",
        "showdiff": "Tuduhaké owahan",
        "anoneditwarning": "<strong>Pènget:</strong> Panjenengan durung mlebu log. Alamat IP-né panjenengan bakal katon marang wong akèh manawa panjenengan mbesut. Manawa panjenengan <strong>[$1 mlebu log]</strong> utawa <strong>[$2 nggawé akun]</strong>, besutané panjenengan bakal dadi darbéné naragunané panjenengan lan uga ana kauntungan liya.",
-       "anonpreviewwarning": "<em>Panjenengan durung mlebu log. Yèn disimpen, alamat IP panjenengan bakal kacathet ing sujarah besutan kaca iki.</em>",
+       "anonpreviewwarning": "<em>Panjenengan durung mlebu log. Yèn disimpen, alamat IP panjenengan bakal kacathet ing sajarah besutan kaca iki.</em>",
        "missingsummary": "<strong>Pangéling-éling:</strong> Panjenengan ora ngisèni ringkesané besutan.\nManawa panjenengan mencèt \"$1\" manèh, besutané panjengan bakal kasimpen tanpa katerangan.",
        "selfredirect": "<strong>Pélik:</strong> Sampéyan ngalih kaca iki iya nyang kaca iki dhéwé.\nSampéyan mungkin salah wènèh tujuan kanggo alihan utawa salah mbesut kaca.\nYèn sampéyan ngeklik \"$1\" manèh, kaca alihan bakal digawé.",
        "missingcommenttext": "Mangga isi tanggepan ing ngisor iki.",
        "anontalkpagetext": "----\n<em>Iki kaca parembugané panganggo anonim sing durung gawé akun, utawa sing ora nganggo akuné.</em>\nMula, awak dhéwé kudu nganggo alamat IP awujud angka kanggo nglacak dhèwèké.\nAlamat IP mangkono bisa dianggo déning sawenèh panganggo.\nManawa panjenengan panganggo anonim lan rumasa yèn ana tanggepan sing ora ilok dieneraké marang panjenengan, mangga [[Special:CreateAccount|gawéa akun]] utawa [[Special:UserLogin|mlebua log]] kanggo ngéndhani salah pangira karo panganggo anonim liyané ing tembé buri.",
        "noarticletext": "Kala saiki kaca iki durung ana tulisané.\nSampéyan bisa [[Special:Search/{{PAGENAME}}|nggolèki sesirahing kaca iki]] sajeroning kaca liya,\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} nggolèki log sing magepokan],\nutawa [{{fullurl:{{FULLPAGENAME}}|action=edit}} nggawé kaca iki]</span>.",
        "noarticletext-nopermission": "Saiki lagi ora ana tèks ing kaca iki. \nPanjenengan bisa [[Special:Search/{{PAGENAME}}|nggolèk sesirah kaca iki]] ing kaca liyané, \nutawa <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{urlencode:{{FULLPAGENAME}}}}}} nggolèk ing log sing gegayutan]</span>, nanging panjenengan ora kawogan nggawé kaca iki.",
-       "missing-revision": "Révisi #$1 saka kaca ajeneng \"{{FULLPAGENAME}}\" ora ana.\n\nIki biyasané kasababaké awit nututi pranala sujarah sing wis lawas saka sawijiné kaca sing wis dibusak.\nRerincèné bisa digolèki ing [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} log busak].",
+       "missing-revision": "Révisi #$1 saka kaca ajeneng \"{{FULLPAGENAME}}\" ora ana.\n\nIki biyasané kasababaké awit nututi pranala sajarah sing wis lawas saka sawijiné kaca sing wis dibusak.\nRerincèné bisa digolèki ing [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} log busak].",
        "userpage-userdoesnotexist": "Akun panganggo \"$1\" ora kadhaftar.\nMangga pesthèkaké dhisik yèn panjenengan péngin nggawé/mbesut kaca iki.",
        "userpage-userdoesnotexist-view": "Akun panganggo \"$1\" ora kadhaftar.",
        "blocked-notice-logextract": "Panganggo iki saiki lagi diblokir.\nLog pamblokiran pungkasan dituduhaké ing ngisor iki minangka bahan rujukan:",
        "histlast": "anyar dhéwé",
        "historysize": "($1 {{PLURAL:$1|bét|bét}})",
        "historyempty": "(suwung)",
-       "history-feed-title": "Sujarah owahan",
-       "history-feed-description": "Sujarah owahaning kaca iki ing wiki",
+       "history-feed-title": "Sajarah owahan",
+       "history-feed-description": "Sajarah owahaning kaca iki ing wiki",
        "history-feed-item-nocomment": "$1 ing $2",
        "history-feed-empty": "Kaca sing disuwun ora ditemokaké. Mbokmenawa wis dibusak saka wiki, utawa diwènèhi jeneng anyar. Coba [[Special:Search|golèka ing wiki]] kanggo kaca anyar sing rélevan.",
        "rev-deleted-comment": "(tingkesaning besutan dibusak)",
        "logdelete-failure": "'''Aturan pandhelikan ora bisa disèt:'''\n$1",
        "revdel-restore": "Ngowahi visiblitas (pangatonan)",
        "pagehist": "Babading kaca",
-       "deletedhist": "Sujarah kabusak",
+       "deletedhist": "Sajarah kabusak",
        "revdelete-hide-current": "Gagal ndhelikaké révisi tanggal $2, $1: iki arupa révisi paling anyar.\nRévisi iki ora bisa didhelikaké.",
        "revdelete-show-no-access": "Gagal nampilaké révisi tanggal $1, jam $2: révisi iki wis ditandhani \"kawates\".\nPanjenengan ora nduwèni aksès menyang révisi iki.",
        "revdelete-modify-no-access": "Gagal ngowahi révisi tanggal $1, jam $2: révisi iki wis ditandhani \"kawates\".\nPanjenengan ora nduwèni aksès menyang révisi iki.",
        "revdelete-offender": "Juru pangriptaning owahan:",
        "suppressionlog": "Log barang-barang sing didelikaké (''oversight'')",
        "suppressionlogtext": "Ngisor iki daptar apa-apa waé sing wis dibusak lan diblokir kalebu kontèn sing didhelikaké saka para pangurus.\nDelok [[Special:BlockList|daptar blokiran]] sing isiné daptar apa-apa waé sing lagi dilarang lan diblokir.",
-       "mergehistory": "Gabung sujarah kaca",
+       "mergehistory": "Gabung sajarah kaca",
        "mergehistory-header": "Ing kaca iki panjenengan bisa nggabung révisi-révisi sajarah saka sawijining kaca sumber menyang kaca anyar.\nPastèkna yèn owah-owahan iki bakal netepaké kasinambungan sajarah kaca.",
        "mergehistory-box": "Gabungna revisi-revisi saka rong kaca:",
        "mergehistory-from": "Kaca sumber:",
        "mergehistory-into": "Kaca paran:",
-       "mergehistory-list": "Sujarah besutan sing bisa digabung",
+       "mergehistory-list": "Sajarah besutan sing bisa digabung",
        "mergehistory-merge": "Révisi-révisi sing kapacak ing ngisor iki saka [[:$1]] bisa digabungaké menyang [[:$2]].\nGunakna tombol radio kanggo nggabungaké révisi-révisi sing digawé sadurungé wektu tartamtu. Gatèkna, menawa nganggo pranala navigasi bakal ngesèt ulang kolom iki.",
        "mergehistory-go": "Tuduhaké besutan sing bisa digabung",
        "mergehistory-submit": "Gabung owahan",
        "mergehistory-fail": "Ora bisa nggabung sajarah, coba dipriksa manèh kacané lan paramèter wektuné.",
        "mergehistory-fail-invalid-source": "Kaca sumber ora trep.",
        "mergehistory-fail-invalid-dest": "Kaca paran ora trep.",
-       "mergehistory-fail-no-change": "Panggabung sujarah ora nggabungaké rèvisi. Mangga priksanen kaca lan paramèter wektuné.",
+       "mergehistory-fail-no-change": "Panggabung sajarah ora nggabungaké rèvisi. Mangga priksanen kaca lan paramèter wektuné.",
        "mergehistory-fail-self-merge": "Kaca asal lan kaca paran padha.",
        "mergehistory-fail-timestamps-overlap": "Rèvisi asal tumpuk-undhung utawa njedhul sawisé révisi paran.",
-       "mergehistory-fail-toobig": "Ora bisa nggabungaké sujarah amarga {{PLURAL:$1|révisi}} sing arep dilih munjuli $1.",
+       "mergehistory-fail-toobig": "Ora bisa nggabungaké sajarah amarga {{PLURAL:$1|révisi}} sing arep dilih munjuli $1.",
        "mergehistory-no-source": "Kaca sumber $1 ora ana.",
        "mergehistory-no-destination": "Kaca paran $1 ora ana.",
        "mergehistory-invalid-source": "Kaca sumber kudu asesirah sing sah.",
        "mergelog": "Gabung log",
        "revertmerge": "Wurung gabung",
        "mergelogpagetext": "Ing ngisor iki kapacak daftar panggabungan sajarah kaca ing kaca liyané.",
-       "history-title": "Sujarah owahaning \"$1\"",
+       "history-title": "Sajarah owahaning \"$1\"",
        "difference-title": "Prabéda antara owahan \"$1\"",
        "difference-title-multipage": "Béda antarané kaca \"$1\" lan \"$2\"",
        "difference-multipage": "(Prabédhan antar kaca)",
        "right-autopatrol": "Gawé supaya suntingan-suntingan ditandhani minangka wis dipatroli",
        "right-patrolmarks": "Ndeleng tandha-tandha patroli owah-owahan anyar",
        "right-unwatchedpages": "Tuduhna daftar kaca-kaca sing ora diawasi",
-       "right-mergehistory": "Gabung sujarah kaca",
+       "right-mergehistory": "Gabung sajarah kaca",
        "right-userrights": "Besut kabèh hak panganggo",
        "right-userrights-interwiki": "Besut hak-haking panganggo asal wiki jaba",
        "right-siteadmin": "Kunci lan buka kunci basis data",
        "action-delete": "busak kaca iki",
        "action-deleterevision": "busak révisi",
        "action-deletelogentry": "busak isian log",
-       "action-deletedhistory": "deleng sujarah sing dibusak sawijiné kaca",
+       "action-deletedhistory": "deleng sajarah sing dibusak sawijiné kaca",
        "action-deletedtext": "deleng tèks révisi sing dibusak",
        "action-browsearchive": "nggolèki kaca-kaca sing wis dibusak",
        "action-undelete": "wurung busak kaca",
        "action-purge": "buwang kaca iki",
        "nchanges": "$1 {{PLURAL:$1|pangowahan|owah-owahan}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|saka keri dhewe mrene}}",
-       "enhancedrc-history": "sujarah",
+       "enhancedrc-history": "sajarah",
        "recentchanges": "Owahan pungkasan",
        "recentchanges-legend": "Pilihan owah-owahan pungkasan",
        "recentchanges-summary": "Runutna owah-owahan pungkasan ing wiki iki ing kaca iki.",
        "listfiles-latestversion-yes": "Iya",
        "listfiles-latestversion-no": "Ora",
        "file-anchor-link": "Barkas",
-       "filehist": "Sujarah barkas",
+       "filehist": "Sajarah barkas",
        "filehist-help": "Klik ing tanggal/wektuné saprelu ndeleng rupané barkasé nalika tanggal iku.",
        "filehist-deleteall": "busaken kabèh",
        "filehist-deleteone": "busaken iki",
        "deletereasonotherlist": "Alesan liya",
        "deletereason-dropdown": "*Alesan pambusakan\n** Spam\n** Vandalisme\n** Nglanggar hak cipta\n** Disuwun sing nulis\n** Pangalihan rusak",
        "delete-edit-reasonlist": "Besut alesané pambusak",
-       "delete-toobig": "Kaca iki darbé sujarah besutan sing dawa, punjul $1 {{PLURAL:$1|owahan}}.\nPambusak tumrap kaca sing kaya mangkono wis ora diidinaké nedya njagani murih ora ana karusakan ing {{SITENAME}}.",
-       "delete-warning-toobig": "Kaca iki duwé sujarah besut sing dawa, punjul $1 {{PLURAL:$1|révisi}}.\nMbusak kaca iki bisa ngrusak lakuné basis dhata ing {{SITENAME}};\nkudu diayahi kanthi ngati-ati.",
+       "delete-toobig": "Kaca iki darbé sajarah besutan sing dawa, punjul $1 {{PLURAL:$1|owahan}}.\nPambusak tumrap kaca sing kaya mangkono wis ora diidinaké nedya njagani murih ora ana karusakan ing {{SITENAME}}.",
+       "delete-warning-toobig": "Kaca iki duwé sajarah besut sing dawa, punjul $1 {{PLURAL:$1|révisi}}.\nMbusak kaca iki bisa ngrusak lakuné basis dhata ing {{SITENAME}};\nkudu diayahi kanthi ngati-ati.",
        "deleteprotected": "Panjenengan ora bisa mbusak kaca iki amarga direksa.",
        "deleting-backlinks-warning": "'''Awas:''' Kaca liyane mungkin ana sing nautake ing kaca sing arep sampeyan busak.",
        "rollback": "Pulihaké besutan",
        "lockedbyandtime": "(déning {{GENDER:$1|$1}} tanggal $2 wanci $3)",
        "move-page": "Ngalih $1",
        "move-page-legend": "Mindhah kaca",
-       "movepagetext": "Formulir ing ngisor iki bakal ngganti jeneng kaca lan ngalihaké kabèh sujarahé nyang jeneng anyar.\nJeneng lawas bakal dadi kaca alihan marang jeneng anyar.\nPanjenengan bisa ndandani kaca alihan sing otomatis nggayut nyang kaca asliné.\nYèn ora, pesthèkaké yèn panjenengan wis mriksa ana-orané kaca alihan [[Special:DoubleRedirects|dhobel]] utawa [[Special:BrokenRedirects|rusak]].\nPanjenengan kudu tanggon saperlu mesthèkaké yèn pranalané menyang kaca sing samesthiné.\n\nÉling-élingen yèn kacané <strong>ora</strong> bakal dilih yèn jeneng sing dituju wis ana kacané, kajaba isiné kaca alihan sing ora ana sujarah besutané.\nIki ateges panjenengan bisa ngganti jeneng kaca bali nyang asliné manawa ana salah, lan panjenengan ora bisa ngamblegi kaca sing wis ana.\n\n<strong>Cathetan:</strong>\nTumindak iki bisa dadi owahan sing ora kinira lan gedhé mungguh ing kaca sing misuwur;\nmangga pesthèkaké dhisik yèn panjenengan mudheng temahané sadurungé mbacutaké.",
-       "movepagetext-noredirectfixer": "Formulir ing ngisor iki bakal ngganti jeneng kaca lan ngalihaké kabèh sujarahé nyang jeneng anyar.\nJeneng lawas bakal dadi kaca alihan marang jeneng anyar.\nPanjenengan kudu yakin yèn wis mriksa ana-orané kaca alihan [[Special:DoubleRedirects|dhobel]] utawa [[Special:BrokenRedirects|rusak]].\nPanjenengan kudu tanggon saperlu mesthèkaké yèn pranalané menyang kaca sing samesthiné.\n\nÉling-élingen yèn kacané <strong>ora</strong> bakal dilih yèn jeneng sing dituju wis ana kacané, kajaba isiné kaca alihan sing ora ana sujarah besutané.\nIki ateges panjenengan bisa ngganti jeneng kaca bali nyang asliné manawa ana salah, lan panjenengan ora bisa ngamblegi kaca sing wis ana.\n\n<strong>Cathetan:</strong>\nTumindak iki bisa dadi owahan sing ora kinira lan gedhé mungguh ing kaca sing misuwur;\nmangga pesthèkaké dhisik yèn panjenengan mudheng temahané sadurungé mbacutaké.",
+       "movepagetext": "Formulir ing ngisor iki bakal ngganti jeneng kaca lan ngalihaké kabèh sajarahé nyang jeneng anyar.\nJeneng lawas bakal dadi kaca alihan marang jeneng anyar.\nPanjenengan bisa ndandani kaca alihan sing otomatis nggayut nyang kaca asliné.\nYèn ora, pesthèkaké yèn panjenengan wis mriksa ana-orané kaca alihan [[Special:DoubleRedirects|dhobel]] utawa [[Special:BrokenRedirects|rusak]].\nPanjenengan kudu tanggon saperlu mesthèkaké yèn pranalané menyang kaca sing samesthiné.\n\nÉling-élingen yèn kacané <strong>ora</strong> bakal dilih yèn jeneng sing dituju wis ana kacané, kajaba isiné kaca alihan sing ora ana sajarah besutané.\nIki ateges panjenengan bisa ngganti jeneng kaca bali nyang asliné manawa ana salah, lan panjenengan ora bisa ngamblegi kaca sing wis ana.\n\n<strong>Cathetan:</strong>\nTumindak iki bisa dadi owahan sing ora kinira lan gedhé mungguh ing kaca sing misuwur;\nmangga pesthèkaké dhisik yèn panjenengan mudheng temahané sadurungé mbacutaké.",
+       "movepagetext-noredirectfixer": "Formulir ing ngisor iki bakal ngganti jeneng kaca lan ngalihaké kabèh sajarahé nyang jeneng anyar.\nJeneng lawas bakal dadi kaca alihan marang jeneng anyar.\nPanjenengan kudu yakin yèn wis mriksa ana-orané kaca alihan [[Special:DoubleRedirects|dhobel]] utawa [[Special:BrokenRedirects|rusak]].\nPanjenengan kudu tanggon saperlu mesthèkaké yèn pranalané menyang kaca sing samesthiné.\n\nÉling-élingen yèn kacané <strong>ora</strong> bakal dilih yèn jeneng sing dituju wis ana kacané, kajaba isiné kaca alihan sing ora ana sajarah besutané.\nIki ateges panjenengan bisa ngganti jeneng kaca bali nyang asliné manawa ana salah, lan panjenengan ora bisa ngamblegi kaca sing wis ana.\n\n<strong>Cathetan:</strong>\nTumindak iki bisa dadi owahan sing ora kinira lan gedhé mungguh ing kaca sing misuwur;\nmangga pesthèkaké dhisik yèn panjenengan mudheng temahané sadurungé mbacutaké.",
        "movepagetalktext": "Menawa sampéyan nyénthang kothak iki, kaca parembugan sing magepokan bakal otomatis dilih nyang sesirah anyar, kajaba kaca parembugané sing dituju wis ana isiné.\n\nYèn mangkéné, sampéyan kudu ngalih utawa nggabung kaca-kaca iku kanthi manual.",
        "moveuserpage-warning": "<strong>Pènget:</strong> Panjenengan iki arep ngalih kaca panganggo. Mangga èlingana yèn mung kacané waé sing bakal dilih, déné panganggoné <em>ora</em> bakal ganti jeneng.",
        "movecategorypage-warning": "<strong>Pélik:</strong> Panjenengan arep ngalih kaca kategori. Tulung gatèkaké yèn mung kacané thok sing bakal dilih déné samubarang kaca sing ana ing kategori lawas <em>ora</em> bakal mèlu dilih nyang kaca kategori anyar.",
        "pageinfo-title": "Inpormasi kanggo \"$1\"",
        "pageinfo-not-current": "Maaf, tidak mungkin memberikan informasi ini ke revisi lama.",
        "pageinfo-header-basic": "Informasi dhasar",
-       "pageinfo-header-edits": "Sujarah besutan",
+       "pageinfo-header-edits": "Sajarah besutan",
        "pageinfo-header-restrictions": "Perlindungan halaman",
        "pageinfo-header-properties": "Properti kaca",
        "pageinfo-display-title": "Sesirah pajangan",
        "fileduplicatesearch-noresults": "Ora tinemu barkas kanthi jeneng \"$1\".",
        "specialpages": "Kaca mirunggan",
        "specialpages-note-top": "Katrangan",
-       "specialpages-note": "* Kaca mirunggan sedhengan.\n* <span class=\"mw-specialpagerestricted\">Kaca mirunggan winatesan.</span>",
        "specialpages-group-maintenance": "Lapuran pangopèn",
        "specialpages-group-other": "Kaca mirunggan liyané",
        "specialpages-group-login": "Mlebu log / nggawé akun",
index 5ba16e7..18b1110 100644 (file)
        "fileduplicatesearch-noresults": "\"$1\"이라는 이름을 가진 파일이 없습니다.",
        "specialpages": "특수 문서 목록",
        "specialpages-note-top": "범례",
-       "specialpages-note": "* 일반 특수 문서입니다.\n* <span class=\"mw-specialpagerestricted\">제한된 특수 문서입니다.</span>",
        "specialpages-group-maintenance": "관리용 목록",
        "specialpages-group-other": "다른 특수 문서",
        "specialpages-group-login": "로그인 / 계정 만들기",
index 4e1a8d2..874f2b5 100644 (file)
        "fileduplicatesearch-noresults": "Et gouf kee Fichier mam Numm \"$1\" fonnt.",
        "specialpages": "Spezialsäiten",
        "specialpages-note-top": "Erklärung",
-       "specialpages-note": "* Normal Spezialsäiten.\n* <span class=\"mw-specialpagerestricted\">Spezialsäite fir Benotzer mat méi Rechter.</span>",
        "specialpages-group-maintenance": "Maintenance-Rapporten",
        "specialpages-group-other": "Aner Spezialsäiten",
        "specialpages-group-login": "Aloggen / Benotzerkont uleeën",
index 9a702b8..41ca9d1 100644 (file)
        "post-expand-template-argument-warning": "Waarsjoewing: dees pagina bevat winnigstes eine sjabloonparameter mit 'n te groete transclusiegruutde.\nDees parameters zeen eweggelaote.",
        "post-expand-template-argument-category": "Pagina's die missende sjabloonillemènte bevatte",
        "parser-template-loop-warning": "D'r is 'ne krinkloup in sjablone geconstateerd: [[$1]]",
+       "template-loop-category": "Pagina's mit sjeblone die zichzelf insloete",
        "parser-template-recursion-depth-warning": "De recursiedeepte veur sjablone is euversjrede ($1)",
        "language-converter-depth-warning": "De deepdjelimiet veure spraokómzètter is euversjreje ($1)",
        "node-count-exceeded-category": "Pagina's wo 't maximaal aantal nodes te väöl is",
-       "node-count-exceeded-warning": "Oppe paasj is 't maximaal aantal nodes te väöl",
+       "node-count-exceeded-warning": "Oppe paasj is 't maximaal aantal nodes behaoltj",
        "expansion-depth-exceeded-category": "Pagina's wo de expansiedeepdje te väöl is",
+       "expansion-depth-exceeded-category-desc": "De pagina geit euver de maximaal oetbreijingsdeepdje.",
        "expansion-depth-exceeded-warning": "De paasj haet te väöl sjablone",
        "parser-unstrip-loop-warning": "Unstriplus gevónje",
        "parser-unstrip-recursion-limit": "Unstriprecursielimiet te väöl ($1)",
+       "converter-manual-rule-error": "'n Fout is óntdèk gewaore in 'ne handjmaesig tougeveudje spraokómzèttingsregel",
        "undo-success": "Hiej onger stuit de teks wo in de verangering ongedaon gemaak is. Controleer veur 't opslaon of 't resultaot gewins is.",
        "undo-failure": "De verangering kòs neet ongedaon gemaak waere waeges angere striedige verangeringe.",
        "undo-norev": "De bewerking kon neet ongedaan gemaak waere, omdat die neet besteet of is verwijderd.",
+       "undo-nochange": "De bewirking liek al óngedaon te zeen gemaak.",
        "undo-summary": "Versie $1 van [[Special:Contributions/$2|$2]] ([[User talk:$2|euverlèk]]) óngedaon gemaak.",
+       "undo-summary-username-hidden": "Drej versie $1 door 'ne verborge gebroeker trögk",
        "cantcreateaccount-text": "'t Aanmake van gebroekers van dit IP-adres ('''$1''') is geblokkeerd door [[User:$3|$3]].\n\nDe door $3 opgegaeve reje is ''$2''",
        "viewpagelogs": "Logbeuk veur dees pagina tuine",
        "nohistory": "Dees pagina is nog neet bewirk.",
        "page_last": "lèste",
        "histlegend": "Verklaoring aafkortinge: (wijz) = versjil mit actueile versie, (vörrige) = versjil mit vörrige versie, K = kleine verangering",
        "history-fieldset-title": "Zeuk nao versies",
-       "history-show-deleted": "Inkel eweggesjaf",
+       "history-show-deleted": "Inkel eweggesjafdje versie",
        "histfirst": "aadste",
        "histlast": "nuujste",
        "historysize": "({{PLURAL:$1|1 byte|$1 bytes}})",
        "history-feed-description": "Bewerkingseuverzich veur dees pagina op de wiki",
        "history-feed-item-nocomment": "$1 op $2",
        "history-feed-empty": "De gevraogde pagina besjteit neet.\nWellich is ze gewis of verplaats.\n[[Special:Search|Doorzeuk de wiki]] veur relevante pagina's.",
+       "history-edit-tags": "Bewirk labels van oetgekaoze versies",
        "rev-deleted-comment": "(bewirkingssamevatting eweggesjaf)",
        "rev-deleted-user": "(gebroeker weggehaold)",
-       "rev-deleted-event": "(actie weggehaold)",
+       "rev-deleted-event": "(logbookregel weggehaold)",
        "rev-deleted-user-contribs": "[gebroeker of IP gewösj - bewèrking verbórge in biedraag]",
        "rev-deleted-text-permission": "Dees bewerking is '''gewusj'''.\nDao kónne details aanwezig zeen in 't [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} wusjlogbook].",
+       "rev-suppressed-text-permission": "Dees paginaversie is <strong>óngerdrók</strong>.\nAchtergrönj zeen te vinjen in 't [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} logbook ven óngerdrökdje versies].",
        "rev-deleted-text-unhide": "Dees versie van de pagina is '''eweggesjaf'''.\nDetails zien meugelik te vinde in 't [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} wislogbook].\nEs beheerder kins se [$1 dees versie bekieke] es se wils.",
        "rev-suppressed-text-unhide": "Dees paginaversie is '''óngerdrök'''.\nAchtergrönj zeen meugelik te vinje in 't [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} logbook ven óngerdrökdje versies].\nEs behierder kèns toe [$1 de versjille bekieken] es se wils.",
        "rev-deleted-text-view": "Dees bewèrking is '''gewösj'''.\nEs beheerder kèns te deze zeen;\ndao kónne details aanwezig zeen in 't [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} wusjlogbook].",
        "revdelete-confirm": "Bevestig des se dit wils doon, des se de consequenties begrieps en des se dit deis in euvereinstömming mit 't geljendj [[{{MediaWiki:Policy-url}}|beleid]].",
        "revdelete-suppress-text": "Versies verbèrge deentj '''allein''' gebroek te waere in de volgende gevalle:\n* Ongepaste perseunlike informatie\n*: ''woonadres, telefoonnummers, Burger Service Nummers, enzovoors.''",
        "revdelete-legend": "Stel zichbaarheidsbeperkinge in",
-       "revdelete-hide-text": "Verberg de bewerkte teks",
+       "revdelete-hide-text": "Versieteks",
        "revdelete-hide-image": "Verberg bestandjsinhoud",
-       "revdelete-hide-name": "Actie en doel verberge",
-       "revdelete-hide-comment": "De bewerkingssamevatting verberge",
+       "revdelete-hide-name": "Verstaek hanjeling en doel",
+       "revdelete-hide-comment": "Bewirkingssamevatting",
        "revdelete-hide-user": "Verberg gebroekersnaam/IP van de gebroeker",
        "revdelete-hide-restricted": "Pas deze beperkinge toe op zowaal beheerders es angere",
        "revdelete-radio-same": "(anger neet)",
        "mergehistory-empty": "Gein inkele versies kinne samegevoeg waere.",
        "mergehistory-done": "$3 {{PLURAL:$3|versie|versies}} van $1 zeen succesvol samegevoeg nao [[:$2]].",
        "mergehistory-fail": "Kan gein gesjiedenis samevoege, lèvver opnuuj de pagina- en tiedparamaeters te controlere.",
+       "mergehistory-fail-bad-timestamp": "Tiedstempel is óngeljig.",
+       "mergehistory-fail-invalid-source": "Brónpagina is óngeljig.",
+       "mergehistory-fail-invalid-dest": "Doelpagina is óngeljig.",
        "mergehistory-no-source": "Bronpagina $1 besteit neet.",
        "mergehistory-no-destination": "Bestömmingspagina $1 besteit neet.",
        "mergehistory-invalid-source": "De bronpagina mot 'ne geldige titel zeen.",
        "grouppage-bot": "{{ns:project}}:Bots",
        "grouppage-sysop": "{{ns:project}}:Beheerders",
        "grouppage-bureaucrat": "{{ns:project}}:Bureaucrate",
-       "grouppage-suppress": "{{ns:project}}:Euverzich",
+       "grouppage-suppress": "{{ns:project}}:Toezich",
        "right-read": "Pagina's bekieke",
        "right-edit": "Pagina's bewerke",
        "right-createpage": "Pagina's aanmake",
        "right-editinterface": "De gebroekersinterface bewerke",
        "right-editusercss": "De CSS-bestande van angere gebroekers bewerke",
        "right-edituserjs": "De JS-bestande van angere gebroekers bewerke",
+       "right-editmyoptions": "Bewirk dien eige veurkäöre",
        "right-rollback": "Snel de letste bewerking(e) van 'n gebroeker van 'n pagina terugdraaie",
        "right-markbotedits": "Teruggedraaide bewerkinge markere es botbewerkinge",
        "right-noratelimit": "Heet gein ti'jdsafhankelijke beperkinge",
        "right-siteadmin": "De database blokkere en weer vriegaeve",
        "right-override-export-depth": "Export paazjes midin geslinkdje paazjes mit 'n deepdje ven 5",
        "right-sendemail": "Versjik e-mail aan anger gebroekers",
+       "grant-generic": "Rechtegroep \"$1\"",
+       "grant-group-page-interaction": "Wirk mit pagina's",
+       "grant-group-file-interaction": "Wirk mit media",
+       "grant-group-watchlist-interaction": "Wirk mit diene volglies",
+       "grant-group-email": "Sjik e-mail",
+       "grant-group-high-volume": "Veur aktiviteite mit hoeag voluum oet",
+       "grant-group-customization": "Aanpassinge en veurkäöre",
+       "grant-group-administration": "Veur behieërdershanjelinge oet",
+       "grant-group-private-information": "Betrach privaatgegaeves euver dich",
+       "grant-group-other": "Divers hanjelinge",
+       "grant-blockusers": "Blokkeer en deblokkeer gebroekers",
+       "grant-createaccount": "Maak gebroekers aan",
+       "grant-createeditmovepage": "Maak, bewirk en verplaats pagina's",
+       "grant-delete": "Wösj pagina's, bewirkinge en logbookregele",
+       "grant-basic": "Basisrechte",
        "newuserlogpage": "Logbook nuuj gebroekers",
        "newuserlogpagetext": "Hiej ónger saton de nuuj ingesjreve gebroekers.",
        "rightslog": "Gebroekersrechtelogbook",
        "action-userrights-interwiki": "gebroekersrechte van gebroekers van anger wiki's te bewerke",
        "action-siteadmin": "de database aaf te sloete of aope te stelle",
        "action-sendemail": "Sjik e-mails",
+       "action-purge": "sjoean dees pagina op",
        "nchanges": "$1 {{PLURAL:$1|bewerking|bewerkinge}}",
+       "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|saer die litste bezeuk}}",
        "enhancedrc-history": "historie",
        "recentchanges": "Lètste verangeringe",
        "recentchanges-legend": "Opties veur recènte verangeringe",
        "recentchanges-label-plusminus": "Dees paginagruuedje is verangerdj mit dit aantaal aan bytes",
        "recentchanges-legend-heading": "<strong>Legenda:</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (zuuch ouch [[Special:NewPages|de nuuj pagina's]])",
+       "recentchanges-submit": "Toean",
+       "rcfilters-legend-heading": "<strong>Lies mit aafkórtinge:</strong>",
+       "rcfilters-activefilters": "Aktief filtjers",
+       "rcfilters-advancedfilters": "Geavenceerdje filtjers",
+       "rcfilters-limit-title": "Te toeane verangeringe",
+       "rcfilters-limit-shownum": "Toean de litste $1 verangeringe",
+       "rcfilters-days-title": "Recènte daag",
+       "rcfilters-hours-title": "Recènte oere",
+       "rcfilters-days-show-days": "$1 {{PLURAL:$1|daag}}",
+       "rcfilters-days-show-hours": "$1 {{PLURAL:$1|oer}}",
+       "rcfilters-quickfilters": "Opgeslage filtjers",
+       "rcfilters-quickfilters-placeholder-title": "Nag gein opgeslage links",
+       "rcfilters-savedqueries-rename": "Herneum",
+       "rcfilters-savedqueries-setdefault": "Stèl in es standerd",
+       "rcfilters-savedqueries-unsetdefault": "Sjaf eweg es standerd",
+       "rcfilters-savedqueries-remove": "Sjaf eweg",
+       "rcfilters-savedqueries-new-name-label": "Naam",
+       "rcfilters-savedqueries-new-name-placeholder": "Besjrief 't doel van de filtjer",
+       "rcfilters-savedqueries-apply-label": "Maak filtjer aan",
+       "rcfilters-savedqueries-cancel-label": "Braek aaf",
+       "rcfilters-savedqueries-add-new-title": "Slaon hujige filtjerinstèllinge op",
+       "rcfilters-restore-default-filters": "Zèt standerd filtjers trögk",
+       "rcfilters-clear-all-filters": "Sjaf alle filtjers eweg",
+       "rcfilters-search-placeholder": "Filtjer recènte verangeringe (blajer of begin mit intikke)",
+       "rcfilters-invalid-filter": "Óngeljige filtjer",
+       "rcfilters-empty-filter": "Gein aktief filtjers. Alle biedrage waere waergaeve.",
+       "rcfilters-filterlist-title": "Filtjers",
+       "rcfilters-filterlist-whatsthis": "Wie wirk dit?",
+       "rcfilters-filterlist-feedbacklink": "Gaef trögksjaking op de nuuj (bèta)filtjers",
+       "rcfilters-highlightbutton-title": "Markeer rizzeltaote",
+       "rcfilters-highlightmenu-title": "Kees 'n kluuer",
+       "rcfilters-highlightmenu-help": "Kees 'n kluuer veur dees eigesjappe oet te lichte",
+       "rcfilters-filterlist-noresults": "Gein filtjers gevónje",
+       "rcfilters-filter-bots-label": "Bot",
+       "rcfilters-tag-prefix-namespace-inverted": "<strong>:neet</strong> $1",
        "rcnotefrom": "{{PLURAL:$5|Verangering|Verangeringe}} saer <strong>$3 óm $4</strong> (maximaal <strong>$1</strong> {{PLURAL:$1|verangering|verangeringe}}).",
        "rclistfrom": "Tuin de verangeringe vanaaf $3 $2",
        "rcshowhideminor": "$1 klein bewèrkinge",
        "rcshowhideanons-show": "Toean",
        "rcshowhideanons-hide": "Versjtaek",
        "rcshowhidepatr": "$1 gecontroleerde bewerkinge",
+       "rcshowhidepatr-show": "Toean",
+       "rcshowhidepatr-hide": "Versjtaek",
        "rcshowhidemine": "$1 mien bewirkinge",
        "rcshowhidemine-show": "Toean",
        "rcshowhidemine-hide": "Versjtaek",
+       "rcshowhidecategorization": "$1 paginacategorisatie",
+       "rcshowhidecategorization-show": "Toean",
+       "rcshowhidecategorization-hide": "Versjtaek",
        "rclinks": "Bekiek de $1 litste verangeringe van de aafgeloupe $2 daag.",
        "diff": "vers",
        "hist": "hist",
        "newpageletter": "N",
        "boteditletter": "b",
        "number_of_watching_users_pageview": "[$1 {{PLURAL:$1|keer|keer}} op 'ne volglies]",
-       "rc_categories": "Beperk tot allein categorieë (sjeij mit 'n \"|\")",
-       "rc_categories_any": "Iddere",
+       "rc_categories": "Bepirk tot categorieë (sjeij mit 'n \"|\")",
+       "rc_categories_any": "Idder vanne gekaozene",
        "rc-change-size-new": "$1 {{PLURAL:$1|byte|bytes}} nao verangering",
        "newsectionsummary": "/* $1 */ nuje subkop",
-       "rc-enhanced-expand": "Details weergaeve (JavaScript verplich)",
+       "rc-enhanced-expand": "Toean details",
        "rc-enhanced-hide": "Details verberge",
        "rc-old-title": "oearsprónkelik aangemaak es \"$1\"",
        "recentchangeslinked": "Volg links",
        "recentchangeslinked-summary": "Dees speciaal pagina tuint de lètste bewirkinge op pagina's die gelink waere vanaaf deze pagina. Pagina's die op [[Special:Watchlist|dien volglies]] staon waere '''vet''' weergegaeve.",
        "recentchangeslinked-page": "Paginanaam:",
        "recentchangeslinked-to": "Verangeringe weergaeve nao de gelinkde pagina's",
+       "recentchanges-page-added-to-category": "[[:$1]] aan categorie tougeveug gewaore",
+       "recentchanges-page-added-to-category-bundled": "[[:$1]] is tougeveug gewaore aan de categorie, [[Special:WhatLinksHere/$1|dees pagina is opgenómme gewaore in anger pagina's]]",
+       "recentchanges-page-removed-from-category": "[[:$1]] is oet de categorie eweggehaoldj gewaore",
+       "recentchanges-page-removed-from-category-bundled": "[[:$1]] is oet de categorie eweggehaoldj gewaore, [[Special:WhatLinksHere/$1|dees pagina is opgenómme gewaore in anger pagina's]]",
+       "autochange-username": "Automatische verangering van MediaWiki",
        "upload": "Upload",
        "uploadbtn": "bestandj uploade",
        "reuploaddesc": "Truuk nao 't uploadformeleer.",
        "unwatchthispage": "Neet mië volge",
        "notanarticle": "Is gein artikel",
        "notvisiblerev": "Bewèrking is verwiederd",
-       "watchlist-details": "D'r {{PLURAL:$1|sjteit ein pagina|sjtaon $1 pagina's}} op dien volglies mit oetzunjering van de euverlèkpagina's.",
+       "watchlist-details": "D'r {{PLURAL:$1|sjteit ein pagina|sjtaon $1 pagina's}} op dien volglies mit de euverlèkpagina's neet mitgetèldj.",
        "wlheader-enotif": "Doe wörs per e-mail gewaarsjuwd",
        "wlheader-showupdated": "Pazjena's die verangerd zeen saers doe ze veur 't lètste bekeeks sjtaon '''vet'''",
        "wlnote": "Hieónger {{PLURAL:$1|steit de lètste verangering|staon de lètste <strong>$1</strong> verangeringe}} van {{PLURAL:$2|'t lètste oer|de lètste <strong>$2</strong> oer}} óp $3 óm $4.",
        "pageinfo-few-watchers": "Minder es  {{PLURAL:$1|eine volger|$1 volgers}}",
        "pageinfo-redirects-name": "Aantaal redireks nao dees pagina",
        "pageinfo-subpages-name": "Subpagina's van dees pagina",
+       "pageinfo-subpages-value": "$1 ($2 {{PLURAL:$2|redirek|redireks}}; $3 {{PLURAL:$3|neet-redirek|neet-redireks}})",
        "pageinfo-firstuser": "Aanmaker",
        "pageinfo-firsttime": "Datum vannen aanmaak",
        "pageinfo-lastuser": "Litste bewirker",
        "pageinfo-recent-edits": "Recènte bewirkinge (binne de aafgeloupe $1)",
        "pageinfo-recent-authors": "Recènte sjrievers",
        "pageinfo-magic-words": "{{PLURAL:$1|Magisch waord|Magische wäörd}} ($1)",
+       "pageinfo-hidden-categories": "Verstaoke {{PLURAL:$1|categorie|categorieje}} ($1)",
        "pageinfo-templates": "{{PLURAL:$1|Gebroek sjebloon|Gebroekde sjeblone}} ($1)",
        "pageinfo-toolboxlink": "Pazjena-infermasie",
        "pageinfo-contentpage": "Getèldj es pagina mit inhawd",
        "fileduplicatesearch-result-n": "'t Bestandj \"$1\" haet {{PLURAL:$2|1 identieke döbbelversie|$2 identiek döbbelversies}}.",
        "fileduplicatesearch-noresults": "d'r Is gei bestandj mitte naam \"$1\" gevónje.",
        "specialpages": "Speciaal pagina's",
-       "specialpages-note": "* Normaal speciaal pagina's\n* <strong class=\"mw-specialpagerestricted\">Beperk toegankelike speciaal pagina's</strong>\n* <span class=\"mw-specialpagecached\">Speciaal pagina's mit allein gegaeves oete cache (meugelik verajerd)</span>",
        "specialpages-group-maintenance": "Óngerhajingsrapporter",
        "specialpages-group-other": "Euverige speciaal pazjena's",
        "specialpages-group-login": "Aanmelje / registrere",
        "htmlform-reset": "Maak verangeringe óngedaon",
        "htmlform-selectorother-other": "Anges",
        "logentry-delete-delete": "$1 {{GENDER:$1|haet}} de pagina $3 gewösj",
-       "logentry-delete-restore": "$1 haet de pagina $3 trögkgezat",
+       "logentry-delete-restore": "$1 {{GENDER:$2|haet}} de pagina $3 ($4) trögkgezatte",
        "logentry-delete-event": "$1 haet de zichbaarheid van {{PLURAL:$5|'ne logbookregel|$5 logbookregels}} van $3 gewiezig: $4",
        "logentry-delete-revision": "$1 {{GENDER:$2|haet}} de zichbaarheid van {{PLURAL:$5|'n versie|$5 versies}} van de pagina $3 verangerdj: $4",
        "logentry-delete-event-legacy": "$1 haet de zichbaarheid van logbookregels van $3 gewiezig",
index dbba31b..5a81bab 100644 (file)
        "fileduplicatesearch-noresults": "Не пронајдов податотека со име „$1“.",
        "specialpages": "Службени страници",
        "specialpages-note-top": "Легенда",
-       "specialpages-note": "* Нормални службени страници.\n* <span class=\"mw-specialpagerestricted\">Ограничени службени страници.</span>",
+       "specialpages-note-restricted": "* Нормални службени страници.\n* <span class=\"mw-specialpagerestricted\">Ограничени службени страници.</span>",
        "specialpages-group-maintenance": "Извештаи за одржување",
        "specialpages-group-other": "Други службени страници",
        "specialpages-group-login": "Најава / направи сметка",
index 508c79e..3145b9a 100644 (file)
@@ -23,7 +23,8 @@
                        "Matma Rex",
                        "Nemo bis",
                        "Mbrt",
-                       "Muhdnurhidayat"
+                       "Muhdnurhidayat",
+                       "Jeluang Terluang"
                ]
        },
        "tog-underline": "Garis bawah pautan:",
        "anontalk": "Perbincangan",
        "navigation": "Pandu arah",
        "and": "&#32;dan",
-       "qbfind": "Cari",
-       "qbbrowse": "Semak imbas",
-       "qbedit": "Sunting",
-       "qbpageoptions": "Laman ini",
-       "qbmyoptions": "Laman-laman saya",
        "faq": "Soalan Lazim",
-       "faqpage": "Project:Soalan Lazim",
        "actions": "Tindakan",
        "namespaces": "Ruang nama",
        "variants": "Kelainan",
        "tagline": "Daripada {{SITENAME}}.",
        "help": "Bantuan",
        "search": "Cari",
-       "search-ignored-headings": " #<!-- jangan usik baris ini --> <pre>\n# Tajuk yang akan diabaikan oleh pencarian.\n# Suntingannya diperlakukan sebaik sahaja laman yang bertajuk ini diindekskan.\n# Anda boleh memaksakan pengindeksan semula laman dengan melakukan suntingan nol (null edit).\n# Sintaks adalah seperti berikut:\n#   * Semuanya dari aksara \"#\" ke hujung baris dikira komen.\n#   * Setiap baris tak kosong adalah tajuk yang setepatnya untuk diabaikan.\nRujukan\nPautan luar\nLihat juga\n #</pre> <!-- jangan usik baris ini -->",
+       "search-ignored-headings": " #<!-- jangan usik baris ini --> <pre>\n# Tajuk yang akan diabaikan oleh pencarian.\n# Suntingannya diperlakukan sebaik sahaja laman yang bertajuk ini diindekskan.\n# Anda boleh memaksakan pengindeksan semula laman dengan melakukan suntingan nol (null edit).\n# Sintaks adalah seperti berikut:\n#   * Semuanya dari aksara \"#\" ke hujung baris dikira komen.\n#   * Setiap baris tak kosong ialah tajuk yang setepatnya untuk diabaikan.\nRujukan\nPautan luar\nLihat juga\n #</pre> <!-- jangan usik baris ini -->",
        "searchbutton": "Cari",
        "go": "Pergi",
        "searcharticle": "Pergi",
        "edit-local": "Sunting huraian tempatan",
        "create": "Cipta",
        "create-local": "Tambahkan huraian tempatan",
-       "editthispage": "Sunting laman ini",
-       "create-this-page": "Cipta laman ini",
        "delete": "Hapus",
-       "deletethispage": "Hapuskan laman ini",
-       "undeletethispage": "Nyahhapuskan laman ini",
        "undelete_short": "Nyahhapus {{PLURAL:$1|satu suntingan|$1 suntingan}}",
        "viewdeleted_short": "Lihat {{PLURAL:$1|satu|$1}} suntingan dihapuskan",
        "protect": "Lindung",
        "protect_change": "ubah",
-       "protectthispage": "Lindungi laman ini",
        "unprotect": "Ubah perlindungan",
-       "unprotectthispage": "Ubah tahap perlindungan laman ini",
        "newpage": "Laman baru",
-       "talkpage": "Bincangkan laman ini",
        "talkpagelinktext": "Perbincangan",
        "specialpage": "Laman khas",
        "personaltools": "Alatan peribadi",
-       "articlepage": "Lihat laman kandungan",
        "talk": "Perbincangan",
        "views": "Rupa",
        "toolbox": "Peralatan",
        "tool-link-userrights": "Tukar kumpulan {{GENDER:$1|pengguna}}",
        "tool-link-userrights-readonly": "Lihat kumpulan {{GENDER:$1|pengguna}}",
        "tool-link-emailuser": "Email {{GENDER:$1|pengguna}} ini",
-       "userpage": "Lihat laman pengguna",
-       "projectpage": "Lihat laman projek",
        "imagepage": "Lihat laman fail",
        "mediawikipage": "Lihat laman pesanan",
        "templatepage": "Lihat laman templat",
        "userlogin-resetpassword-link": "Lupa kata laluan anda?",
        "userlogin-helplink2": "Bantuan untuk log masuk",
        "userlogin-loggedin": "Anda sudah log masuk sebagai {{GENDER:$1|$1}}. Gunakan borang di bawah untuk log masuk sebagai pengguna lain.",
-       "userlogin-reauth": "Anda mesti log masuk sekali lagi untuk mengesahkan bahawa anda adalah {{GENDER:$1|$1}}.",
+       "userlogin-reauth": "Anda mesti log masuk sekali lagi untuk mengesahkan bahawa anda ialah {{GENDER:$1|$1}}.",
        "userlogin-createanother": "Buka satu lagi akaun",
        "createacct-emailrequired": "Alamat e-mel",
        "createacct-emailoptional": "Alamat e-mel (pilihan)",
        "noname": "Nama pengguna tidak sah.",
        "loginsuccesstitle": "Berjaya melog masuk",
        "loginsuccess": "'''Anda telah log masuk ke dalam {{SITENAME}} sebagai \"$1\".'''",
-       "nosuchuser": "Tiada pengguna yang menggunakan nama \"$1\".\nNama pengguna adalah kes sensitif.\nSemak ejaan anda, atau sila [[Special:CreateAccount|membuka akaun baru]].",
+       "nosuchuser": "Tiada pengguna yang menggunakan nama \"$1\".\nNama pengguna adalah sesitif huruf.\nSemak ejaan anda, atau sila [[Special:CreateAccount|membuka akaun baru]].",
        "nosuchusershort": "Pengguna \"$1\" tidak wujud. Sila semak ejaan anda.",
        "nouserspecified": "Sila nyatakan nama pengguna.",
        "login-userblocked": "Pengguna ini disekat. Log masuk tidak dibenarkan.",
        "botpasswords-updated-body": "Kata laluan bot untuk nama bot \"$1\" bagi pengguna \"$2\" telah dikemaskini.",
        "botpasswords-deleted-title": "Kata laluan bot telah dipadam",
        "botpasswords-deleted-body": "Kata laluan bot untuk nama bot \"$1\" bagi pengguna \"$2\" telah dipadam.",
-       "botpasswords-newpassword": "Kata laluan baru untuk log masuk dengan <strong>$1</strong> adalah <strong>$2</strong>. <em>Sila catatkan ini untuk rujukan masa depan.</em> <br> (Untuk bots lama yang memerlukan nama log masuk untuk menjadi sama dengan nama pengguna akhirnya, anda juga boleh menggunakan <strong>$3</strong> sebagai nama pengguna dan <strong>$4</strong> sebagai kata laluan.)",
+       "botpasswords-newpassword": "Kata laluan baru untuk log masuk dengan <strong>$1</strong> adalah <strong>$2</strong>. <em>Sila catatkan ini untuk rujukan masa depan.</em> <br> (Untuk bot-bot lama yang memerlukan nama log masuk agar sama dengan nama pengguna akhirnya, anda juga boleh menggunakan <strong>$3</strong> sebagai nama pengguna dan <strong>$4</strong> sebagai kata laluan.)",
        "botpasswords-no-provider": "BotPasswordsSessionProvider tidak tersedia.",
        "botpasswords-restriction-failed": "Bot sekatan kata laluan menghalang log masuk ini.",
        "botpasswords-invalid-name": "Nama pengguna yang dinyatakan tidak mengandungi pemisah kata laluan bot (\"$1\").",
        "hr_tip": "Garis melintang (gunakan dengan hemat)",
        "summary": "Ringkasan:",
        "subject": "Perkara:",
-       "minoredit": "Ini adalah suntingan kecil",
+       "minoredit": "Ini ialah suntingan kecil",
        "watchthis": "Pantau laman ini",
        "savearticle": "Paparkan Laman",
        "publishpage": "Terbitkan",
        "preview": "Pralihat",
        "showpreview": "Paparkan pralihat",
        "showdiff": "Lihat perubahan",
-       "blankarticle": "<strong>Amaran:</strong> Laman yang anda sedang menciptakan adalah kosong.\nJika akan menklik \"$1\" sekali lagi, laman ini akan diciptakan tanpa sebarang kandungan.",
+       "blankarticle": "<strong>Amaran:</strong> Laman yang sedang anda ciptakan adalah kosong.\nJika anda menklik \"$1\" sekali lagi, laman ini akan diciptakan tanpa sebarang kandungan.",
        "anoneditwarning": "<strong>Amaran:</strong> Anda tidak log masuk. Alamat IP anda akan disiarkan kepada umum jika anda membuat sebarang suntingan. Jika anda <strong>[$1 log masuk]</strong> atau <strong>[$2 membuka akaun]</strong>, suntingan anda akan diatribusikan kepada nama pengguna anda di samping manfaat-manfaat lain.",
        "anonpreviewwarning": "''Anda belum log masuk. Jika anda menyimpan laman ini, alamat IP anda akan direkodkan dalam sejarah penyuntingan laman ini.''",
        "missingsummary": "'''Peringatan:''' Anda tidak menyatakan ringkasan suntingan. Klik '''Simpan''' sekali lagi untuk menyimpan suntingan ini tanpa ringkasan.",
        "accmailtext": "Kata laluan janaan rawak untuk [[User talk:$1|$1]] telah dikirim kepada $2. Anda boleh menukarnya di halaman ''[[Special:ChangePassword|tukar kata laluan]]'' sebaik sahaja log masuk.",
        "newarticle": "(Baru)",
        "newarticletext": "Anda telah mengikuti pautan ke laman yang belum wujud.\nUntuk mencipta laman ini, sila taip dalam kotak di bawah\n(lihat [$1 laman bantuan] untuk maklumat lanjut).\nJika anda tiba di sini secara tak sengaja, hanya klik butang '''back''' pada pelayar anda.",
-       "anontalkpagetext": "----''Ini ialah laman perbincangan bagi pengguna tanpa nama yang belum membuka akaun atau tidak log masuk.\nOleh itu kami terpaksa menggunakan alamat IP untuk mengenal pasti pengguna tersebut. Alamat IP ini boleh dikongsi oleh ramai pengguna.\nSekiranya anda adalah seorang pengguna tanpa nama dan berasa bahawa komen yang tidak kena mengena telah ditujukan kepada anda, sila [[Special:CreateAccount|buka akaun baru]] atau [[Special:UserLogin|log masuk]] untuk mengelakkan sebarang kekeliruan dengan pengguna tanpa nama yang lain.''",
+       "anontalkpagetext": "----''Ini ialah laman perbincangan bagi pengguna tanpa nama yang belum membuka akaun atau tidak log masuk.\nOleh itu, kami terpaksa menggunakan alamat IP untuk mengenal pasti pengguna tersebut. Alamat IP ini boleh dikongsi oleh ramai pengguna.\nSekiranya anda ialah seorang pengguna tanpa nama dan berasa bahawa komen yang tidak kena-mengena telah ditujukan kepada anda, sila [[Special:CreateAccount|buka akaun baru]] atau [[Special:UserLogin|log masuk]] untuk mengelakkan sebarang kekeliruan dengan pengguna tanpa nama yang lain.''",
        "noarticletext": "Laman ini buat masa sekarang tidak berteks. Anda boleh [[Special:Search/{{PAGENAME}}|cari tajuk bagi laman ini]] dalam laman-laman lain, <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} cari log-log yang berkaitan], atau [{{fullurl:{{FULLPAGENAME}}|action=edit}} sunting laman ini]</span>.",
        "noarticletext-nopermission": "Tiada teks dalam laman ini ketika ini.\nAnda boleh [[Special:Search/{{PAGENAME}}|mencari tajuk laman ini]] dalam laman lain,\natau <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} mencari log yang berkaitan]</span>.",
        "missing-revision": "Semakan #$1 pada halaman \"{{FULLPAGENAME}}\" tidak wujud.\n\nHal ini biasanya disebabkan oleh pautan sejarah yang lapuk ke halaman yang sudah dihapuskan.\nButirannya boleh didapati di [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} log penghapusan].",
        "nonunicodebrowser": "'''AMARAN: Pelayar anda tidak mematuhi Unicode. Aksara-aksara bukan ASCII akan dipaparkan dalam kotak sunting sebagai kod perenambelasan.'''",
        "editingold": "'''AMARAN: Anda sedang\nmenyunting sebuah semakan yang sudah ketinggalan zaman.\nJika anda menyimpannya, sebarang perubahan yang dibuat selepas tarikh semakan ini akan hilang.'''",
        "yourdiff": "Perbezaan",
-       "copyrightwarning": "Sila ambil perhatian bahawa semua sumbangan kepada {{SITENAME}} akan dikeluarkan di bawah $2 (lihat $1 untuk butiran lanjut). Jika anda tidak mahu tulisan anda disunting sewenang-wenangnya oleh orang lain dan diedarkan secara bebas, maka jangan kirim di sini.<br />\nAnda juga berjanji bahawa ini adalah hasil kerja tangan anda sendiri, atau disalin daripada domain awam atau mana-mana sumber bebas lain.\n'''JANGAN KIRIM KARYA HAK CIPTA ORANG LAIN TANPA KEBENARAN!'''",
-       "copyrightwarning2": "Sila ambil perhatian bahawa semua sumbangan terhadap {{SITENAME}} boleh disunting, diubah, atau dipadam oleh penyumbang lain. Jika anda tidak mahu tulisan anda disunting sewenang-wenangnya, maka jangan kirim di sini.<br />\nAnda juga berjanji bahawa ini adalah hasil kerja tangan anda sendiri, atau\ndisalin daripada domain awam atau mana-mana sumber bebas lain (lihat $1 untuk butiran lanjut).\n'''JANGAN KIRIM KARYA HAK CIPTA ORANG LAIN TANPA KEBENARAN!'''",
+       "copyrightwarning": "Sila ambil perhatian bahawa semua sumbangan kepada {{SITENAME}} akan dikeluarkan di bawah $2 (lihat $1 untuk butiran lanjut). Jika anda tidak mahu tulisan anda disunting sewenang-wenangnya oleh orang lain dan diedarkan secara bebas, maka jangan kirim di sini.<br />\nAnda juga berjanji bahawa ini ialah hasil kerja tangan anda sendiri, atau disalin daripada domain awam atau mana-mana sumber bebas lain.\n'''JANGAN KIRIM KARYA HAK CIPTA ORANG LAIN TANPA KEBENARAN!'''",
+       "copyrightwarning2": "Sila ambil perhatian bahawa semua sumbangan terhadap {{SITENAME}} boleh disunting, diubah, atau dipadam oleh penyumbang lain. Jika anda tidak mahu tulisan anda disunting sewenang-wenangnya, maka jangan kirim di sini.<br />\nAnda juga berjanji bahawa ini ialah hasil kerja tangan anda sendiri, atau\ndisalin daripada domain awam atau mana-mana sumber bebas lain (lihat $1 untuk butiran lanjut).\n'''JANGAN KIRIM KARYA HAK CIPTA ORANG LAIN TANPA KEBENARAN!'''",
        "editpage-cannot-use-custom-model": "Model kandungan laman ini tidak boleh diubah.",
        "longpageerror": "'''Ralat: Teks yang anda serahkan itu panjangnya {{PLURAL:$1|1|$1}} kilobait, iaitu lebih panjang daripada had maksimum {{PLURAL:$2|1|$2}} kilobait.'''\nOleh itu, ia tidak boleh disimpan.",
        "readonlywarning": "'''Amaran: Pangkalan data ini dikunci untuk tujuan penyelenggaraan , maka anda tidak akan dapat menyimpan suntingan anda buat sekarang.'''\nAnda boleh menyalin tampal teks anda pada fail teks dan menyimpannya untuk lain kali.\n\nPenyelia yang menguncinya memberikan penjelasan ini: $1",
        "revdel-restore": "Tukar kebolehnampakan",
        "pagehist": "Sejarah laman",
        "deletedhist": "Sejarah yang dihapuskan",
-       "revdelete-hide-current": "Ralat menyembunyikan item bertarikh $2, $1: ini adalah versi semasa.\nIa tidak dapat disembunyikan.",
+       "revdelete-hide-current": "Ralat menyembunyikan item bertarikh $2, $1: Ini ialah versi semasa.\nIa tidak dapat disembunyikan.",
        "revdelete-show-no-access": "Ralat menunjukkan item bertarikh $2, $1: item ini telah ditanda \"larangan\".\nAnda tidak memiliki capaian padanya.",
        "revdelete-modify-no-access": "Ralat menyunting item bertarikh $2, $1: item ini telah ditanda \"larangan\".\nAnda tidak memiliki capaian padanya.",
        "revdelete-modify-missing": "Ralat menyunting item ID $1: ia tiada dalam pangkalan data!",
        "revdelete-edit-reasonlist": "Ubah sebab-sebab hapus",
        "revdelete-offender": "Pengarang semakan:",
        "suppressionlog": "Log penahanan",
-       "suppressionlogtext": "Berikut adalah senarai penghapusan dan sekatan yang melibatkan kandungan yang telah disembunyikan daripada penyelia.\nLihat [[Special:BlockList|senarai sekatan]] untuk senarai larangan dan sekatan semasa.",
+       "suppressionlogtext": "Berikut ialah senarai penghapusan dan sekatan yang melibatkan kandungan yang telah disembunyikan daripada penyelia.\nLihat [[Special:BlockList|senarai sekatan]] untuk senarai larangan dan sekatan semasa.",
        "mergehistory": "Gabungkan sejarah laman",
        "mergehistory-header": "Anda boleh menggabungkan semua semakan dalam sejarah bagi sesebuah laman sumber ke dalam laman lain.\nSila pastikan bahawa perubahan ini akan mengekalkan kesinambungan sejarah laman.\n\n'''Setidak-tidaknya semakan semasa bagi laman sumber akan ditinggalkan.'''",
        "mergehistory-box": "Gabungkan semakan bagi dua laman:",
        "prefs-help-gender": "Pilihan: Digunakan oleh perisian ini untuk merujuk jantina anda dengan betul. Maklumat ini akan didedahkan kepada awam.",
        "email": "E-mel",
        "prefs-help-realname": "Nama sebenar adalah tidak wajib.\n\nJika dinyatakan, ia akan digunakan untuk mengiktiraf karya anda.",
-       "prefs-help-email": "Alamat e-mail adalah tidak wajib, tapi diperlukan untuk set semula kata laluan jika anda terlupa kata laluan anda.",
+       "prefs-help-email": "Alamat e-mel adalah tidak wajib, tapi diperlukan untuk set semula kata laluan jika anda terlupa kata laluan anda.",
        "prefs-help-email-others": "Anda juga boleh memilih untuk membolehkan pengguna lain menghubungi anda melalui e-mel melalui sebuah pautan pada laman pengguna atau perbincangan anda.\nAlamat e-mel anda tidak didedahkan apabila pengguna lain menghubungi anda.",
        "prefs-help-email-required": "Alamat e-mel adalah wajib.",
        "prefs-info": "Maklumat asas",
        "saveusergroups": "Simpan Kumpulan Pengguna",
        "userrights-groupsmember": "Ahli bagi:",
        "userrights-groupsmember-auto": "Ahli automatik bagi:",
-       "userrights-groups-help": "Anda boleh mengubah keahlian kumpulan bagi pengguna ini:\n* Petak yang bertanda bererti pengguna tersebut adalah ahli kumpulan itu.\n* Petak yang tidak bertanda bererti bahawa pengguna tersebut bukan ahli kumpulan itu.\n* Tanda bintang (*) menandakan bahawa anda tidak boleh melucutkan keahlian pengguna tersebut setelah anda melantiknya, dan begitulah sebaliknya.",
+       "userrights-groups-help": "Anda boleh mengubah keahlian kumpulan bagi pengguna ini:\n* Petak yang bertanda bererti pengguna tersebut ialah ahli kumpulan itu.\n* Petak yang tidak bertanda bererti bahawa pengguna tersebut bukan ahli kumpulan itu.\n* Tanda bintang (*) menandakan bahawa anda tidak boleh melucutkan keahlian pengguna tersebut setelah anda melantiknya, dan begitulah sebaliknya.",
        "userrights-reason": "Sebab:",
        "userrights-no-interwiki": "Anda tidak mempunyai keizinan untuk mengubah hak-hak pengguna di wiki lain.",
        "userrights-nodatabase": "Pangkalan data $1 tiada atau bukan tempatan.",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (lihat juga [[Special:NewPages|senarai laman baru]])",
        "rcfilters-filter-pageedits-description": "Suntingan kandungan wiki, perbincangan, huraian kategori…",
        "rcfilters-filter-logactions-description": "Tindakan pentadbiran, pembuatan akaun, penghapusan halaman, muat naik…",
-       "rcnotefrom": "Yang berikut adalah {{PLURAL:$5|suntingan|suntingan-suntingan}} sejak <strong>$3, $4</strong> (selebihi <strong>$1</strong> dipaparkan).",
+       "rcnotefrom": "Yang berikut ialah {{PLURAL:$5|suntingan|suntingan-suntingan}} sejak <strong>$3, $4</strong> (selebihi <strong>$1</strong> dipaparkan).",
        "rclistfrom": "Paparkan perubahan sejak $3 $2",
        "rcshowhideminor": "$1 suntingan kecil",
        "rcshowhideminor-show": "Paparkan",
        "filetype-mime-mismatch": "Sambungan fail \".$1\" tidak padan dengan jenis MIME fail ($2).",
        "filetype-badmime": "Memuat naik fail jenis MIME \"$1\" adalah tidak dibenarkan.",
        "filetype-bad-ie-mime": "Fail ini tidak boleh dimuat naik kerana Internet Explorer mengesannya sebagai fail jenis \"$1\" yang tidak dibenarkan dan berbahaya.",
-       "filetype-unwanted-type": "'''\".$1\"''' adalah jenis fail yang tidak dikehendaki. {{PLURAL:$3|Jenis|Jenis-jenis}} fail yang diutamakan ialah $2.",
-       "filetype-banned-type": "'''\".$1\"''' adalah {{PLURAL:$4|jenis|jenis-jenis}} fail yang dilarang. {{PLURAL:$3|Jenis|Jenis-jenis}} fail yang dibenarkan ialah $2.",
+       "filetype-unwanted-type": "'''\".$1\"''' ialah jenis fail yang tidak dikehendaki. {{PLURAL:$3|Jenis|Jenis-jenis}} fail yang diutamakan ialah $2.",
+       "filetype-banned-type": "<strong>\".$1\"</strong> ialah {{PLURAL:$4|jenis|jenis-jenis}} fail yang dilarang. {{PLURAL:$3|Jenis|Jenis-jenis}} fail yang dibenarkan ialah $2.",
        "filetype-missing": "Fail ini tidak mempunyai sambungan (contohnya \".jpg\").",
        "empty-file": "Fail yang anda serahkan adalah kosong.",
        "file-too-large": "Fail yang anda serahkan adalah terlalu besar.",
        "fileexists": "Sebuah fail dengan nama ini sudah wujud. Sila semak <strong>[[:$1]]</strong> sekiranya {{GENDER:|anda}} tidak pasti jika anda mahu menukarnya.\n[[$1|thumb]]",
        "filepageexists": "Laman penerangan untuk fail ini telah pun dicipta di <strong>[[:$1]]</strong>, tetapi tiada fail dengan nama ini wujud.\nRingkasan yang anda masukkan tidak akan muncul di laman penerangan tersebut. Untuk memastikannya muncul, anda perlu menyuntingnya secara manual.\n[[$1|thumb]]",
        "fileexists-extension": "Sebuah fail dengan nama yang serupa sudah wujud: [[$2|thumb]]\n* Nama fail yang hendak dimuat naik: <strong>[[:$1]]</strong>\n* Nama fail yang sudah sedia ada: <strong>[[:$2]]</strong>\nAdakah anda mungkin mahu menggunakan nama yang lebih tersendiri?",
-       "fileexists-thumbnail-yes": "Fail ini kelihatan seperti sebuah imej yang telah dikecilkan ''(gambar kenit)''. [[$1|thumb]]\nSila semak fail <strong>[[:$1]]</strong>.\nJika fail yang disemak itu adalah sama dengan yang saiz asal, maka anda tidak perlu memuat naik gambar kenit tambahan.",
-       "file-thumbnail-no": "Nama fail ini bermula dengan <strong>$1</strong>.\nBarangkali ia adalah sebuah imej yang telah dikecilkan ''(gambar kenit)''.\nJika anda memiliki imej ini dalam leraian penuh, sila muat naik fail tersebut. Jika tidak, sila tukar nama fail ini.",
+       "fileexists-thumbnail-yes": "Fail ini kelihatan seperti sebuah imej yang telah dikecilkan <em>(gambar kenit)</em>.\n[[$1|thumb]]\nSila semak fail <strong>[[:$1]]</strong>.\nJika fail yang disemak itu adalah sama dengan yang saiz asal, maka anda tidak perlu memuat naik gambar kenit tambahan.",
+       "file-thumbnail-no": "Nama fail ini bermula dengan <strong>$1</strong>.\nBarangkali ia ialah sebuah imej yang telah dikecilkan <em>(gambar kenit)</em>.\nJika anda memiliki imej ini dalam leraian penuh, sila muat naik fail tersebut. Jika tidak, sila tukar nama fail ini.",
        "fileexists-forbidden": "Sebuah fail dengan nama ini telah pun wujud, dan tidak boleh ditulis ganti. Jika anda masih mahu memuat naik fail ini, sila berundur dan muat naik fail ini dengan nama lain. [[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "Sebuah fail dengan nama ini telah pun wujud dalam gedung fail kongsi. Jika anda masih mahu memuat naik fail ini, sila kembali ke borang muat naik dan gunakan nama lain. [[File:$1|thumb|center|$1]]",
-       "file-exists-duplicate": "Fail ini adalah salinan bagi {{PLURAL:$1|fail|fail-fail}} berikut:",
+       "file-exists-duplicate": "Fail ini ialah salinan bagi {{PLURAL:$1|fail|fail-fail}} berikut:",
        "file-deleted-duplicate": "Sebuah fail yang serupa dengan fail ini ([[:$1]]) telah pun dihapuskan sebelum ini. Anda seharusnya memeriksa sejarah penghapusan fail itu terlebih dahulu sebelum memuat naiknya sekali lagi.",
        "file-deleted-duplicate-notitle": "Satu fail yang seiras dengan fail ini telah dihapuskan dahulu, maka judulnya telah disekat. Anda harus meminta sesiapa yang boleh melihat data fail yang disekat untuk meneliti situasinya sebelum cuba memuat naiknya semula.",
        "uploadwarning": "Amaran muat naik",
        "lockmanager-fail-svr-release": "Selak-selak tidak dapat dikeluarkan di pelayan $1.",
        "zip-file-open-error": "Wujud ralat ketika membuka fail untuk pemeriksaan ZIP.",
        "zip-wrong-format": "Fail yang dinyatakan bukan fail ZIP.",
-       "zip-bad": "Fail ini adalah fail ZIP rosak atau tidak dapat dibaca.\nIa tidak dapat diperiksa dengan betul demi keselamatan.",
-       "zip-unsupported": "Fail ini adalah fail ZIP yang menggunakan ciri-ciri ZIP tidak disokong oleh MediaWiki. \nIa tidak dapat diperiksa dengan betul demi keselamatan.",
+       "zip-bad": "Fail ini ialah fail ZIP rosak atau tidak dapat dibaca.\nIa tidak dapat diperiksa dengan betul demi keselamatan.",
+       "zip-unsupported": "Fail ini ialah fail ZIP yang menggunakan ciri-ciri ZIP tidak disokong oleh MediaWiki. \nIa tidak dapat diperiksa dengan betul demi keselamatan.",
        "uploadstash": "Stor muat naik",
        "uploadstash-summary": "Laman ini menyediakan capaian kepada fail-fail yang dimuat naik (atau sedang dimuat naik) tapi belum diterbitkan ke dalam wiki. Fail-fail ini tidak dapat dilihat oleh sesiapa melainkan pengguna yang memuatnaiknya.",
        "uploadstash-clear": "Bersihkan fail-fail sorokan",
        "nolinkstoimage": "Tiada laman yang mengandungi pautan ke fail ini.",
        "morelinkstoimage": "Lihat [[Special:WhatLinksHere/$1|semua pautan]] ke fail ini.",
        "linkstoimage-redirect": "$1 (lencongan fail) $2",
-       "duplicatesoffile": "{{PLURAL:$1|Fail|$1 buah fail}} berikut adalah salinan bagi fail ini ([[Special:FileDuplicateSearch/$2|butiran lanjut]]):",
+       "duplicatesoffile": "{{PLURAL:$1|Fail|$1 buah fail}} berikut ialah salinan bagi fail ini ([[Special:FileDuplicateSearch/$2|butiran lanjut]]):",
        "sharedupload": "Fail ini daripada $1 dan boleh digunakan oleh projek lain.",
        "sharedupload-desc-there": "Fail ini dari $1 dan mungkin digunakan oleh projek lain.\nSila lihat [$2 laman penerangan fail] untuk maklumat lanjut.",
        "sharedupload-desc-here": "Fail ini dari $1 dan mungkin digunakan oleh projek lain.\nPenerangan pada [$2 laman penerangan failnya] di sana ditunjukkan di bawah.",
        "activeusers-from": "Tunjukkan pengguna bermula pada:",
        "activeusers-noresult": "Tiada pengguna dijumpai.",
        "listgrouprights": "Hak kumpulan pengguna",
-       "listgrouprights-summary": "Berikut adalah senarai kumpulan pengguna yang ditubuhkan di wiki ini, dengan hak-hak mereka masing-masing.\nMungkin terdapat [[{{MediaWiki:Listgrouprights-helppage}}|maklumat tambahan]] mengenai setiap hak.",
+       "listgrouprights-summary": "Berikut ialah senarai kumpulan pengguna yang ditubuhkan di wiki ini, dengan hak-hak mereka masing-masing.\nMungkin terdapat [[{{MediaWiki:Listgrouprights-helppage}}|maklumat tambahan]] mengenai setiap hak.",
        "listgrouprights-key": "Petunjuk:\n* <span class=\"listgrouprights-granted\">Hak ditunaikan</span>\n* <span class=\"listgrouprights-revoked\">Hak dibatalkan</span>",
        "listgrouprights-group": "Kumpulan",
        "listgrouprights-rights": "Hak",
        "trackingcategories-name": "Nama pesanan",
        "trackingcategories-desc": "Kriteria kemasukan kategori",
        "noindex-category-desc": "Laman tidak didaftar oleh robot kerana ia mempunyai kata ajaib <code><nowiki>__NOINDEX__</nowiki></code> padanya dan terdapat dalam ruang nama di mana tanda tersebut adalah dibenarkan.",
-       "index-category-desc": "Laman mempunyai <code><nowiki>__INDEX__</nowiki></code> padanya (dan terdapat dalam ruang nama di mana tanda tersebut dibenarkan), dan oleh itu adalah didaftar oleh robot di mana ia biasanya tidak akan.",
+       "index-category-desc": "Laman mempunyai <code><nowiki>__INDEX__</nowiki></code> padanya (dan terdapat dalam ruang nama di mana tanda tersebut dibenarkan), dan oleh itu ia didaftar oleh robot bilamana ia biasanya tidak akan didaftar.",
        "post-expand-template-inclusion-category-desc": "Saiz laman melebihi <code>$wgMaxArticleSize</code> setelah semua templat telah dikembangkan; oleh itu beberapa templat tidak dikembangkan.",
        "post-expand-template-argument-category-desc": "Laman menjadi lebih besar daripada <code>$wgMaxArticleSize</code> setelah mengembangkan sebuah hujah templat (sebarang dalam tiga kurungan, seperti <code>{{{Foo}}}</code>.)",
        "expensive-parserfunction-category-desc": "Laman menggunakan terlalu banyak fungsi parser yang mahal (seperti <code>#ifexist</code>). Lihat [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgExpensiveParserFunctionLimit Manual:$wgExpensiveParserFunctionLimit].",
        "rollbacklinkcount": "mengundurkan $1 {{PLURAL:$1|suntingan}}",
        "rollbacklinkcount-morethan": "mengundurkan lebih daripada $1 {{PLURAL:$1|suntingan}}",
        "rollbackfailed": "Pengunduran gagal",
-       "cantrollback": "Suntingan tersebut tidak dapat dibalikkan: penyumbang terakhir adalah satu-satunya pengarang bagi rencana ini.",
+       "cantrollback": "Suntingan tersebut tidak dapat dibalikkan: penyumbang terakhir ialah satu-satunya pengarang bagi rencana ini.",
        "alreadyrolled": "Suntingan terakhir bagi [[:$1]] oleh [[Pengguna:$2|$2]] ([[Perbincangan pengguna:$2|bincang]]{{int:pipe-separator}}[[Khas:Sumbangan/$2|{{int:contribslink}}]]) tidak dapat dibalikkan; seorang pengguna lain sudahpun menyunting atau membalikkan laman itu.\n\nSuntingan terakhir kepada laman ini telah dibuat oleh [[Pengguna:$3|$3]] ([[Perbincangan pengguna:$3|bincang]]{{int:pipe-separator}}[[Khas:Sumbangan/$3|{{int:contribslink}}]]).",
        "editcomment": "Ringkasan sutingan: <em>$1</em>.",
        "revertpage": "Membalikkan suntingan oleh [[Special:Contributions/$2|$2]] ([[User talk:$2|Perbincangan]]) kepada versi terakhir oleh [[User:$1|$1]]",
        "protect_expiry_old": "Waktu tamat telah berlalu.",
        "protect-unchain-permissions": "Aktifkan pilihan perlindungan selanjutnya",
        "protect-text": "Anda boleh melihat dan menukar peringkat perlindungan bagi laman '''$1'''.",
-       "protect-locked-blocked": "Anda telah disekat, justeru tidak boleh menukar peringkat perlindungan.\nIni adalah tetapan semasa bagi laman '''$1''':",
-       "protect-locked-dblock": "Anda tidak boleh menukar peringkat perlindungan kerana pangkalan data sedang dikunci.\nIni adalah tetapan semasa bagi laman '''$1''':",
-       "protect-locked-access": "Anda tidak mempunyai keizinan untuk menukar peringkat perlindungan.\nIni adalah tetapan semasa bagi laman '''$1''':",
+       "protect-locked-blocked": "Anda telah disekat, justeru tidak boleh menukar peringkat perlindungan.\nIni ialah tetapan semasa bagi laman '''$1''':",
+       "protect-locked-dblock": "Anda tidak boleh menukar peringkat perlindungan kerana pangkalan data sedang dikunci.\nIni ialah tetapan semasa bagi laman '''$1''':",
+       "protect-locked-access": "Anda tidak mempunyai keizinan untuk menukar peringkat perlindungan.\nIni ialah tetapan semasa bagi laman '''$1''':",
        "protect-cascadeon": "Laman ini dilindungi kerana ia terkandung dalam {{PLURAL:$1|laman|laman-laman}} berikut, yang dilindungi secara melata. Penukaran peringkat perlindungan laman ini tidak akan menjejaskan perlindungan melata tersebut.",
        "protect-default": "Benarkan semua pengguna",
        "protect-fallback": "Benarkan pengguna yang berizin \"$1\" sahaja",
        "change-blocklink": "ubah sekatan",
        "contribslink": "sumb.",
        "emaillink": "hantar e-mel",
-       "autoblocker": "Disekat secara automatik kerana alamat IP anda baru digunakan oleh \"[[User:$1|$1]]\". Sebab yang diberi adalah: \"$2\"",
+       "autoblocker": "Disekat secara automatik kerana alamat IP anda baru digunakan oleh \"[[User:$1|$1]]\". Sebab yang diberi ialah: \"$2\"",
        "blocklogpage": "Log sekatan",
        "blocklog-showlog": "Pengguna ini pernah disekat sebelum ini. Log sekatan disediakan di bawah sebagai rujukan:",
        "blocklog-showsuppresslog": "Pengguna ini pernah disekat dan tersembunyi sebelum ini.\nLog sekatan disediakan di bawah sebagai rujukan:",
        "blocklogentry": "menyekat [[$1]] sehingga $2 $3",
        "reblock-logentry": "menukar tetapan sekatan [[$1]] yang tamat pada $2 $3",
-       "blocklogtext": "Ini adalah log bagi tindakan menyekat dan menyahsekat pengguna.\nAlamat-alamat IP yang disekat secara automatik tidak disenaraikan di sini.\nSila lihat juga [[Special:BlockList|senarai sekatan]] untuk senarai larangan dan sekatan yang sedang berkuatkuasa.",
+       "blocklogtext": "Ini ialah log bagi tindakan menyekat dan menyahsekat pengguna.\nAlamat-alamat IP yang disekat secara automatik tidak disenaraikan di sini.\nSila lihat juga [[Special:BlockList|senarai sekatan]] untuk senarai larangan dan sekatan yang sedang berkuat kuasa.",
        "unblocklogentry": "menyahsekat $1",
        "block-log-flags-anononly": "pengguna tanpa nama sahaja",
        "block-log-flags-nocreate": "pembukaan akaun dimatikan",
        "proxyblockreason": "Alamat IP anda telah disekat kerana ia merupakan proksi terbuka.\nSila hubungi penyedia perkhidmatan Internet anda atau pihak sokongan teknikal dan beritahu mereka mengenai masalah keselamatan yang berat ini.",
        "sorbsreason": "Alamat IP anda telah disenaraikan sebagai proksi terbuka dalam DNSBL yang digunakan oleh {{SITENAME}}.",
        "sorbs_create_account_reason": "Alamat IP anda telah disenaraikan sebagai proksi terbuka dalam DNSBL yang digunakan oleh {{SITENAME}}. Oleh itu, anda tidak dibenarkan membuka akaun baru.",
-       "xffblockreason": "Alamat IP yang terdapat dalam pengepala X-Forwarded-For, sama ada milik anda ataupun pelayan proksi yang anda gunakan, telah disekat. Sebab asal sekatan adalah: $1",
+       "xffblockreason": "Alamat IP yang terdapat dalam pengepala X-Forwarded-For, sama ada milik anda ataupun pelayan proksi yang anda gunakan, telah disekat. Sebab asal sekatan ialah: $1",
        "cant-see-hidden-user": "Pengguna yang anda cuba sekat telahpun disekat dan tersorok.\nMemandangkan anda tidak mempunyai hak untuk menyorokkan pengguna, anda tidak boleh melihat atau menyunting sekatan pengguna tersebut.",
        "ipbblocked": "Anda tidak boleh menyekat atau menyahsekat pengguna lain kerana anda sendiri telah disekat",
        "ipbnounblockself": "Anda tidak dibenarkan menyahsekat diri sendiri",
        "tooltip-ca-nstab-main": "Lihat laman kandungan",
        "tooltip-ca-nstab-user": "Lihat laman pengguna",
        "tooltip-ca-nstab-media": "Lihat laman media",
-       "tooltip-ca-nstab-special": "Ini adalah laman khas yang tidak boleh disunting.",
+       "tooltip-ca-nstab-special": "Ini ialah laman khas yang tidak boleh disunting.",
        "tooltip-ca-nstab-project": "Lihat laman projek",
        "tooltip-ca-nstab-image": "Lihat laman imej",
        "tooltip-ca-nstab-mediawiki": "Lihat pesanan sistem",
        "saturday-at": "Sabtu $1",
        "sunday-at": "Ahad $1",
        "yesterday-at": "Semalam $1",
-       "bad_image_list": "Berikut adalah format yang digunakan:\n\nHanya item senarai (baris yang dimulakan dengan *) diambil kira. Pautan pertama pada sesebuah baris mestilah merupakan pautan ke sebuah imej rosak.\nSebarang pautan berikutnya pada baris yang sama dikira sebagai pengecualian (rencana yang dibenarkan disertakan imej).",
+       "bad_image_list": "Berikut ialah format yang digunakan:\n\nHanya item senarai (baris yang dimulakan dengan *) diambil kira. Pautan pertama pada sesebuah baris mestilah merupakan pautan ke sebuah imej rosak.\nSebarang pautan berikutnya pada baris yang sama dikira sebagai pengecualian (rencana yang dibenarkan disertakan imej).",
        "metadata": "Metadata",
        "metadata-help": "Fail ini mengandungi maklumat tambahan daripada kamera digital atau pengimbas yang digunakan untuk menghasilkannya. Jika fail ini telah diubah suai daripada rupa asalnya, beberapa butiran dalam maklumat ini mungkin sudah tidak relevan.",
        "metadata-expand": "Tunjukkan butiran penuh",
        "version-poweredby-others": "penyumbang-penyumbang lain",
        "version-poweredby-translators": "para penterjemah translatewiki.net",
        "version-credits-summary": "Kami ingin mengucapkan sekalung budi kepada mereka yang berikut atas sumbangan mereka keada [[Special:Version|MediaWiki]].",
-       "version-license-info": "MediaWiki adalah perisian bebas; anda boleh mengedarkannya semula dan/atau mengubah suainya di bawah terma-terma Lesen Awam GNU sebagai mana yang telah diterbitkan oleh Yayasan Perisian Bebas, sama ada versi 2 bagi Lesen tersebut, atau (berdasarkan pilihan anda) mana-mana versi selepasnya.\n\nMediaWiki diedarkan dengan harapan bahawa ia berguna, tetapi TANPA SEBARANG WARANTI; hatta waranti yang tersirat bagi KEBOLEHDAGANGAN mahupun KESESUAIAN UNTUK TUJUAN TERTENTU. Sila lihat Lesen Awam GNU untuk butiran lanjut.\n\nAnda patut telah menerima [{{SERVER}}{{SCRIPTPATH}}/COPYING sebuah salinan bagi Lesen Awam GNU] bersama-sama dengan atur cara ini; jika tidak, tulis ke Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA atau [//www.gnu.org/licenses/old-licenses/gpl-2.0.html baca dalam talian].",
+       "version-license-info": "MediaWiki ialah perisian bebas; anda boleh mengedarkannya semula dan/atau mengubah suainya di bawah terma-terma Lesen Awam GNU sebagai mana yang telah diterbitkan oleh Yayasan Perisian Bebas, sama ada versi 2 bagi Lesen tersebut, atau (berdasarkan pilihan anda) mana-mana versi selepasnya.\n\nMediaWiki diedarkan dengan harapan bahawa ia berguna, tetapi TANPA SEBARANG WARANTI; hatta waranti yang tersirat bagi KEBOLEHDAGANGAN mahupun KESESUAIAN UNTUK TUJUAN TERTENTU. Sila lihat Lesen Awam GNU untuk butiran lanjut.\n\nAnda patut telah menerima [{{SERVER}}{{SCRIPTPATH}}/COPYING sebuah salinan bagi Lesen Awam GNU] bersama-sama dengan atur cara ini; jika tidak, tulis kepada Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA atau [//www.gnu.org/licenses/old-licenses/gpl-2.0.html baca dalam talian].",
        "version-software": "Perisian yang dipasang",
        "version-software-product": "Produk",
        "version-software-version": "Versi",
        "fileduplicatesearch-noresults": "Tidak ada gambar-gambar dengan nama \"$1\" dijumpai.",
        "specialpages": "Laman khas",
        "specialpages-note-top": "Petunjuk",
-       "specialpages-note": "* Laman khas biasa.\n* <span class=\"mw-specialpagerestricted\">Laman khas terhad.</span>",
        "specialpages-group-maintenance": "Laporan penyenggaraan",
        "specialpages-group-other": "Laman khas lain",
        "specialpages-group-login": "Log masuk / buka akaun",
        "right-pagelang": "Mengubah bahasa laman",
        "action-pagelang": "mengubah bahasa laman",
        "log-name-pagelang": "Log perubahan bahasa",
-       "log-description-pagelang": "Ini adalah log untuk perubahan-perubahan bahasa laman.",
+       "log-description-pagelang": "Ini ialah log untuk perubahan-perubahan bahasa laman.",
        "logentry-pagelang-pagelang": "$1 telah {{GENDER:$2|mengubahkan}} bahasa untuk laman $3 dari $4 ke $5.",
        "default-skin-not-found": "Maaf, tidak terdapat rupa asali wiki anda yang tertakrif dalam <code dir=\"ltr\">$wgDefaultSkin</code> sebagai <code>$1</code>.\n\nNampaknya pemasangan anda merangkumi {{PLURAL:$4|rupa|rupa-rupa}} yang berikut. Rujuk [https://www.mediawiki.org/wiki/Manual:Skin_configuration Manual: Skin configuration] untuk cara-cara membolehkan {{PLURAL:$4|rupa tersebut|rupa-rupa tersebut serta memilih rupa asali}}.\n\n$2\n\n; Jika anda baru memasang MediaWiki:\n: Mungkin anda memasangnya dari git, atau terus dari kod sumber dengan menggunakan suatu kaedah lain. Perkara ini dijangka. Cuba pasang beberapa rupa dari [https://www.mediawiki.org/wiki/Category:All_skins direktori rupa mediawiki.org], dengan:\n:* Memuat turun [https://www.mediawiki.org/wiki/Download pemasang tarball] yang datang dengan beberapa rupa dan sambungan. Anda boleh menyalin-tampal direktori <code>skins/</code> daripadanya.\n:* Memuatkan satu persatu tarball rupa dari [https://www.mediawiki.org/wiki/Special:SkinDistributor mediawiki.org].\n:* [https://www.mediawiki.org/wiki/Download_from_Git#Using_Git_to_download_MediaWiki_skins Menggunakan Git untuk memuat turun rupa].\n: Tindakan ini seharusnya tidak mengganggu repositori git anda jika anda seorang pembangun MediaWiki.\n\n; Jika anda baru menaik taraf MediaWiki:\n: MediaWiki 1.24 ke atas tidak lagi membolehkan  secara automatik rupa-rupa yang terpasang dari luaran (rujuk [https://www.mediawiki.org/wiki/Manual:Skin_autodiscovery Manual: Skin autodiscovery]). Anda boleh menampalkan {{PLURAL:$5|baris|baris-baris}} berikut kepada <code>LocalSettings.php</code> untuk membolehkan {{PLURAL:$5|rupa|semua rupa}} yang terpasang:\n\n<pre dir=\"ltr\">$3</pre>\n\n; Jika anda baru mengubahsuai <code>LocalSettings.php</code>:\n: Semak nama-nama rupa untuk kesilapan ejaan.",
        "default-skin-not-found-no-skins": "Maaf, tidak terdapat rupa asali wiki anda yang tertakrif dalam <code dir=\"ltr\">$wgDefaultSkin</code> sebagai <code>$1</code>.\n\nTiadanya rupa yang terpasang.\n\n; Jika anda baru memasang atau menaik taraf MediaWiki:\n: Mungkin anda memasangnya dari git, atau terus dari kod sumber dengan menggunakan suatu kaedah lain. Perkara ini dijangka. MediaWiki 1.24 ke atas tidak menyertakan sebarang rupa dalam repositori utama.  Cuba pasang beberapa rupa dari [https://www.mediawiki.org/wiki/Category:All_skins direktori rupa mediawiki.org], dengan:\n:* Memuatkan tarball rupa satu persatu dari [https://www.mediawiki.org/wiki/Special:SkinDistributor mediawiki.org].\n:* [https://www.mediawiki.org/wiki/Download_from_Git#Using_Git_to_download_MediaWiki_skins Menggunakan Git untuk memuat turun rupa].\n: Tindakan ini seharusnya tidak mengganggu repositori git anda jika anda seorang pembangun MediaWiki. Rujuk [https://www.mediawiki.org/wiki/Manual:Skin_configuration Manual: Skin configuration] untuk cara-cara membolehkan penggunaan rupa-rupa serta memilih rupa asali.",
index 18af297..911fc80 100644 (file)
        "fileduplicatesearch-noresults": "Nisciuno file chiamato \"$1\" è stato accucchiato.",
        "specialpages": "Paggene speciale",
        "specialpages-note-top": "Liggenda",
-       "specialpages-note": "* Paggene speciale normale.\n* <span class=\"mw-specialpagerestricted\">Paggene speciale ch' 'e restriziune.</span>",
        "specialpages-group-maintenance": "Report 'e manutenzione",
        "specialpages-group-other": "Ati paggene speciale",
        "specialpages-group-login": "Trasite o criate n'acciesso nuovo",
index edaebe7..2763df2 100644 (file)
        "fileduplicatesearch-noresults": "Ingen ved navn «$1» funnet.",
        "specialpages": "Spesialsider",
        "specialpages-note-top": "Tegnforklaring",
-       "specialpages-note": "* Normale spesialsider.\n* <span class=\"mw-specialpagerestricted\">Spesialsider med begrenset tilgang.</span>",
        "specialpages-group-maintenance": "Vedlikeholdsrapporter",
        "specialpages-group-other": "Andre spesialsider",
        "specialpages-group-login": "Innlogging / opprette bruker",
index de9207a..99041d4 100644 (file)
        "right-autocreateaccount": "Automatisch aanmelden met een extern gebruikersaccount",
        "right-minoredit": "Bewerkingen als klein markeren",
        "right-move": "Pagina's hernoemen",
-       "right-move-subpages": "Pagina's inclusief subpagina's verplaatsen",
+       "right-move-subpages": "Pagina's inclusief deelpagina's verplaatsen",
        "right-move-rootuserpages": "Gebruikerspagina's van het hoogste niveau hernoemen",
        "right-move-categorypages": "Categoriepagina's hernoemen",
        "right-movefile": "Bestanden hernoemen",
        "action-history": "de geschiedenis van deze pagina te bekijken",
        "action-minoredit": "deze bewerking als klein te markeren",
        "action-move": "deze pagina te hernoemen",
-       "action-move-subpages": "deze pagina en bijbehorende subpagina's te hernoemen",
+       "action-move-subpages": "deze pagina en bijbehorende deelpagina's te hernoemen",
        "action-move-rootuserpages": "gebruikerspagina's van het hoogste niveau te hernoemen",
        "action-move-categorypages": "categoriepagina's te hernoemen",
        "action-movefile": "dit bestand te hernoemen",
        "rcfilters-legend-heading": "<strong>Lijst met afkortingen:</strong>",
        "rcfilters-activefilters": "Actieve filters",
        "rcfilters-advancedfilters": "Geavanceerde filters",
+       "rcfilters-limit-title": "Wijzigingen om te tonen",
+       "rcfilters-limit-shownum": "Toon laatste $1 wijzigingen",
+       "rcfilters-days-show-days": "$1 {{PLURAL:$1|dag|dagen}}",
+       "rcfilters-days-show-hours": "$1 {{PLURAL:$1|uur|uren}}",
        "rcfilters-quickfilters": "Opgeslagen filters",
        "rcfilters-quickfilters-placeholder-title": "Nog geen koppelingen opgeslagen",
        "rcfilters-quickfilters-placeholder-description": "Om uw filterinstellingen op te slaan en later te kunnen hergebruiken, klik op het bladwijzer pictogram in het Actieve Filter gebied beneden.",
        "rcfilters-savedqueries-unsetdefault": "Als standaard verwijderen",
        "rcfilters-savedqueries-remove": "Verwijderen",
        "rcfilters-savedqueries-new-name-label": "Naam",
+       "rcfilters-savedqueries-new-name-placeholder": "Beschrijf het doel van het filter",
        "rcfilters-savedqueries-apply-label": "Filter aanmaken",
        "rcfilters-savedqueries-cancel-label": "Annuleren",
        "rcfilters-savedqueries-add-new-title": "Huidige filter instellingen opslaan",
        "rcfilters-invalid-filter": "Ongeldig filter",
        "rcfilters-empty-filter": "Geen actieve filters. Alle bijdragen worden weergeven.",
        "rcfilters-filterlist-title": "Filters",
-       "rcfilters-filterlist-whatsthis": "Wat is dit?",
+       "rcfilters-filterlist-whatsthis": "Hoe werkt dit?",
        "rcfilters-filterlist-feedbacklink": "Geef terugkoppeling op de nieuwe (beta)filters",
        "rcfilters-highlightbutton-title": "Resultaten markeren",
        "rcfilters-highlightmenu-title": "Kies een kleur",
        "rcfilters-filter-editsbyself-description": "Uw eigen bijdragen.",
        "rcfilters-filter-editsbyother-label": "Wijzigingen door anderen",
        "rcfilters-filter-editsbyother-description": "Alle wijzigingen behalve die door u gemaakt zijn.",
-       "rcfilters-filtergroup-userExpLevel": "Ervaringsniveau (alleen voor geregistreerde gebruikers)",
+       "rcfilters-filtergroup-userExpLevel": "Gebruikersregistratie en ervaring",
        "rcfilters-filter-user-experience-level-registered-label": "Geregistreerd",
-       "rcfilters-filter-user-experience-level-registered-description": "Ingelogde gebruikers.",
+       "rcfilters-filter-user-experience-level-registered-description": "Aangemelde bewerkers.",
        "rcfilters-filter-user-experience-level-unregistered-label": "Niet-geregistreerd",
-       "rcfilters-filter-user-experience-level-unregistered-description": "Gebruikers die niet zijn ingelogd.",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Bewerkers die niet zijn aangemeld.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Nieuwkomers",
-       "rcfilters-filter-user-experience-level-newcomer-description": "Minder dan 10 bewerkingen en 4 dagen van activiteit.",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Geregistreerde bewerkers met minder dan 10 bewerkingen en 4 dagen van activiteit.",
        "rcfilters-filter-user-experience-level-learner-label": "Leerlingen",
-       "rcfilters-filter-user-experience-level-learner-description": "Meer ervaring dan \"nieuwkomers\", maar minder dan \"ervaren gebruikers\".",
+       "rcfilters-filter-user-experience-level-learner-description": "Geregistreerde bewerkers met meer ervaring dan \"nieuwkomers\", maar minder dan \"ervaren gebruikers\".",
        "rcfilters-filter-user-experience-level-experienced-label": "Ervaren gebruikers",
-       "rcfilters-filter-user-experience-level-experienced-description": "Meer dan 30 dagen van activiteit en 500 bewerkingen.",
+       "rcfilters-filter-user-experience-level-experienced-description": "Geregistreerde bewerkers met meer dan 500 bewerkingen en 30 dagen van activiteit.",
        "rcfilters-filtergroup-automated": "Automatische bijdragen",
        "rcfilters-filter-bots-label": "Bot",
        "rcfilters-filter-bots-description": "De wijzigingen van geautomatiseerde hulpmiddelen.",
        "rcfilters-filter-logactions-label": "Geregistreerde acties",
        "rcfilters-filter-logactions-description": "Administratieve handelingen, account creaties, pagina verwijderingen, uploads…",
        "rcfilters-hideminor-conflicts-typeofchange": "Bepaalde soorten wijzigingen kunnen niet worden aangemerkt als \"klein\", dus dit filter is in conflict met de volgende soorten wijzigingenfilters: $1",
-       "rcfilters-filtergroup-lastRevision": "Laatste versie",
+       "rcfilters-filtergroup-lastRevision": "Laatste versies",
        "rcfilters-filter-lastrevision-label": "Laatste versie",
-       "rcfilters-filter-lastrevision-description": "De meest recente wijziging aan de pagina.",
-       "rcfilters-filter-previousrevision-label": "Eerdere versies",
-       "rcfilters-filter-previousrevision-description": "Alle wijzigingen die niet de meest recente wijziging op de pagina zijn.",
+       "rcfilters-filter-lastrevision-description": "Alleen de meest recente wijziging aan de pagina.",
+       "rcfilters-filter-previousrevision-label": "Niet de laatste versie",
+       "rcfilters-filter-previousrevision-description": "Alle wijzigingen die niet de \"laatste versie\" zijn.",
        "rcfilters-filter-excluded": "Uitgesloten",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:niet</strong> $1",
+       "rcfilters-exclude-button-off": "Geselecteerde uitsluiten",
        "rcfilters-view-tags": "Gelabelde bewerkingen",
        "rcfilters-view-namespaces-tooltip": "Filter resultaten op naamruimte",
        "rcfilters-view-tags-tooltip": "Filter resultaten door middel van bewerkingslabels",
        "delete-warning-toobig": "Deze pagina heeft een lange bewerkingsgeschiedenis, meer dan $1 {{PLURAL:$1|versie|versies}}.\nHet verwijderen van deze pagina kan de werking van de database van {{SITENAME}} verstoren.\nWees voorzichtig.",
        "deleteprotected": "U kunt deze pagina niet verwijderen omdat hij is beveiligd.",
        "deleting-backlinks-warning": "<strong>Waarschuwing:</strong> [[Special:WhatLinksHere/{{FULLPAGENAME}}|andere pagina's]] gebruiken of verwijzen naar de pagina die u wilt verwijderen.",
+       "deleting-subpages-warning": "<strong>Waarschuwing:</strong>De pagina die u wilt verwijderen heeft [[Special:PrefixIndex/{{FULLPAGENAME}}/|{{PLURAL:$1|een deelpagina|$1 deelpagina's|51=meer dan 50 deelpagina's}}]].",
        "rollback": "Wijzigingen ongedaan maken",
        "rollbacklink": "terugdraaien",
        "rollbacklinkcount": "{{PLURAL:$1|één bewerking|$1 bewerkingen}} terugdraaien",
        "articleexists": "De pagina bestaat al of de paginanaam is ongeldig.\nKies een andere paginanaam.",
        "cantmove-titleprotected": "U kunt geen pagina naar deze naam hernoemen, omdat deze naam beveiligd is tegen het aanmaken ervan.",
        "movetalk": "Bijbehorende overlegpagina hernoemen",
-       "move-subpages": "Subpagina's hernoemen (maximaal $1)",
-       "move-talk-subpages": "Subpagina's van overlegpagina's hernoemen (maximaal $1)",
+       "move-subpages": "Deelpagina's hernoemen (maximaal $1)",
+       "move-talk-subpages": "Deelpagina's van overlegpagina's hernoemen (maximaal $1)",
        "movepage-page-exists": "De pagina $1 bestaat al en kan niet automatisch verwijderd worden.",
        "movepage-page-moved": "De pagina $1 is hernoemd naar $2.",
        "movepage-page-unmoved": "De pagina $1 kon niet hernoemd worden naar $2.",
        "movepage-max-pages": "Het maximale aantal automatisch te hernoemen pagina's is bereikt ({{PLURAL:$1|$1|$1}}).\nDe overige pagina's worden niet automatisch hernoemd.",
        "movelogpage": "Hernoemingslogboek",
        "movelogpagetext": "Hieronder staan hernoemde pagina's.",
-       "movesubpage": "{{PLURAL:$1|Subpagina|Subpagina's}}",
-       "movesubpagetext": "De {{PLURAL:$1|subpagina|$1 subpagina's}} van deze pagina {{PLURAL:$1|wordt|worden}} hieronder weergegeven.",
+       "movesubpage": "{{PLURAL:$1|Deelpagina|Deelpagina's}}",
+       "movesubpagetext": "De {{PLURAL:$1|deelpagina|$1 deelpagina's}} van deze pagina {{PLURAL:$1|wordt|worden}} hieronder weergegeven.",
        "movesubpagetalktext": "De bijbehorende overlegpagina heeft $1 {{PLURAL:$1|deelpagina|deelpagina's}} hierbeneden getoond.",
-       "movenosubpage": "Deze pagina heeft geen subpagina's.",
+       "movenosubpage": "Deze pagina heeft geen deelpagina's.",
        "movereason": "Reden:",
        "revertmove": "terugdraaien",
        "delete_and_move_text": "Onder de naam \"[[:$1]]\" bestaat al een pagina.\nWilt u deze verwijderen om plaats te maken voor de te hernoemen pagina?",
        "import-interwiki-submit": "Importeren",
        "import-mapping-default": "Importeren naar standaardplaatsen",
        "import-mapping-namespace": "Importeren naar een naamruimte:",
-       "import-mapping-subpage": "Importeren als subpagina's van de volgende pagina:",
+       "import-mapping-subpage": "Importeren als deelpagina's van de volgende pagina:",
        "import-upload-filename": "Bestandsnaam:",
        "import-comment": "Opmerking:",
        "importtext": "Gebruik de [[Special:Export|exportfunctie]] in de wiki waar de informatie vandaan komt.\nSla de uitvoer op uw eigen computer op, en voeg die daarna hier toe.",
        "import-error-bad-location": "Versie $2 met behulp van model $3 kan niet worden opgeslagen als \"$1\" op deze wiki, aangezien dat model niet ondersteund wordt op die pagina.",
        "import-options-wrong": "Verkeerde {{PLURAL:$2|optie|opties}}: <nowiki>$1</nowiki>",
        "import-rootpage-invalid": "De opgegeven basispagina is ongeldig.",
-       "import-rootpage-nosubpage": "In de naamruimte \"$1\" van de basispagina is het aanmaken van subpagina's niet mogelijk.",
+       "import-rootpage-nosubpage": "In de naamruimte \"$1\" van de basispagina is het aanmaken van deelpagina's niet mogelijk.",
        "importlogpage": "Importlogboek",
        "importlogpagetext": "Administratieve import van pagina's met geschiedenis van andere wiki's.",
        "import-logentry-upload-detail": "$1 {{PLURAL:$1|versie|versies}} geïmporteerd",
        "pageinfo-few-watchers": "Minder dan  {{PLURAL:$1|één volger|$1 volgers}}",
        "pageinfo-few-visiting-watchers": "Er kan wel of niet een volger zijn die de laatste bewerkingen hier bezoekt",
        "pageinfo-redirects-name": "Aantal doorverwijzingen naar deze pagina",
-       "pageinfo-subpages-name": "Subpagina's van deze pagina",
+       "pageinfo-subpages-name": "Aantal deelpagina's van deze pagina",
        "pageinfo-subpages-value": "$1 ($2 {{PLURAL:$2|doorverwijzing|doorverwijzingen}}; $3 {{PLURAL:$3|niet-doorverwijzing|niet-doorverwijzingen}})",
        "pageinfo-firstuser": "Gebruiker die de pagina heeft aangemaakt",
        "pageinfo-firsttime": "Datum waarop de pagina is aangemaakt",
        "fileduplicatesearch-noresults": "Er is geen bestand met de naam \"$1\" gevonden.",
        "specialpages": "Speciale pagina's",
        "specialpages-note-top": "Legenda",
-       "specialpages-note": "* Normale speciale pagina's\n* <span class=\"mw-specialpagerestricted\">Beperkt toegankelijke speciale pagina's</span>",
+       "specialpages-note-restricted": "* Normale speciale pagina's.\n* <span class=\"mw-specialpagerestricted\">Beperkt toegankelijke speciale pagina's.</span>",
        "specialpages-group-maintenance": "Onderhoudsrapporten",
        "specialpages-group-other": "Overige speciale pagina's",
        "specialpages-group-login": "Aanmelden / registreren",
index 7065d7b..c6c66f0 100644 (file)
        "preview": "Førehandsvising",
        "showpreview": "Førehandsvis",
        "showdiff": "Sjå skilnader",
+       "blankarticle": "<strong>Åtvaring:</strong> Sida du er i ferd med å oppretta er tom.\nKlikkar du på «$1» ein gong til vil sida opprettast utan innhald.",
        "anoneditwarning": "'''Åtvaring:''' Du er ikkje innlogga.\nIP-adressa di vil verta lagra i den offentlege endringshistorikken til sida. Om du <strong>[$1 loggar inn]</strong> eller <strong>[$2 lagar ein konto]</strong>, vil endringane dine knytast til brukarnamnet ditt, saman med andre fordelar.",
        "anonpreviewwarning": "''Du er ikkje innlogga. Lagrar du vil IP-adressa di verta ført opp i endringshistorikken til denne sida.''",
        "missingsummary": "'''Påminning:''' Du har ikkje skrive noko endringssamandrag. Dersom du trykkjer «Lagre» ein gong til, vert endringa di lagra utan.",
        "fileduplicatesearch-noresults": "Fann inga fil med namnet «$1».",
        "specialpages": "Spesialsider",
        "specialpages-note-top": "Tyding",
-       "specialpages-note": "* Vanlege spesialsider.\n* <span class=\"mw-specialpagerestricted\">Spesialsider med avgrensa tilgang.</span>",
        "specialpages-group-maintenance": "Vedlikehaldsrapportar",
        "specialpages-group-other": "Andre spesialsider",
        "specialpages-group-login": "Logga inn / oppretta brukarkonto",
index a584068..61a40b3 100644 (file)
        "fileduplicatesearch-noresults": "Brak pliku o nazwie „$1”.",
        "specialpages": "Strony specjalne",
        "specialpages-note-top": "Legenda",
-       "specialpages-note": "* Normalne strony specjalne.\n* <span class=\"mw-specialpagerestricted\">Zastrzeżone strony specjalne.</span>",
+       "specialpages-note-restricted": "* Normalne strony specjalne.\n* <span class=\"mw-specialpagerestricted\">Zastrzeżone strony specjalne.</span>",
        "specialpages-group-maintenance": "Raporty konserwacyjne",
        "specialpages-group-other": "Inne strony specjalne",
        "specialpages-group-login": "Logowanie / rejestracja",
index f917ee5..eda97ca 100644 (file)
        "fileduplicatesearch-result-n": "فائل ''$1'' چ {{PLURAL:$2|1 رلدی نقل|$2 رلدیاں نقلں}} نیں۔",
        "fileduplicatesearch-noresults": "\"$1\" ناں دی کوئی فائل نئیں لبی۔",
        "specialpages": "خاص صفے",
-       "specialpages-note": "* نارمل خاص صفے.\n* <span class=\"mw-specialpagerestricted\">روکے گۓ خاص صفے.</span>\n* <span class=\"mw-specialpagecached\">کاشے خاص صفے (پرانے ہوگۓ ہون).</span>",
        "specialpages-group-maintenance": "مرمت رپورٹ",
        "specialpages-group-other": "ہور خاص صفے",
        "specialpages-group-login": "لاگان / کھاتہ کھولو",
index a13593a..2eaaf0b 100644 (file)
        "fileduplicatesearch-noresults": "Não foi encontrado nenhum arquivo com o nome \"$1\".",
        "specialpages": "Páginas especiais",
        "specialpages-note-top": "Legenda",
-       "specialpages-note": "* Páginas especiais normais.\n* <span class=\"mw-specialpagerestricted\">Páginas especiais restritas.</span>",
+       "specialpages-note-restricted": "* Páginas especiais normais.\n* <span class=\"mw-specialpagerestricted\">Páginas especiais restritas.</span>",
        "specialpages-group-maintenance": "Relatórios de manutenção",
        "specialpages-group-other": "Outras páginas especiais",
        "specialpages-group-login": "Entrar / Criar conta",
index e771309..23f56c0 100644 (file)
        "rcfilters-hideminor-conflicts-typeofchange-global": "O filtro \"Edições menores\" entra em conflito com um ou mais filtros de Tipo de Modificação, porque certos tipos de modificações não podem ser classificados como \"menores\". Os filtros em conflito estão marcados na área Filtros Ativos, acima.",
        "rcfilters-hideminor-conflicts-typeofchange": "Certos tipos de modificações não podem ser classificados como \"menores\", portanto este filtro entra em conflito com os seguintes filtros de Tipo de Modificação: $1",
        "rcfilters-typeofchange-conflicts-hideminor": "Este filtro de Tipo de Modificação entra em conflito com o filtro \"Edições menores\". Certos tipos de modificações não podem ser classificados como \"menores\".",
-       "rcfilters-filtergroup-lastRevision": "Última revisão",
+       "rcfilters-filtergroup-lastRevision": "Últimas revisões",
        "rcfilters-filter-lastrevision-label": "Última revisão",
-       "rcfilters-filter-lastrevision-description": "A modificação mais recente de uma página.",
-       "rcfilters-filter-previousrevision-label": "Revisões anteriores",
-       "rcfilters-filter-previousrevision-description": "Todas as modificações que não sejam a modificação mais recente de uma página.",
+       "rcfilters-filter-lastrevision-description": "Só a modificação mais recente de uma página.",
+       "rcfilters-filter-previousrevision-label": "Revisões menos a mais recente",
+       "rcfilters-filter-previousrevision-description": "Todas as modificações que não são a \"última revisão\".",
        "rcfilters-filter-excluded": "Excluído",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:não</strong> $1",
        "rcfilters-exclude-button-off": "Excluir os selecionados",
        "fileduplicatesearch-noresults": "Não foi encontrado nenhum ficheiro com o nome \"$1\".",
        "specialpages": "Páginas especiais",
        "specialpages-note-top": "Legenda",
-       "specialpages-note": "* Páginas especiais normais.\n* <span class=\"mw-specialpagerestricted\">Páginas especiais restritas.</span>",
+       "specialpages-note-restricted": "* Páginas especiais normais.\n* <span class=\"mw-specialpagerestricted\">Páginas especiais restritas.</span>",
        "specialpages-group-maintenance": "Relatórios de manutenção",
        "specialpages-group-other": "Outras páginas especiais",
        "specialpages-group-login": "Entrar / criar conta",
index 9da374a..95ac6bd 100644 (file)
        "privacypage": "Project:Tasertit n tusligi",
        "retrievedfrom": "Itwarr-d zi \"$1\"",
        "youhavenewmessages": "Ghar-k / Ghar-m $1 ($2).",
-       "editsection": "Ẓṛeg",
-       "editold": "ẓṛeg",
+       "editsection": "ⵙⵏⴼⵍ",
+       "editold": "ⵙⵏⴼⵍ",
        "viewsourceold": "ẓeṛ aɣbalu",
-       "editlink": "ẓṛg",
+       "editlink": "ⵙⵏⴼⵍ",
        "viewsourcelink": "ẓṛ aghbalu",
        "editsectionhint": "Ẓṛeg tigezmi: $1",
        "toc": "ⵜⵓⵎⴰⵢⵉⵏ",
        "yourpassword": "Tawalt n wadaf:",
        "login": "ⴰⴷⴼ",
        "nav-login-createaccount": "Adef / egg amiḍan",
-       "logout": "Ufugh",
-       "userlogout": "Ufugh",
+       "logout": "ⴼⴼⵖ",
+       "userlogout": "ⴼⴼⵖ",
        "createaccount": "Egg amiḍan",
        "createacct-benefit-body2": "{{PLURAL:$1|ⵜⴰⵙⵏⴰ|ⵜⴰⵙⵏⵉⵡⵉⵏ}}",
        "loginsuccesstitle": "Adaf icna",
        "newarticletext": "Tdefar-d tazdayt n Tasna εad war telli .\nbac ad tegged , arri di taflwit a swadday (xemm i [$1  Tasna n Tallalt] i ineɣmisen ifruryen).\nmala qacek da s ɣalaṭ waha, tecca di tbutunt n '''deffar''' di (browser) inec .",
        "noarticletext": "Rxxu ur din llint ca tira di tasna ya.\nTzmmard [[Special:Search/{{PAGENAME}}|rzu xf yizwl n tasna ya]] di tasniwin nnḍni,\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} search the related logs],\nnigh [{{fullurl:{{FULLPAGENAME}}|action=edit}} edit this page]</span>.",
        "previewnote": "'''Wa d Azar-ascan waha;\ntiẓṛigin εad war twaḥḍent!'''",
-       "editing": "Aẓṛag di $1",
-       "editingsection": "Aẓrag  di $1 (tigezmi)",
+       "editing": "ⴰⵙⵏⴼⵍ ⵏ $1",
+       "editingsection": "ⴰⵙⵏⴼⵍ ⵏ $1 (ⵜⵉⴳⵣⵎⵉ)",
        "copyrightwarning": "Maṛṛa tirra di {{SITENAME}} twaggent swadday i $2 (ẓar da $1).\nmala war texsed tirra inac ad twaẓṛegent , ad twamsebḍant .\nUr ten-teg ca da.<br />\ntjadjid-anɣ Ɛawt ila qa d cekk ig yuran manaya, niɣ tesneɣlet-id zi ca n uɣbal nniḍn d alelli.\n'''UR SADDAF CA TIRRA ƔARSENT COPYRIGHTE BLA MA AD-IXES BAB-INES !'''",
        "templatesused": "Timudmiwin itwaggen di Tasna ya:",
        "templatesusedpreview": "Timudmiwin igg itwasxdemen dg uzar-ascan a :",
        "action-read": "ⵖⵔ ⵜⴰⵙⵏⴰ ⴰ",
        "action-edit": "ⵙⵏⴼⵍ ⵜⴰⵙⵏⴰ ⴰ",
        "action-delete": "ⴽⴽⵙ ⵜⴰⵙⵏⴰ ⴰ",
-       "nchanges": "$1 {{PLURAL:$1|tiẓṛegt|tiẓṛigin}}",
+       "nchanges": "$1 {{PLURAL:$1|ⵓⵙⵏⴼⵍ|ⵉⵙⵏⴼⵉⵍⵏ}}",
        "enhancedrc-history": "ⴰⵎⵣⵔⵓⵢ",
        "recentchanges": "Tiẓṛigin tineggura",
        "recentchanges-feed-description": "Bbar tiẓṛigin timayutin n wiki deg usudem(feed) a .",
        "rcshowhideliu": "$1 users ig yudeffen",
        "rcshowhideanons": "$1 users war twasnen",
        "rcshowhidepatr": "Tiẓṛigin ig itwaẓrent di $1",
-       "rcshowhidemine": "$1 tiẓṛigin inu",
+       "rcshowhidemine": "$1 ⵉⵙⵏⴼⵉⵍⵏ ⵉⵏⵓ",
        "rclinks": "Ẓar $1 tiẓṛigin tinggura di $2 n ussan inggura",
        "diff": "imṣebḍan",
        "hist": "ⴰⵎⵣⵔⵓⵢ",
        "filehist": "Amzruy n usatul",
        "filehist-help": "Tka di date/time bac ad tẓerd afaylu mamec ja d-itban di Lwaqt a .",
        "filehist-deleteall": "ⴽⴽⵙ ⵎⴰⵔⵔⴰ",
-       "filehist-deleteone": "sfaḍ",
+       "filehist-deleteone": "ⴽⴽⵙ",
        "filehist-current": "ⴰⵎⵉⵔⴰⵏ",
        "filehist-datetime": "ⴰⵙⴰⴽⵓⴷ/ⴰⴽⵓⴷ",
        "filehist-user": "Aseqdac",
        "filehist-dimensions": "Tisektiwin",
-       "filehist-filesize": "Tiddi n ufaylu",
+       "filehist-filesize": "ⵜⵉⴷⴷⵉ ⵏ ⵓⴼⴰⵢⵍⵓ",
        "filehist-comment": "ⴰⵅⴼⴰⵡⴰⵍ",
        "imagelinks": "Aseqdec usatul",
        "linkstoimage": "{{PLURAL:$1|Tasna ya teqn-ad|$1 Tasniwin a qnent-id}} ɣa ufaylu ya :",
        "statistics-pages": "ⵜⴰⵙⵏⵉⵡⵉⵏ",
        "doubleredirects": "(redirects) ɛɛawdent",
        "brokenredirects": "(redirects) arẓent",
-       "brokenredirects-edit": "arri",
+       "brokenredirects-edit": "ⵙⵏⴼⵍ",
        "brokenredirects-delete": "ⴽⴽⵙ",
        "withoutinterwiki": "Tasna bla tiẓdayin n tutlayt",
        "withoutinterwiki-submit": "Smmrad",
        "actioncomplete": "Tiggawt tsala",
        "deletedtext": "\"$1\" Twakkes.\nXemm $2 i tikkas timaynutin.",
        "dellogpage": "Aɣmis n uṣfaḍ",
-       "deletecomment": "Ssebba:",
+       "deletecomment": "ⵜⴰⵎⵏⵜⵉⵍⵜ:",
        "deleteotherreason": "Ca n ssebba nniḍn:",
        "deletereasonotherlist": "Ssebba nniḍn",
        "rollbacklink": "Sdwl ghar dffar",
        "whatlinkshere-hidelinks": "$1 timqqan",
        "blockip": "Sbdd asqdac a",
        "ipbreason": "ⵜⴰⵎⵏⵜⵉⵍⵜ:",
-       "ipboptions": "2 n timirin:2 hours,1 n wass:1 day,3 n wussan:3 days,1 imalass:1 week,2 imallassn:2 weeks,1 wayur:1 month,3 wayurn:3 months,6 wayurn:6 months,1 asggwas:1 year,tartalla:infinite",
+       "ipboptions": "2 ⵜⵙⵔⴰⴳⵉⵏ:2 hours,1 ⵡⴰⵙⵙ:1 day,3 ⵡⵓⵙⵙⴰⵏ:3 days,1 ⵉⵎⴰⵍⴰⵙⵙ:1 week,2 ⵉⵎⴰⵍⴰⵙⵙⵏ:2 weeks,1 ⵡⴰⵢⵢⵓⵓⵔ:1 month,3 ⵡⴰⵢⵢⵓⵔⵏ:3 months,6 ⵡⴰⵢⵢⵓⵔⵏ:6 months,1 ⵓⵙⴳⴳⵯⴰⵙ:1 year,ⵍⴱⴷⴰ:infinite",
        "autoblocklist-submit": "ⵔⵣⵓ",
        "ipblocklist": "Tabdart n tansiwin IP d isemawen n iseqdacen ig iteblukan",
        "blocklist-reason": "ⵜⴰⵎⵏⵜⵉⵍⵜ",
index 46196c1..1161326 100644 (file)
        "fileduplicatesearch-noresults": "Betg chattà ina datoteca cun il num \"$1\".",
        "specialpages": "Paginas spezialas",
        "specialpages-note-top": "Legenda",
-       "specialpages-note": "* Paginas spezialas normalas.\n* <span class=\"mw-specialpagerestricted\">Paginas spezialas restrenschidas.</span>",
        "specialpages-group-maintenance": "Rapports da mantegnamant",
        "specialpages-group-other": "Autras paginas spezialas",
        "specialpages-group-login": "S'annunziar / crear in conto",
index cfb86b0..a24c6c2 100644 (file)
        "fileduplicatesearch-noresults": "Nu s-a găsit niciun fișier cu numele „$1”.",
        "specialpages": "Pagini speciale",
        "specialpages-note-top": "Legendă",
-       "specialpages-note": "* Pagini speciale normale.\n* <span class=\"mw-specialpagerestricted\">Pagini speciale restricționate.</span>",
        "specialpages-group-maintenance": "Întreținere",
        "specialpages-group-other": "Alte pagini speciale",
        "specialpages-group-login": "Autentificare / creare cont",
index 8cffd2a..41c9360 100644 (file)
        "changepassword-success": "'A password toje ha state cangiate!",
        "changepassword-throttled": "Tu è pruvate 'nu sacche de vote a trasè.\nPe piacere aspitte $1 apprime de pruvà arrete.",
        "botpasswords": "Password d'u bot",
+       "botpasswords-existing": "Passuord de le bot esistende",
+       "botpasswords-createnew": "Ccreje 'na passuord nove pu bot",
+       "botpasswords-editexisting": "Cange 'na passuord d'u bot ca esiste ggià",
        "botpasswords-label-appid": "Nome d'u bot:",
        "botpasswords-label-create": "Ccreje",
        "botpasswords-label-update": "Aggiorne",
        "botpasswords-updated-title": "Passuord d'u bot cangiate",
        "botpasswords-deleted-title": "Passuord d'u bot scangellate",
        "resetpass_forbidden": "Le Password non ge ponne cangià",
+       "resetpass_forbidden-reason": "Le passuord non ge ponne essere cangiate: $1",
        "resetpass-no-info": "Tu a essere colleghete pe accedere a sta pàgene direttamende.",
        "resetpass-submit-loggedin": "Cange 'a password",
        "resetpass-submit-cancel": "Annulle",
        "invalid-content-data": "Condenute d'u date invalide",
        "content-not-allowed-here": "\"$1\" condenute non g'è permesse sus 'a pàgene [[$2]]",
        "editwarning-warning": "Assenne da sta pàgene tu puè perdè tutte le date ca è cangiate.\nCe tu è trasute, tu puè disabbilità st'avvertimende jndr'à sezione \"{{int:prefs-editing}}\" de le preferenze tune.",
+       "editpage-invalidcontentmodel-title": "'U Modelle d'u condenute non gè supportate",
+       "editpage-invalidcontentmodel-text": "'U modelle d'u condenute \"$1\" non g'è supportate.",
        "editpage-notsupportedcontentformat-title": "'U formate d'u condenute non gè supportate",
        "editpage-notsupportedcontentformat-text": "'U formate d'u condenute $1 non g'è supportate da 'u modelle de condenute $2.",
        "content-model-wikitext": "Uicchiteste",
        "grant-group-file-interaction": "Inderaggisce cu le media",
        "grant-group-watchlist-interaction": "Inderaggisce cu le pàggene condrollate",
        "grant-group-email": "Manne 'n'e-mail",
+       "grant-createaccount": "Ccreje le cunde utinde",
+       "grant-createeditmovepage": "Ccreje, cange e spueste le pàggene",
+       "grant-delete": "Scangille pàggene, revisiune e vôsce de l'archivije",
        "newuserlogpage": "Archivije de ccreazione de le utinde",
        "newuserlogpagetext": "Quiste ète l'archivije de le creazziune de l'utinde.",
        "rightslog": "Archivie de le diritte de l'utende",
        "recentchanges-submit": "Fà 'ndrucà",
        "rcfilters-activefilters": "Filtre attive",
        "rcfilters-advancedfilters": "Filtre avanzate",
+       "rcfilters-limit-title": "Cangiaminde da 'ndrucà",
+       "rcfilters-limit-shownum": "Fà 'ndrucà le urteme $1 cangiaminde",
+       "rcfilters-days-title": "Urteme sciurne",
+       "rcfilters-hours-title": "Urteme ore",
+       "rcfilters-days-show-days": "$1 {{PLURAL:$1|sciurne}}",
+       "rcfilters-days-show-hours": "$1 {{PLURAL:$1|ore}}",
        "rcfilters-quickfilters": "Filtre reggistrate",
        "rcfilters-quickfilters-placeholder-title": "Nisciune collegamende reggistrate",
+       "rcfilters-savedqueries-defaultlabel": "Filtre reggistrate",
+       "rcfilters-savedqueries-rename": "Renomene",
+       "rcfilters-savedqueries-setdefault": "'Mboste cumme predefinite",
+       "rcfilters-savedqueries-unsetdefault": "Live cumme predefinite",
+       "rcfilters-savedqueries-remove": "Live",
+       "rcfilters-savedqueries-new-name-label": "Nome",
+       "rcfilters-savedqueries-new-name-placeholder": "Dì a ce serve 'u filtre",
        "rcfilters-savedqueries-apply-label": "Ccrèje 'nu filtre",
+       "rcfilters-savedqueries-cancel-label": "Annulle",
+       "rcfilters-filterlist-title": "Filtre",
+       "rcfilters-filterlist-whatsthis": "Cumme funzionane?",
+       "rcfilters-highlightmenu-title": "Scacchie 'nu culore",
+       "rcfilters-highlightmenu-help": "Scacchie 'nu culore pe evidenzià sta probbietà",
+       "rcfilters-filterlist-noresults": "Nisciune filtre acchiate",
+       "rcfilters-filter-bots-label": "Bot",
+       "rcfilters-filter-patrolled-label": "Condrollate",
+       "rcfilters-filter-patrolled-description": "Cangiaminde signate cumme condrollate.",
+       "rcfilters-filter-unpatrolled-label": "Non condrollate",
+       "rcfilters-filter-unpatrolled-description": "Cangiaminde non signate cumme condrollate.",
+       "rcfilters-filtergroup-significance": "Significate",
+       "rcfilters-filter-minor-label": "Cangiaminde stuèdeche",
        "rcnotefrom": "Sotte {{PLURAL:$5|ste 'u cangiamende|stonne le cangiaminde}} da <strong>$3, $4</strong> ('nzigne a <strong>$1</strong> fatte vedè).",
        "rclistfrom": "Fà vedè le urteme cangiaminde partenne da $3 $2",
        "rcshowhideminor": "$1 cangiaminde stuèdeche",
        "rcshowhidemine": "$1 cangiaminde mie",
        "rcshowhidemine-show": "Fà vedè",
        "rcshowhidemine-hide": "Scunne",
+       "rcshowhidecategorization-show": "Fà 'ndrucà",
+       "rcshowhidecategorization-hide": "Scunne",
        "rclinks": "Vide l'urteme $1 cangiaminde jndr'à l'urteme $2 sciurne",
        "diff": "diff",
        "hist": "cunde",
        "mostrevisions": "Pàggene cchiù cangete",
        "prefixindex": "Tutte le pàggene cu 'u prefisse",
        "prefixindex-namespace": "Tutte le pàggene cu 'u prefisse ($1 namespace)",
+       "prefixindex-submit": "Fà 'ndrucà",
        "prefixindex-strip": "Strisce d'u prefisse jndr'à l'elenghe",
        "shortpages": "Pàggene corte",
        "longpages": "Pàggene longhe",
        "protectedpages-performer": "Stoche a protegge l'utende",
        "protectedpages-params": "Parametre de protezzione",
        "protectedpages-reason": "Mutive",
+       "protectedpages-submit": "Fà 'ndrucà le pàggene",
        "protectedpages-unknown-timestamp": "Scanusciute",
        "protectedpages-unknown-performer": "Utende scanusciute",
        "protectedtitles": "Titele prutette",
        "protectedtitles-summary": "Sta pàgene elenghe le titole ca so prutette da 'a ccrejazzione. Pe 'n'elenghe de le pàggene ca sò prutette, 'ndruche [[{{#special:ProtectedPages}}|{{int:protectedpages}}]].",
        "protectedtitlesempty": "Nisciune titele jè pe mò prutette cu ste parametre.",
+       "protectedtitles-submit": "Fà 'ndrucà le titole",
        "listusers": "Liste de l'utende",
        "listusers-editsonly": "Fà vedè sulamende l'utinde cu cangiaminde fatte",
        "listusers-creationsort": "Arrenghete pe date de ccreazione",
        "usereditcount": "$1 {{PLURAL:$1|cangiamende|cangiaminde}}",
        "usercreated": "{{GENDER:$3|Ccrejate}} 'u $1 a le ore $2",
        "newpages": "Pàggene nuève",
+       "newpages-submit": "Fà 'ndrucà",
        "newpages-username": "Nome de l'utende:",
        "ancientpages": "Pàggene vìcchje",
        "move": "Spuèste",
        "apisandbox-intro": "Ause sta pàgene pe sperimendà cu le <strong>API de le web service pe MediaUicchi</strong>.\nFà referimende a [[mw:API:Main page| 'a documendazione de l'API]] pe cchiù dettaglie de l'ause de l'API.\nEsembie: [https://www.mediawiki.org/wiki/API#A_simple_example pigghie 'u condenute d'a Pàgene Prengepàle]. Scacchie 'n'azione pe 'ndrucà otre esembie.\n\nVide ca, pure ca queste jè 'na buatte de sabbie tu puè carrescià le cangiaminde de sta pàgene sus 'a uicchi.",
        "apisandbox-submit": "Fà 'na richieste",
        "apisandbox-reset": "Pulizze",
+       "apisandbox-retry": "Pruève arrete",
        "apisandbox-examples": "Esembie",
        "apisandbox-results": "Resultate",
        "apisandbox-request-url-label": "URL richieste:",
        "apisandbox-request-time": "Tiembe cercate: {{PLURAL:$1|$1 ms}}",
+       "apisandbox-continue": "Condinue",
+       "apisandbox-continue-clear": "Pulizze",
        "booksources": "Sorgende de le libbre",
        "booksources-search-legend": "Cirche pe le fonde de le libbre",
        "booksources-isbn": "ISBN:",
        "fileduplicatesearch-noresults": "Nisciune file chiamate \"$1\" ha state acchiate.",
        "specialpages": "Pàggene speciele",
        "specialpages-note-top": "Leggende",
-       "specialpages-note": "* Pàggene speciale normale.\n* <span class=\"mw-specialpagerestricted\">Pàggene speciale cu le restriziune.</span>",
        "specialpages-group-maintenance": "Report d'a manutenzione",
        "specialpages-group-other": "Otre pàggene speciele",
        "specialpages-group-login": "Tràse / Reggistrate",
index 6606815..f712a14 100644 (file)
        "rcfilters-legend-heading": "<strong>Список сокращений:</strong>",
        "rcfilters-activefilters": "Активные фильтры",
        "rcfilters-advancedfilters": "Расширенные фильтры",
+       "rcfilters-limit-title": "Изменения для показа",
+       "rcfilters-days-title": "Последние дни",
+       "rcfilters-hours-title": "Последние часы",
+       "rcfilters-days-show-days": "$1 {{PLURAL:$1|день|дня|дней}}",
+       "rcfilters-days-show-hours": "$1 {{PLURAL:$1|час|часа|часов}}",
        "rcfilters-quickfilters": "Сохранённые фильтры",
        "rcfilters-quickfilters-placeholder-title": "Сохраненных ссылок еще нет",
        "rcfilters-quickfilters-placeholder-description": "Чтобы сохранить настройки фильтра и повторно использовать их позже, щелкните значок закладки в области «Активный фильтр» ниже.",
        "rcfilters-invalid-filter": "Недопустимый фильтр",
        "rcfilters-empty-filter": "Нет активных фильтров. Показываются все правки.",
        "rcfilters-filterlist-title": "Фильтры",
-       "rcfilters-filterlist-whatsthis": "ЧÑ\82о Ñ\8dÑ\82о?",
+       "rcfilters-filterlist-whatsthis": "Ð\9aак Ñ\8dÑ\82о Ñ\80абоÑ\82аеÑ\82?",
        "rcfilters-filterlist-feedbacklink": "Оставить отзыв о новых (бета) фильтрах",
        "rcfilters-highlightbutton-title": "Выделить результаты",
        "rcfilters-highlightmenu-title": "Выберите цвет",
        "rcfilters-filter-editsbyself-description": "Ваш вклад.",
        "rcfilters-filter-editsbyother-label": "Изменения, внесённые другими участниками",
        "rcfilters-filter-editsbyother-description": "Все правки, кроме ваших собственных.",
-       "rcfilters-filtergroup-userExpLevel": "УÑ\80овнÑ\8f Ð¾Ð¿Ñ\8bÑ\82а (Ñ\82олÑ\8cко Ð´Ð»Ñ\8f Ð·Ð°Ñ\80егиÑ\81Ñ\82Ñ\80иÑ\80ованнÑ\8bÑ\85 Ñ\83Ñ\87аÑ\81Ñ\82ников)",
+       "rcfilters-filtergroup-userExpLevel": "РегиÑ\81Ñ\82Ñ\80аÑ\86иÑ\8f Ñ\83Ñ\87аÑ\81Ñ\82ника Ð¸ ÐµÐ³Ð¾ Ð¾Ð¿Ñ\8bÑ\82",
        "rcfilters-filter-user-experience-level-registered-label": "Зарегистрированные",
        "rcfilters-filter-user-experience-level-registered-description": "Вошедшие редакторы.",
        "rcfilters-filter-user-experience-level-unregistered-label": "Незарегистрированные",
        "rcfilters-filter-user-experience-level-unregistered-description": "Редакторы, которые не вошли в систему.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Новички",
-       "rcfilters-filter-user-experience-level-newcomer-description": "Ð\9cенее 10 Ð¿Ñ\80авок Ð¸ 4 Ð´Ð½ÐµÐ¹ работы.",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Ð\97аÑ\80егиÑ\81Ñ\82Ñ\80иÑ\80ованнÑ\8bе Ñ\80едакÑ\82оÑ\80Ñ\8b Ñ\81 Ð¼ÐµÐ½ÐµÐµ Ñ\87ем 10 Ð¿Ñ\80авками Ð¸ 4 Ð´Ð½Ñ\8fми работы.",
        "rcfilters-filter-user-experience-level-learner-label": "Учащиеся",
-       "rcfilters-filter-user-experience-level-learner-description": "Ð\91олÑ\8cÑ\88е Ð¾Ð¿Ñ\8bÑ\82а, Ñ\87ем Ñ\83 Â«Ð\9dовиÑ\87ков», Ð½Ð¾ Ð¼ÐµÐ½Ñ\8cÑ\88е, Ñ\87ем Ñ\83 Â«Ð\9eпÑ\8bÑ\82нÑ\8bÑ\85 Ð¿Ð¾Ð»Ñ\8cзоваÑ\82елей».",
+       "rcfilters-filter-user-experience-level-learner-description": "Ð\97аÑ\80егиÑ\81Ñ\82Ñ\80иÑ\80ованнÑ\8bе Ñ\80едакÑ\82оÑ\80Ñ\8b, Ñ\87ей Ð¾Ð¿Ñ\8bÑ\82 Ð½Ð°Ñ\85одиÑ\82Ñ\81Ñ\8f Ð³Ð´Ðµ-Ñ\82о Ð¼ÐµÐ¶Ð´Ñ\83 Ñ\83Ñ\80овнÑ\8fми Â«Ð\9dовиÑ\87ок» Ð¸ Â«Ð\9eпÑ\8bÑ\82нÑ\8bе Ð¿Ð¾Ð»Ñ\8cзоваÑ\82ели».",
        "rcfilters-filter-user-experience-level-experienced-label": "Опытные пользователи",
-       "rcfilters-filter-user-experience-level-experienced-description": "Ð\91олее 30 Ð´Ð½ÐµÐ¹ Ð°ÐºÑ\82ивноÑ\81Ñ\82и Ð¸ 500 Ð¿Ñ\80авок.",
+       "rcfilters-filter-user-experience-level-experienced-description": "Ð\97аÑ\80егиÑ\81Ñ\82Ñ\80иÑ\80ованнÑ\8bе Ñ\80едакÑ\82оÑ\80Ñ\8b Ñ\81 Ð±Ð¾Ð»ÐµÐµ Ñ\87ем 500 Ð¿Ñ\80авок Ð¸ 30 Ð´Ð½Ñ\8fми Ð°ÐºÑ\82ивноÑ\81Ñ\82и.",
        "rcfilters-filtergroup-automated": "Автоматизированные вклады",
        "rcfilters-filter-bots-label": "Бот",
        "rcfilters-filter-bots-description": "Правки, сделанные с помощью автоматизированных инструментов.",
        "rcfilters-hideminor-conflicts-typeofchange-global": "Фильтр \"малые правки\" конфликтует с одним или несколькими фильтрами, поскольку некоторые типы правок не могут быть названы малыми. Конфликтные фильтры отмечены вверху, в области Активных фильтров.",
        "rcfilters-hideminor-conflicts-typeofchange": "Определённые типы правок не могут быть названы «малыми», поэтому этот фильтр конфликтует со следующим фильтром типа правок: $1",
        "rcfilters-typeofchange-conflicts-hideminor": "Этот фильтр типа правок конфликтует с фильтром малых правок. Определённые типы правок не могут быть отмечены «малыми».",
-       "rcfilters-filtergroup-lastRevision": "ТекÑ\83Ñ\89аÑ\8f Ð²ÐµÑ\80Ñ\81иÑ\8f",
+       "rcfilters-filtergroup-lastRevision": "Ð\9fоÑ\81ледние Ð²ÐµÑ\80Ñ\81ии",
        "rcfilters-filter-lastrevision-label": "Текущая версия",
-       "rcfilters-filter-lastrevision-description": "Самое последнее изменение на странице.",
-       "rcfilters-filter-previousrevision-label": "Ð\91олее Ñ\80анние Ð²ÐµÑ\80Ñ\81ии",
-       "rcfilters-filter-previousrevision-description": "Все правки, не являющиеся самыми последними на странице.",
+       "rcfilters-filter-lastrevision-description": "ТолÑ\8cко Ñ\81амое последнее изменение на странице.",
+       "rcfilters-filter-previousrevision-label": "Ð\9dе Ð¿Ð¾Ñ\81леднÑ\8fÑ\8f Ð²ÐµÑ\80Ñ\81иÑ\8f",
+       "rcfilters-filter-previousrevision-description": "Все правки, не являющиеся «последней версией».",
        "rcfilters-filter-excluded": "Исключено",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:not</strong> $1",
+       "rcfilters-exclude-button-off": "Исключить выбранное",
+       "rcfilters-exclude-button-on": "Исключение выбранного",
        "rcfilters-view-tags": "Тегированные правки",
        "rcfilters-view-namespaces-tooltip": "Результаты фильтра по пространствам имён",
        "rcfilters-view-tags-tooltip": "Результаты фильтра, использующего метки правок",
        "fileduplicatesearch-noresults": "Не найден файл с именем «$1».",
        "specialpages": "Спецстраницы",
        "specialpages-note-top": "Легенда",
-       "specialpages-note": "* Обычные служебные страницы.\n* <span class=\"mw-specialpagerestricted\">Служебные страницы с ограниченным доступом.</span>",
        "specialpages-group-maintenance": "Отчёты технического обслуживания",
        "specialpages-group-other": "Другие служебные страницы",
        "specialpages-group-login": "Представиться / Зарегистрироваться",
index 010223b..907e6c7 100644 (file)
        "anontalk": "بحث",
        "navigation": "رھنمائي",
        "and": "&#32؛۽",
-       "qbfind": "ڳوليو",
-       "qbbrowse": "جھانگيو",
-       "qbedit": "سنواريو",
-       "qbpageoptions": "هيءُ صفحو",
-       "qbmyoptions": "منهنجا صفحا",
        "faq": "ڪپس",
-       "faqpage": "Project:ڪپس",
        "actions": "ڪارگذاريون",
        "namespaces": "نانءُپولارَ",
        "variants": "بَدَلَ",
        "edit-local": "مقامي تشريح کي ترميميو",
        "create": "سرجيو",
        "create-local": "مقامي تشريح ڏيو",
-       "editthispage": "هيءُ صفحو سنواريو",
-       "create-this-page": "اهو صفحو نئين سر جوڙيو",
        "delete": "ڊاھيو",
-       "deletethispage": "هيءُ صفحو ڊاهيو",
-       "undeletethispage": "هيءُ صفحو اڻ ڊاهيو",
        "undelete_short": "اڻڊاهيو {{PLURAL:$1|هڪ ترميم|$1 ترميمون}}",
        "viewdeleted_short": "ڏسو {{PLURAL:$1|هڪ ڊاٺل ترميم|$1 ڊاٺل ترميمون}}",
        "protect": "تحفظيو",
        "protect_change": "تبديل ڪريو",
-       "protectthispage": "هيءُ صفحو تحفظيو",
        "unprotect": "تحفظ بدلايو",
-       "unprotectthispage": "هن صفحي جو تحفظ بدلايو",
        "newpage": "نئون صفحو",
-       "talkpage": "هن صفحي تي بحث ڪريو",
        "talkpagelinktext": "بحث",
        "specialpage": "خاص صفحو",
        "personaltools": "ذاتي اوزار",
-       "articlepage": "مسودو ڏسو",
        "talk": "بحث",
        "views": "ڏيٺون",
        "toolbox": "اوزارَ",
        "tool-link-userrights": "{{GENDER:$1|يوزر}} گروھ تبديل ڪريو",
        "tool-link-userrights-readonly": "{{GENDER:$1|يوزر}} گروھ ڏسو",
        "tool-link-emailuser": "ھن {{GENDER:$1|يوزر}} ڏانھن برقٽپال موڪليو",
-       "userpage": "يوزر صفحو ڏسو",
-       "projectpage": "رٿائي صفحو ڏسو",
        "imagepage": "ذريعاتي صفحو ڏسو",
        "mediawikipage": "نياپي جو صفحو ڏسو",
        "templatepage": "سانچي جو صفحو ڏسو",
        "rcfilters-search-placeholder": "تازيون تبديليون ڇاڻيو (جھانگيو يا لکڻ شروع ڪريو)",
        "rcfilters-empty-filter": "ڪي بہ سرگرم ڇاڻيون ناھن. سڀ ڀاڱيداريون ڏيکاريل آھن.",
        "rcfilters-filterlist-title": "ڇاڻيون",
-       "rcfilters-filterlist-whatsthis": "Ù\87Ù\8a Ú\87ا Ø¢Ù\87Ù\8a؟",
+       "rcfilters-filterlist-whatsthis": "Ù\87Ù\8a ÚªÙ\8aئÙ\86 ÚªÙ\85 ÚªÙ\86 Ù¿Ø§؟",
        "rcfilters-highlightbutton-title": "نتيجن کي نمايان (هاءِ لائيٽ) ڪيو",
        "rcfilters-highlightmenu-title": "رنگ چونڊيو",
-       "rcfilters-filter-registered-label": "رجسٽر ٿيل",
-       "rcfilters-filter-unregistered-label": "اڻ رجسٽر ٿيل",
        "rcfilters-filter-editsbyself-label": "مون پاران تبديليون",
        "rcfilters-filter-editsbyother-label": "ٻين پاران تبديليون",
+       "rcfilters-filter-user-experience-level-registered-label": "رجسٽر ٿيل",
+       "rcfilters-filter-user-experience-level-unregistered-label": "اڻرجسٽر ٿيل",
        "rcfilters-filter-user-experience-level-newcomer-label": "نوان ايندڙ",
        "rcfilters-filter-user-experience-level-learner-label": "سکندڙ",
        "rcfilters-filter-user-experience-level-experienced-label": "تجربيڪار واھپ",
        "tooltip-compareselectedversions": "هن صفحي جن ٻن چونڊيل پرتن درميان تفاوت ڏسو.",
        "tooltip-watch": "هيءُ صفحو پنهنجي نظر ۾ فھرست ۾ شامل ڪريو",
        "tooltip-rollback": "\"واپس ورايو\" ھن صفحي ۾ پوئين ڀاڱيدار جي ڪيل ترميم(ن) کي ھڪ ٽڙڪ سان اڻڪري ٿو",
+       "tooltip-preferences-save": "ترجيحون سانڍيو",
        "tooltip-summary": "ننڍو خلاصو ڏيو",
        "anonymous": "گمنام {{PLURAL:$1|يوزر|يوزرس}} جو {{SITENAME}}",
        "simpleantispam-label": "اينٽي-اسپام روڪ.\nھن کي <strong>نہ</strong> ڀريو!",
index 368e648..824851e 100644 (file)
        "retrievedfrom": "Yurrid z \"$1\"",
        "youhavenewmessages": "{{PLURAL:$3|{{GENDER:$3|ⴷⴰⵔⴽ|ⴷⴰⵔⵎ}}}} $1 ($2).",
        "newmessageslinkplural": "{{PLURAL:$1|ⵜⵓⵣⵉⵏⵜ ⵜⴰⵎⴰⵢⵏⵓⵜ|ⵜⵓⵣⵉⵏⵉⵏ ⵜⵉⵎⴰⵢⵏⵓⵜⵉⵏ}}",
-       "newmessagesdifflinkplural": "{{PLURAL:$1|â´°âµ\99âµ\8fâ´¼âµ\8d âµ\89ⴳⴳⵯâµ\94â´°âµ\8f|âµ\89âµ\99âµ\8fâ´¼ⵍⵏ ⴳⴳⵯⵔⴰⵏⵉⵏ}}",
+       "newmessagesdifflinkplural": "{{PLURAL:$1|âµ\93âµ\99âµ\8fâ´¼âµ\8d âµ\89ⴳⴳⵯâµ\94â´°âµ\8f|âµ\89âµ\99âµ\8fâ´¼âµ\89ⵍⵏ ⴳⴳⵯⵔⴰⵏⵉⵏ}}",
        "youhavenewmessagesmulti": "{{GENDER:|ⴷⴰⵔⴽ|ⴷⴰⵔⵎ}} ⵜⵓⵣⵉⵏⵉⵏ ⵜⵉⵎⴰⵢⵏⵓⵜⵉⵏ ⴳ $1",
        "editsection": "ⵙⵏⴼⵍ",
        "editold": "ⵙⵏⴼⵍ",
        "notloggedin": "Ur tmlit mat git",
        "createaccount": "Murzm amidan nek (lkunt)..",
        "createaccountmail": "S tirawt taliktunant",
-       "createacct-benefit-body1": "{{PLURAL:$1|ⴰⵙⵏⴼⵍ|ⵉⵙⵏⴼⵍⵏ}}",
+       "createacct-benefit-body1": "{{PLURAL:$1|â´°âµ\99âµ\8fâ´¼âµ\8d|âµ\89âµ\99âµ\8fâ´¼âµ\89âµ\8dâµ\8f}}",
        "createacct-benefit-body2": "{{PLURAL:$1|ⵜⴰⵙⵏⴰ|ⵜⴰⵙⵏⵉⵡⵉⵏ}}",
        "createacct-benefit-body3": "{{PLURAL:$1|ⴰⵏⴰⵎⵓ ⵉⴳⴳⵯⵔⴰⵏ|ⵉⵏⴰⵎⵓⵜⵏ ⴳⴳⵯⵔⴰⵏⵉⵏ}}",
        "badretype": "Tasarut lin tgit ur dis tucka.",
        "action-delete": "ⴽⴽⵙ ⵜⴰⵙⵏⴰ ⴰⴷ",
        "nchanges": "$1 {{PLURAL:$1|ⵓⵙⵏⴼⵍ|ⵉⵙⵏⴼⵍⵏ}}",
        "enhancedrc-history": "ⴰⵎⵣⵔⵓⵢ",
-       "recentchanges": "ⵉⵙⵏⴼⵍⵏ ⴳⴳⵯⵔⴰⵏⵉⵏ",
+       "recentchanges": "âµ\89âµ\99âµ\8fâ´¼âµ\89âµ\8dâµ\8f â´³â´³âµ¯âµ\94â´°âµ\8fâµ\89âµ\8f",
        "recentchanges-legend": "Tixtiɣitin (options) n imbddl imaynutn",
        "recentchanges-summary": "Ml imbddln imaynutn  n wiki ɣ tasna yad",
        "recentchanges-feed-description": "ⴹⴼⵓⵔ ⵉⵙⵏⴼⵍⵏ ⴰⴽⴽⵯ ⵉⴳⴳⵯⵔⴰⵏ ⵏ ⵓⵡⵉⴽⵉ ⴳ ⵉⴼⵉⵍⵉ ⴰⴷ.",
        "recentchanges-label-minor": "ⵡⴰⴷ ⵉⴳⴰ ⴰⵙⵏⴼⵍ ⵓⵎⵥⵉⵢ",
        "recentchanges-label-bot": "ⴰⵙⵏⴼⵍ ⴰⴷ ⵉⵙⴽⵔ ⵜ ⵢⴰⵏ ⵓⵔⵓⴱⵓ",
        "recentchanges-label-unpatrolled": "Ambddl ad ura jju ittmẓra",
+       "recentchanges-label-plusminus": "ⵜⵏⴼⵍ ⵜⵉⴷⴷⵉ ⵏ ⵜⴰⵙⵏⴰ ⵙ ⵡⵓⵟⵟⵓⵏ ⴰⴷ ⵏ ⵉⴷ ⴱⴰⵢⵜ",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (ⵥⵔ ⵓⵍⴰ [[Special:NewPages|ⵜⴰⵍⴳⴰⵎⵜ ⵏ ⵜⴰⵙⵏⵉⵡⵉⵏ ⵜⵉⵎⴰⵢⵏⵓⵜⵉⵏ]])",
        "rcfilters-savedqueries-new-name-label": "ⵉⵙⵎ",
        "rcfilters-filterlist-whatsthis": "ⵎⴰⵜⵜⴰ ⵓⵢⴰ?",
        "rcfilters-filter-bots-label": "ⴱⵓⵜ",
        "rcnotefrom": "Had imbddln lli ittuyskarn z '''$2''' ('''$1''' ɣ uggar).",
        "rclistfrom": "Mel imbdeltn imaynutn z $3 $2",
-       "rcshowhideminor": "$1 ⵉⵙⵏⴼⵍⵏ ⵓⵎⵥⵉⵢⵏ",
+       "rcshowhideminor": "$1 âµ\89âµ\99âµ\8fâ´¼âµ\89âµ\8dâµ\8f âµ\93âµ\8eâµ¥âµ\89âµ¢âµ\8f",
        "rcshowhideminor-hide": "ⵙⵙⵏⵜⵍ",
        "rcshowhidebots": "$1 ⵉⴷ ⴱⵓⵜ",
        "rcshowhidebots-hide": "ⵙⵙⵏⵜⵍ",
        "recentchangeslinked": "Imbddel zun ɣwid",
        "recentchangeslinked-feed": "Imbddeln zund ɣwid",
        "recentchangeslinked-toolbox": "Imbddeln zund ɣwid",
-       "recentchangeslinked-title": "ⵉⵙⵏⴼⵍⵏ ⵇⵇⵏⵏⵉⵏ ⵙ \"$1\"",
+       "recentchangeslinked-title": "âµ\89âµ\99âµ\8fâ´¼âµ\89âµ\8dâµ\8f âµ\87âµ\87âµ\8fâµ\8fâµ\89âµ\8f âµ\99 \"$1\"",
        "recentchangeslinked-summary": "Ɣid umuɣ iymbddeln li ittyskarnin tigira yad ɣ tisniwin li ittuyzdayn d kra n tasna (ulla i igmamn n kra taggayt ittuyzlayn). Tisniwin  ɣ [[Special:Watchlist|Umuɣ n tisniwin li ttsaggat]].",
        "recentchangeslinked-page": "ⵉⵙⵎ ⵏ ⵜⴰⵙⵏⴰ:",
        "recentchangeslinked-to": "Afficher les changements vers les pages liées au lieu de la page donnée\nMel imbddeln z tisniwin li ittuyzdayni bla tasna li trit.",
        "listusers": "ⵜⴰⵍⴳⴰⵎⵜ ⵏ ⵉⵙⵎⵔⴰⵙⵏ",
        "usercreated": "{{GENDER:$3|ⵉⵙⵏⵓⵍⴼⴰ|ⵜⵙⵏⵓⵍⴼⴰ}} ⴳ $1 ⴳ $2",
        "newpages": "ⵜⴰⵙⵏⵉⵡⵉⵏ ⵜⵉⵎⴰⵢⵏⵓⵜⵉⵏ",
-       "move": "âµ\99âµ\8eâ´°ⵜⵜⵉ",
+       "move": "âµ\99âµ\8eâµ\93ⵜⵜⵉ",
        "movethispage": "ⵙⵎⴰⵜⵜⵉ ⵜⴰⵙⵏⴰ ⴰⴷ",
        "unusedcategoriestext": "Taggayin ad llant waxxa gis nt ur tlli kra n tasna wala kra n taggayin yaḍnin",
        "notargettitle": "F walu",
        "restriction-type": "ⵜⵓⵔⴰⴳⵜ:",
        "restriction-level": "Restriction level:",
        "restriction-edit": "ⵙⵏⴼⵍ",
-       "restriction-move": "âµ\99âµ\8eâ´°ⵜⵜⵉ",
+       "restriction-move": "âµ\99âµ\8eâµ\93ⵜⵜⵉ",
        "undeletelink": "mel/rard",
        "undeleteviewlink": "Ẓṛ",
        "undelete-search-submit": "ⵙⵉⴳⴳⵍ",
        "tooltip-ca-unprotect": "ⵙⵏⴼⵍ ⴰⴼⵔⴰⴳ ⵏ ⵜⴰⵙⵏⴰ ⴰⴷ",
        "tooltip-ca-delete": "ⴽⴽⵙ ⵜⴰⵙⵏⴰ ⴰⴷ",
        "tooltip-ca-undelete": "Rard imbddeln imzwura li ittyskarnin ɣ tasna yad",
-       "tooltip-ca-move": "âµ\99âµ\8eâ´°ⵜⵜⵉ ⵜⴰⵙⵏⴰ ⴰⴷ",
+       "tooltip-ca-move": "âµ\99âµ\8eâµ\93ⵜⵜⵉ ⵜⴰⵙⵏⴰ ⴰⴷ",
        "tooltip-ca-watch": "ⵔⵏⵓ ⵜⴰⵙⵏⴰ ⴰⴷ ⵉ ⵜⵍⴳⴰⵎⵜ {{GENDER:|ⵏⵏⴽ|ⵏⵏⵎ}} ⵏ ⵓⴹⴼⴼⵓⵔ",
        "tooltip-ca-unwatch": "ⵙⵉⵜⵜⵉ ⵜⴰⵙⵏⴰ ⴰⴷ ⵣⴳ ⵜⵍⴳⴰⵎⵜ {{GENDER:|ⵏⵏⴽ|ⵏⵏⵎ}} ⵏ ⵓⴹⴼⴼⵓⵔ",
        "tooltip-search": "ⵙⵉⴳⴳⵍ ⴳ {{SITENAME}}",
        "tooltip-n-mainpage-description": "Kid tasna tamuqrant",
        "tooltip-n-portal": "f' usenfar, matzdart atitskrt, maniɣrattaft ɣayli trit",
        "tooltip-n-currentevents": "Tiɣri izrbn i kullu maɣid immusn",
-       "tooltip-n-recentchanges": "ⵜⴰⵍⴳⴰⵎⵜ ⵏ ⵉⵙⵏⴼⵍⵏ ⴳⴳⵯⵔⴰⵏⵉⵏ ⴳ ⵓⵡⵉⴽⵉ",
+       "tooltip-n-recentchanges": "âµ\9câ´°âµ\8dⴳⴰâµ\8eâµ\9c âµ\8f âµ\89âµ\99âµ\8fâ´¼âµ\89âµ\8dâµ\8f â´³â´³âµ¯âµ\94â´°âµ\8fâµ\89âµ\8f â´³ âµ\93ⵡâµ\89â´½âµ\89",
        "tooltip-n-randompage": "Srbu yat tasna ɣik nna ka tga",
        "tooltip-n-help": "Adɣar n w-aws",
        "tooltip-t-whatlinkshere": "Umuɣ n kullu tisnatin n Wiki lid ilkkmn ɣid",
        "tags-active-no": "ⵓⵀⵓ",
        "tags-edit": "ⵙⵏⴼⵍ",
        "tags-delete": "ⴽⴽⵙ",
-       "tags-hitcount": "$1 {{PLURAL:$1|ⵓⵙⵏⴼⵍ|ⵉⵙⵏⴼⵍⵏ}}",
+       "tags-hitcount": "$1 {{PLURAL:$1|âµ\93âµ\99âµ\8fâ´¼âµ\8d|âµ\89âµ\99âµ\8fâ´¼âµ\89âµ\8dâµ\8f}}",
        "tags-create-submit": "ⵙⵏⵓⵍⴼⵓ",
        "comparepages": "ⵙⵎⵣⴰⵣⴰⵍ ⵜⴰⵙⵏⵉⵡⵉⵏ",
        "compare-page1": "ⵜⴰⵙⵏⴰ 1",
index a678e3b..ce26e62 100644 (file)
@@ -6,7 +6,18 @@
        },
        "tog-underline": "لنک  ہیٹھ لکیر",
        "tog-hideminor": "چھوٹیاں تبدیلیاں لُکاؤ",
+       "tog-numberheadings": "سرخیاں کوں خود کار نمبر ݙیوو",
        "tog-showtoolbar": "آلات ترمیم ݙکھاؤ",
+       "tog-editondblclick": "ڈبل کلک نال ورقے وچ تبدیلیاں کرو",
+       "tog-uselivepreview": "براہ راست نمائش ورتو",
+       "underline-always": "ہمیشہ",
+       "underline-never": "کݙاہیں وی کائناں",
+       "underline-default": "سکن یا براؤزر دا طے شدہ",
+       "editfont-style": "خانہ ترمیم دا فونٹ",
+       "editfont-default": "براؤزر دا طے شدہ",
+       "editfont-monospace": "مونوسپیسڈ فونٹ",
+       "editfont-sansserif": "سنس سیرف فونٹ",
+       "editfont-serif": "سیرف فونٹ",
        "sunday": "اتوار",
        "monday": "سونوار",
        "tuesday": "منگل",
        "oct": "اکتوبر",
        "nov": "نومبر",
        "dec": "دسمبر",
+       "january-date": "$1 جنوری",
+       "february-date": "$1 فروری",
+       "march-date": "$1 مارچ",
+       "april-date": "$1 اپریل",
+       "may-date": "$1 مئی",
+       "june-date": "$1 جون",
+       "july-date": "$1 جولائی",
+       "august-date": "$1 اگست",
+       "september-date": "$1 ستمبر",
+       "october-date": "$1 اکتوبر",
+       "november-date": "$1 نومبر",
+       "december-date": "$1 دسمبر",
+       "period-am": "سویر",
+       "period-pm": "شام",
        "pagecategories": "{{PLURAL:$1|زمرہ|زمرہ جات}}",
        "category_header": "زمرہ \"$1\" وچ ورقے",
        "subcategories": "ذیلی زمرہ جات",
        "noindex-category": "غیر فہرست شدہ صفحات",
        "broken-file-category": "ٹٹے ہوۓ جوڑاں آلے صفحے",
        "about": "تعارف",
+       "article": "مواد آلا ورقہ",
        "newwindow": "(نویں ونڈو وچ کھولو)",
        "cancel": "مکاؤ",
+       "moredotdotdot": "ٻئے",
+       "mypage": "ورقہ",
        "mytalk": "ڳالھ مہاڑ",
+       "anontalk": "ڳالھ مہاڑ",
        "navigation": "رہنمائی",
        "and": "&#32;تے",
+       "faq": "عام طور تے پچھے ونڄݨ آلے سوال",
+       "actions": "کم",
        "namespaces": "ناں دیاں جہاواں",
        "variants": "قسماں",
        "navigation-heading": "فہرست رہنمائی",
+       "errorpagetitle": "نقص",
        "returnto": "واپس $1 چلو",
        "tagline": " {{SITENAME}} توں",
        "help": "مدد",
        "search": "کھوج",
        "searchbutton": "کھوج",
+       "go": "ڄلو",
        "searcharticle": "ڄلو",
        "history": "پچھلے کم",
        "history_short": "تاریخچہ",
+       "history_small": "تاریخچہ",
        "printableversion": "چھپݨ جوگا ورقہ",
        "permalink": "پکا جوڑ",
+       "print": "چھاپو",
        "view": "ݙکھالے",
        "view-foreign": "$1 تے ݙیکھو",
        "edit": "لکھو",
        "create": "بݨاؤ",
        "create-local": "آپنی لکھت رلاؤ",
        "delete": "مٹاؤ",
+       "protect": "حفاظت کرو",
+       "protect_change": "تبدیل کرو",
+       "unprotect": "تحفظ وچ تبدیلی",
        "newpage": "نواں ورقہ",
        "talkpagelinktext": "ڳالھ مہاڑ",
+       "specialpage": "خاص ورقہ",
        "personaltools": "ذاتی آوزار",
        "talk": "ڳالھ مہاڑ",
        "views": "ݙکھالے",
        "toolbox": "آوزار",
+       "tool-link-emailuser": "ایں {{GENDER:$1|صارف}} کوں ای میل کرو",
+       "imagepage": "فائل آلا ورقہ ݙیکھو",
+       "mediawikipage": "سنیہے آلا ورقہ ݙیکھو",
+       "templatepage": "سانچے آلا ورقہ ݙیکھو",
+       "viewhelppage": "مدد آلا ورقہ ݙیکھو",
+       "categorypage": "کیٹاگری آلا ورقہ ݙیکھو",
+       "viewtalkpage": "مباحثہ ݙیکھو",
        "otherlanguages": "ٻنھاں زباناں وچ",
        "redirectedfrom": "($1 کنوں ولدا رجوع )",
        "redirectpagesub": "صفحہ ریڈائریکٹ کرو",
        "redirectto": "اڳے کرو:",
        "lastmodifiedat": "ایہ ورقہ چھیکڑی واری  $1 کوں $2 تے تبدیل تھیا ہائی۔",
+       "protectedpage": "آم شام ورقہ",
        "jumpto": "ٹپ مارو",
        "jumptonavigation": "رہنمائی",
        "jumptosearch": "ڳولو",
        "disclaimers": "لاتعلقی اظہار",
        "disclaimerpage": "Project:عام لاتعلقی اظہار",
        "edithelp": "لکھݨ وچ مدد",
+       "helppage-top-gethelp": "مدد",
        "mainpage": "وݙا ورقہ",
        "mainpage-description": "پہلا ورقہ",
        "portal": "بیٹھک",
        "portal-url": "Project:دیوان عام",
        "privacy": "پرائیویسی پالیسی",
        "privacypage": "Project:پرائیویسی پالیسی",
+       "ok": "ٹھیک ہے",
        "retrievedfrom": "\"$1\" توں گھدا",
        "youhavenewmessages": "{{PLURAL:$3| تہاݙے کیتے}} $1 ($2).",
+       "newmessageslinkplural": "{{PLURAL:$1|نواں سنیہا|999=نویں سنیہے}}",
        "newmessagesdifflinkplural": "چھیکڑی {{PLURAL:$1|تبدیلی|تبدیلیاں}}",
        "editsection": "لکھو",
        "editold": "لکھو",
        "viewsourcelink": "ماخذ ݙیکھو",
        "editsectionhint": "حصہ لکھو: $1",
        "toc": "حصے",
+       "showtoc": "ݙیکھاؤ",
+       "hidetoc": "لُکاؤ",
+       "collapsible-collapse": "لکاؤ",
+       "collapsible-expand": "ودھاؤ",
+       "confirmable-yes": "ڄیا",
+       "confirmable-no": "کو",
+       "viewdeleted": "ݙیکھو $1؟",
+       "feedlinks": "فیڈ",
        "site-atom-feed": "$1 اٹوم فیڈ",
        "page-atom-feed": "$1 اٹوم فیڈ",
        "red-link-title": "$1 (ایہ ورقہ اڄݨ تائیں کائنی بݨیا)",
        "nstab-main": "ورقہ",
        "nstab-user": "صفحۂ صارف",
+       "nstab-media": "میڈیا آلا ورقہ",
        "nstab-special": "خاص ورقہ",
        "nstab-project": "پروجیکٹ ورقہ",
        "nstab-image": "فائل",
        "nstab-mediawiki": "سنیہہ",
        "nstab-template": "سانچہ",
+       "nstab-help": "مدد ورقہ",
        "nstab-category": "زمرہ",
        "mainpage-nstab": "وݙا ورقہ",
+       "nosuchaction": "کوئی اینجھا کم کائنی",
        "nosuchspecialpage": "اینجھا کوئی خاص ورقہ کائنی",
+       "error": "نقص",
+       "databaseerror": "ڈیٹابیس دی غلطی",
+       "databaseerror-error": "نقص: $1",
        "badtitle": "بھیڑا عنوان",
        "viewsource": "ماخذ ݙیکھو",
        "viewsource-title": "$1 دا مسودہ ݙیکھو",
+       "yourname": "صارف دا ناں",
        "userlogin-yourname": "صارف ناں",
        "userlogin-yourname-ph": "آپݨا ورتݨ ناں صارف درج کرو",
+       "createacct-another-username-ph": "آپݨا ورتݨ ناں صارف درج کرو",
+       "yourpassword": "پاس ورڈ",
        "userlogin-yourpassword": "پاس ورڈ",
        "userlogin-yourpassword-ph": "پاس ورڈ درج کرو",
        "createacct-yourpassword-ph": "پاس ورڈ درج کرو",
+       "yourpasswordagain": "پاس ورڈ ولدا لکھو",
        "createacct-yourpasswordagain": "پاس ورڈ دی تصدیق کرو",
        "createacct-yourpasswordagain-ph": "پاس ورڈ ولدا درج کرو",
        "userlogin-remembermypassword": "میکوں لاگ ان رکھو",
+       "userlogin-signwithsecure": "محفوظ رابطہ (کنکشن) استعمال کرو",
+       "cannotlogin-title": "لاگ ان نی تھی سڳدے",
+       "cannotlogin-text": "لاگ ان تھیوݨ ناممکن ہے",
+       "cannotloginnow-title": "ہݨ لاگ ان نہوے تھی سڳدے",
        "login": "لاگ ان تھیوو",
+       "logout": "لاگ آؤٹ",
+       "userlogout": "لاگ آؤٹ",
+       "notloggedin": "لاگ ان نہوے تھئے",
        "userlogin-noaccount": "تہاݙا کھاتہ کائنی؟",
        "userlogin-joinproject": "جُڑ ونڄو {{SITENAME}} نال",
        "createaccount": "کھاتہ کھولو",
        "userlogin-resetpassword-link": "پاسورڈ بھل ڳئے ہو؟",
        "userlogin-helplink2": "لاگ ان تھیوݨ کیتے مدد دی لوڑ ہے؟",
+       "createacct-emailrequired": "ای میل پتہ",
        "createacct-emailoptional": "ای-میل پتہ، آپشنل",
        "createacct-email-ph": "اپنا ای-میل پتہ لکھو",
+       "createacct-another-email-ph": "اپنا ای-میل پتہ لکھو",
+       "createacct-reason": "سبب",
        "createacct-submit": "اپݨاں کھاتا کھولو",
+       "createacct-another-submit": "کھاتہ کھولو",
        "createacct-benefit-heading": "{{SITENAME}} تہاݙے وانگوں علم دوست افراد دا مرہون منت ہے۔",
        "createacct-benefit-body1": "$1 {{PLURAL:$1|تبدیلی|تبدیلیاں}}",
        "createacct-benefit-body2": "\n$1 {{PLURAL:$1|ورقہ|ورقے}}",
        "createacct-benefit-body3": "ہݨ دے {{PLURAL:$1|کم|کماں}}",
+       "loginerror": "لاگ ان وچ غلطی",
+       "createacct-error": "کھاتہ بݨاوݨ وچ غلطی",
+       "loginsuccesstitle": "لاگ ان تھی ڳیا",
+       "mailmypassword": "نواں پاس ورڈ بݨاؤ",
+       "accountcreated": "کھاتہ کھل ڳیا",
        "loginlanguagelabel": "زبان: $1",
        "pt-login": "لاگ ان تھیوو",
        "pt-login-button": "لاگ ان تھیوو",
        "pt-createaccount": "کھاتہ کھولو",
        "pt-userlogout": "لاگ آؤٹ",
+       "changepassword": "پاس ورڈ تبدیل کرو",
+       "oldpassword": "پراݨا پاس ورڈ",
+       "newpassword": "نواں پاس ورڈ",
+       "retypenew": "نواں پاس ورڈ ولدا لکھو",
+       "botpasswords-label-create": "بݨاؤ",
+       "botpasswords-label-update": "اپ ݙیٹ",
+       "botpasswords-label-cancel": "منسوخ",
+       "botpasswords-label-delete": "مٹاؤ",
+       "botpasswords-label-resetpassword": "پاس ورڈ تبدیل کرو",
+       "resetpass-submit-loggedin": "پاس ورڈ تبدیل کرو",
+       "resetpass-submit-cancel": "منسوخ",
+       "resetpass-temp-password": "عارضی لنگھݨ لفظ، پاس ورڈ",
        "passwordreset": "نواں پاس ورڈ بݨاؤ",
+       "passwordreset-username": "صارف دا ناں",
+       "passwordreset-domain": "ڈومین",
+       "passwordreset-email": "ای میل پتہ",
+       "passwordreset-emailtitle": "{{SITENAME}} کھاتہ دی تفصیلات",
+       "changeemail-none": "(کوئی وی کائنی)",
+       "changeemail-password": "تہاݙا {{SITENAME}} پاس ورڈ:",
+       "changeemail-submit": "ای-میل بدلو",
+       "resettokens-tokens": "ٹوکن",
+       "resettokens-token-label": "$1 (موجودہ قدر: $2)",
        "bold_sample": "موٹی لکھائی",
        "bold_tip": "موٹی لکھائی",
        "italic_sample": "ترچھا متن",
        "sig_tip": "تہاݙے دستخط ویلے دے نال",
        "hr_tip": "اُفقی لکیر (زیادہ استعمال نہ کریں)",
        "summary": "خلاصہ",
+       "subject": "عنوان:",
        "minoredit": "ایہ ہک چھوٹی تبدیلی ہے",
        "watchthis": "ایں ورقے تے اکھ رکھو",
        "savearticle": "محفوظ",
+       "savechanges": "تبدیلیاں محفوظ کرو",
+       "publishpage": "ورقہ شائع کرو",
+       "publishchanges": "تبدیلیاں شائع کرو",
        "preview": "نمائش",
        "showpreview": "نمائش",
        "showdiff": "تبدیلیاں ݙکھاؤ",
        "loginreqlink": "لاگ ان",
+       "newarticle": "(نواں)",
        "userpage-userdoesnotexist-view": "صارف کھاتہ \"$1\" رجسٹرڈ کائنی۔",
        "continue-editing": "خانہ ترمیم وچ ونڄو",
        "editing": "تساں \"$1\" لکھدے پئے ہو",
        "creating": "زیر تخلیق $1",
        "editingsection": "«$1» دے قطعہ دی ترمیم",
+       "yourtext": "تہاݙی لکھائی",
+       "yourdiff": "فرق",
        "templatesused": "ایں ورقے تے  ورتے ڳئے {{PLURAL:$1|سانچے|سانچہ}}:",
+       "templatesusedpreview": "ایں کچے کم تے  ورتے ڳئے {{PLURAL:$1|سانچے|سانچہ}}:",
        "template-protected": "(بچایا گیا)",
        "template-semiprotected": "(نیم محفوظ)",
        "hiddencategories": "ایہ ورقہ {{PLURAL:$1|1 لُکے زمریاں|$1 لکا زمرہ }} وچ شامل ہے:",
        "permissionserrors": "خطائے اجازت",
        "moveddeleted-notice": "ایہ ورقہ مٹایا ڳیا ہے۔ مٹاوݨ دا لاگ ہیٹھاں ݙتا ہویا ہے",
        "content-model-wikitext": "ویکی متن",
+       "content-model-text": "سادہ متن",
+       "content-model-javascript": "جاوا  سکرپٹ",
+       "content-json-empty-object": "خالی آبجیکٹ",
+       "content-json-empty-array": "خالی ایرے",
        "undo-failure": "متنازع تبدیلیاں پاروں ایہ تبدیلی واپس نی تھی سڳدی۔",
        "viewpagelogs": "صفحے دے لاگ ݙیکھو",
        "currentrev-asof": "حالیہ نسخہ بمطابق $1",
        "nextrevision": "نویں تبدیلی →",
        "currentrevisionlink": "موجودہ حالت",
        "cur": " رائج",
+       "next": "اڳوں تے",
        "last": "پچھلا",
+       "page_first": "پہلا",
+       "page_last": "چھیکڑی",
        "history-fieldset-title": "دہرائی کیتے لبھت",
        "histfirst": "قدیم ترین",
        "histlast": "تازہ ترین",
+       "historyempty": "(خالی)",
        "history-feed-title": "ریویژن رکارڈ",
+       "history-feed-description": "وکی تے ایں ورقے دی ریویژن ہسٹری",
        "history-feed-item-nocomment": "$2 کوں $1",
        "rev-delundel": "ݙکھاؤ/لکاؤ",
+       "rev-showdeleted": "ݙیکھاؤ",
+       "revdelete-show-file-submit": "ڄیا",
+       "revdelete-hide-comment": "تبدیلی دا خلاصہ",
+       "mergehistory-reason": "سبب",
        "mergelog": "لاگ رلاؤ",
+       "revertmerge": "وکھریاں کرو",
        "history-title": "\"$1\" دا ریکارڈ",
        "difference-title": "\"$1\" دے نسخیاں دے درمیان فرق",
        "lineno": "سطر $1:",
        "searchresults-title": "\"$1\" دے کھوج نتارے",
        "prevn": "پچھلے {{PLURAL:$1|$1}}",
        "nextn": "اگلے {{PLURAL:$1|$1}}",
+       "prev-page": "پچھلا ورقہ",
+       "next-page": "اڳلا ورقہ",
        "prevn-title": "پہلے $1 {{PLURAL:$1|نتیجے}}",
        "nextn-title": "اگلے $1 {{PLURAL:$1|نتیجے}}",
        "shown-title": "وکھاؤ $1 {{PLURAL:$1|نتیجے}}",
        "search-suggest": "بھلا تہاݙا مطلب ہائی: $1",
        "searchall": "یکے",
        "search-nonefound": "سوال دے نال رلدے ملدے نتارے کائنی۔",
+       "powersearch-togglelabel": "ݙیکھو",
+       "powersearch-toggleall": "یکے",
+       "powersearch-togglenone": "کوئی وی کائنی",
+       "preferences": "ترجیحات",
        "mypreferences": "ترجیحات",
+       "prefs-edits": "تبدیلیاں دی گنتی:",
+       "prefsnologintext2": "آپݨیاں ترجیہاں تبدیل کرݨ کیتے لاگ ان تھیوو",
+       "prefs-skin": "جِلد",
+       "skin-preview": "نمائش",
+       "datedefault": "کوئی ترجیح کائنی",
+       "saveprefs": "بچاؤ",
+       "searchresultshead": "ڳولو",
+       "stub-threshold-sample-link": "نمونہ",
+       "stub-threshold-disabled": "غیر فعال",
+       "servertime": "سرور دا وقت:",
+       "guesstimezone": "براؤزر توں بھرو۔",
+       "timezoneregion-africa": "افریقہ",
+       "timezoneregion-america": "امریکہ",
+       "timezoneregion-antarctica": "انٹارکٹیکا",
+       "timezoneregion-arctic": "قطب شمالی",
+       "timezoneregion-asia": "ایشیاء",
+       "timezoneregion-atlantic": "بحر اوقیانوس",
+       "timezoneregion-australia": "آسٹریلیا",
+       "timezoneregion-europe": "یورپ",
+       "timezoneregion-indian": "بحر ہند",
+       "timezoneregion-pacific": "بحر الکاہل",
+       "prefs-searchoptions": "ڳولو",
+       "prefs-namespaces": "ناں دیاں جہاواں",
+       "default": "پہلے کنوں طے تھیا ہویا",
+       "prefs-files": "فائلاں",
+       "prefs-custom-css": "کسٹم سی ایس ایس",
+       "prefs-custom-js": "کسٹم جاوا سکرپٹ",
+       "prefs-emailconfirm-label": "ای میل دی تصدیق",
+       "youremail": "ای میل",
+       "prefs-registration": "رجسٹریشن ویلہ:",
+       "yourrealname": "اصلی ناں:",
+       "yourlanguage": "زبان",
+       "email": "ای میل",
+       "prefs-info": "بنیادی معلومات",
+       "prefs-i18n": "بین الاقوامیت",
+       "prefs-signature": "دستخط",
+       "prefs-dateformat": "تاریخ دی ترتیب",
+       "prefs-timeoffset": "وقت دی ترتیب",
+       "prefs-advancedediting": "عام آپشن",
+       "prefs-editor": "خانہ ترمیم",
+       "prefs-preview": "نمائش",
+       "prefs-tokenwatchlist": "ٹوکن",
+       "group": "گروپ:",
+       "group-user": "ورتݨ آلے",
        "group-bot": "بوٹ",
        "group-sysop": "منتظمین",
+       "group-all": "(سارے)",
        "grouppage-bot": "{{ns:project}}:بوٹ",
        "grouppage-sysop": "{{ns:project}}:ایڈمنسٹریٹر",
        "right-writeapi": "اے پی آئی تحریر دا استعمال",
        "recentchanges-label-plusminus": "ورقے دا تبدیل شدہ حجم بلحاظ تعداد بائٹ",
        "recentchanges-legend-heading": "<strong>اختصارات:</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (ایہ وی ݙیکھو [[Special:NewPages|نویں ورقیاں دی لسٹ]])",
+       "recentchanges-submit": "ݙیکھاؤ",
+       "rcfilters-savedqueries-rename": "نواں ناں لکھو",
+       "rcfilters-savedqueries-setdefault": "ݙیفالٹ بݨاؤ",
+       "rcfilters-savedqueries-unsetdefault": "ݙیفالٹ توں ہٹاؤ",
+       "rcfilters-savedqueries-remove": "مٹاؤ",
+       "rcfilters-savedqueries-new-name-label": "ناں",
+       "rcfilters-savedqueries-cancel-label": "منسوخ",
+       "rcfilters-filter-bots-label": "ٻوٹ",
        "rclistfrom": "$3 $2 توں ہونے آلیاں نویاں تبدیلیاں ݙکھاؤ",
        "rcshowhideminor": "$1 معمولی تبدیلیاں",
        "rcshowhideminor-show": "ݙیکھاؤ",
        "rcshowhideanons-show": "ݙیکھاؤ",
        "rcshowhideanons-hide": "لُکاؤ",
        "rcshowhidepatr": "$1 مراجعت شدہ ترامیم",
+       "rcshowhidepatr-show": "ݙیکھاؤ",
+       "rcshowhidepatr-hide": "لُکاؤ",
        "rcshowhidemine": "ذاتی ترامیم میݙے کم $1",
        "rcshowhidemine-show": "ݙیکھاؤ",
        "rcshowhidemine-hide": "لُکاؤ",
+       "rcshowhidecategorization-show": "ݙیکھاؤ",
+       "rcshowhidecategorization-hide": "لُکاؤ",
        "rclinks": "آخری $2 ݙینہ دیاں $1 تبدیلیاں ݙکھاؤ",
        "diff": "فرق",
        "hist": "پچھلا کم",
        "recentchangeslinked-to": "کھلے ہوئے ورقے دی بجائے ایندے نال جُڑے ہوئے ورقے دیاں تبدیلیاں ݙکھاؤ",
        "upload": "فائل چڑھاؤ",
        "uploadlogpage": "اپلوڈ لاگ",
+       "filename": "فائل دا ناں",
        "filedesc": "خلاصہ",
+       "fileuploadsummary": "خلاصہ",
+       "filereuploadsummary": "حالیہ تبدیلیاں",
+       "filesource": "ماخذ",
+       "savefile": "فائل بچاؤ",
+       "upload-dialog-title": "فائل چڑھاؤ",
+       "upload-dialog-button-cancel": "منسوخ",
+       "upload-dialog-button-back": "پچھوں",
+       "upload-dialog-button-done": "مکمل",
+       "upload-dialog-button-save": "بچاؤ",
+       "upload-dialog-button-upload": "اپلوڈ",
+       "upload-form-label-infoform-title": "تفصیلات",
+       "upload-form-label-infoform-name": "ناں",
+       "upload-form-label-infoform-description": "تفصیل",
+       "upload-form-label-usage-title": "استعمال",
+       "upload-form-label-usage-filename": "فائل دا ناں",
+       "upload-form-label-own-work": "یہ میݙا ذاتی کم ہے",
+       "upload-form-label-infoform-categories": "قسماں، زمرے",
+       "upload-form-label-infoform-date": "تاریخ",
        "license": "اجازت نامہ:",
        "license-header": "اجازہ کاری",
+       "listfiles-delete": "مٹاؤ",
        "imgfile": "فائل",
        "listfiles": "فائل لسٹ",
+       "listfiles_thumb": "تھمب نیل",
+       "listfiles_date": "تاریخ",
+       "listfiles_name": "ناں",
+       "listfiles_user": "ورتݨ والا",
+       "listfiles_size": "حجم",
+       "listfiles_description": "تفصیل",
+       "listfiles_count": "ورژن",
+       "listfiles-latestversion": "موجودہ ورژن",
+       "listfiles-latestversion-yes": "ڄیا",
+       "listfiles-latestversion-no": "کو",
        "file-anchor-link": "فائل",
        "filehist": "فائل دا تاریخچہ",
        "filehist-help": "کہیں خاص ویلے تے تاریخ کوں فائل کینویں  نظردی ہائی، ݙیکݨ کیتے اوں ویلے تے کلک کرو۔",
+       "filehist-deleteall": "سارے مٹاؤ",
+       "filehist-deleteone": "مٹاؤ",
        "filehist-revert": "واپس",
        "filehist-current": "موجودہ",
        "filehist-datetime": "تریخ/ویلہ",
        "filehist-nothumb": "کوئی تھمبنیل کائنی۔",
        "filehist-user": "ورتݨ والا",
        "filehist-dimensions": "پاسے",
+       "filehist-filesize": "تصویر دا سائز",
        "filehist-comment": "رائے",
        "imagelinks": "فائل ورتݨ",
        "linkstoimage": "اِیں فائل نال ہیٹھاں درج  {{PLURAL:$1|ورقہ مربوط ہے|$1 صفحات مربوط ہن}}:",
        "sharedupload-desc-here": "ایہ فائل $1 توں ہے تے ݙوجھیاں منصوبیاں تے وی ورتی ویسی۔\nایندی وضاحت [$2 فائل دی وضاحت دا ورقہ]  تے تھلے ݙتی ڳئی۔",
        "filepage-nofile": "ایں ناں دی کوئی فائل کائنی۔",
        "upload-disallowed-here": "تساں ایں فائل تے لکھ نی سڳدے۔",
+       "filedelete-comment": "سبب:",
+       "filedelete-submit": "مٹاؤ",
        "randompage": "رلے ملے ورقے",
+       "randomincategory-submit": "ڄلو",
        "statistics": "شماريات",
+       "pageswithprop-submit": "ڄلو",
        "double-redirect-fixer": "ریڈائرکٹ فکسر",
+       "brokenredirects-edit": "لکھو",
+       "brokenredirects-delete": "مٹاؤ",
+       "withoutinterwiki-submit": "ݙیکھاؤ",
        "nbytes": "$1 {{PLURAL:$1|بائٹ}}",
        "nmembers": "{{PLURAL:$1|رکن|اراکین}}",
        "prefixindex": "سارے ورقے بمع سابقہ",
+       "protectedpages-page": "ورقہ",
+       "protectedpages-reason": "سبب",
        "listusers": "ورتݨ والیاں دے ناں",
        "newpages": "نویں ورقے",
        "move": "ٹرو",
        "pager-newer-n": "{{PLURAL:$1|newer 1|زیادہ نواں $1}}",
        "pager-older-n": "{{PLURAL:$1|قدیم}} $1",
+       "apisandbox-reset": "صاف",
        "booksources": "کتابی وسائل",
        "booksources-search-legend": "ایں مضمون تے کتاباں لبھو",
        "booksources-search": "ڳولو",
        "specialloguserlabel": "کرݨ آلا :",
+       "speciallogtitlelabel": "ہدف (عنوان یا {{ns:user}}: صارف کیتے صارف دا ناں):",
        "log": "لاگز",
        "all-logs-page": "سارےعوامی لاگ",
        "logempty": "لاگ وچ رلدیاں ملدیاں چیزاں کائنی۔",
        "tooltip-t-whatlinkshere": "ایں نال جڑے سارے وکی ورقے۔",
        "tooltip-t-recentchangeslinked": "ایں ورقے توں جڑے ورقیاں وچ نویاں تبدیلیاں",
        "tooltip-feed-atom": "اِیں ورقے دا اٹوم فیڈ",
+       "tooltip-t-emailuser": "{{GENDER:$1|اایں صارف}} کوں ای میل بھیجو",
        "tooltip-t-upload": "فائل چڑھاؤ",
        "tooltip-t-specialpages": "سارے خاص ورقیاں دی تندیر",
        "tooltip-t-print": "ایں ورقے دا چھپݨ آلا انگ ݙیکھو",
        "pageinfo-lasttime": "چھیکڑی ترمیم دی تاریخ",
        "pageinfo-edits": "ترامیم دی مجموعی تعداد",
        "pageinfo-authors": "مختلف مصنفین دی  تعداد",
+       "pageinfo-recent-edits": "حالیہ ترامیم دی تعداد (گزشتہ $1 وچ)",
        "pageinfo-recent-authors": "مختلف مصنفین دی حالیہ تعداد",
        "pageinfo-magic-words": "جادوئی {{PLURAL:$1|لفظ|الفاظ}} ($1)",
        "pageinfo-hidden-categories": "پوشیدہ {{PLURAL:$1|زمرہ|زمرہ جات}} ($1)",
        "logentry-newusers-create": "صارف کھاتہ $1 {{GENDER:$2|بݨایا ڳیا}}",
        "logentry-newusers-autocreate": "صارف کھاتہ $1 خودکار طور  {{GENDER:$2|تخلیق تھیا}}",
        "logentry-upload-upload": "$1 {{GENDER:$2|اپلوڈ}} $3",
+       "logentry-upload-overwrite": "$1 نے $3 دا نواں نسخہ {{GENDER:$2|اپلوڈ کیتا}}",
        "searchsuggest-search": "ڳولو",
        "duration-days": "$1 {{PLURAL:$1|ݙینہ}}",
        "randomrootpage": "بے ترتيب بنیادی صفحہ"
index ff1b21c..b60523e 100644 (file)
        "fileduplicatesearch-noresults": "Datoteke imenovane »$1« ni mogoče najti.",
        "specialpages": "Posebne strani",
        "specialpages-note-top": "Legenda",
-       "specialpages-note": "* Navadne posebne strani.\n* <span class=\"mw-specialpagerestricted\">Omejene posebne strani.</span>",
+       "specialpages-note-restricted": "* Navadne posebne strani.\n* <span class=\"mw-specialpagerestricted\">Omejene posebne strani.</span>",
        "specialpages-group-maintenance": "Vzdrževalna poročila",
        "specialpages-group-other": "Ostale posebne strani",
        "specialpages-group-login": "Prijavite se / ustvarite račun",
index 1f4b643..1ee5c3b 100644 (file)
        "fileduplicatesearch-noresults": "Датотека под називом „$1“ није пронађена.",
        "specialpages": "Посебне странице",
        "specialpages-note-top": "Легенда",
-       "specialpages-note": "* Нормалне посебне странице\n* <span class=\"mw-specialpagerestricted\">Ограничене посебне странице</span>",
        "specialpages-group-maintenance": "Извештаји одржавања",
        "specialpages-group-other": "Остале посебне странице",
        "specialpages-group-login": "Пријава / регистрација",
index ac3c379..7bf39b3 100644 (file)
        "fileduplicatesearch-noresults": "Datoteka pod nazivom „$1“ nije pronađena.",
        "specialpages": "Posebne stranice",
        "specialpages-note-top": "Legenda",
-       "specialpages-note": "* Normalne posebne stranice\n* <span class=\"mw-specialpagerestricted\">Ograničene posebne stranice</span>",
        "specialpages-group-maintenance": "Izveštaji održavanja",
        "specialpages-group-other": "Ostale posebne stranice",
        "specialpages-group-login": "Prijava / registracija",
index 191dd95..961ba5a 100644 (file)
        "fileduplicatesearch-noresults": "Ingen fil med namnet \"$1\" hittades.",
        "specialpages": "Specialsidor",
        "specialpages-note-top": "Teckenförklaring",
-       "specialpages-note": "* Normala specialsidor.\n* <span class=\"mw-specialpagerestricted\">Specialsidor med begränsad åtkomst.</span>",
        "specialpages-group-maintenance": "Underhållsrapporter",
        "specialpages-group-other": "Övriga specialsidor",
        "specialpages-group-login": "Logga in / skapa konto",
index 2e31c50..493d2e6 100644 (file)
        "fileduplicatesearch-noresults": "\"$1\" అనే పేరుగల దస్త్రమేమీ కనబడలేదు.",
        "specialpages": "ప్రత్యేక పేజీలు",
        "specialpages-note-top": "సూచిక",
-       "specialpages-note": "* మామూలు ప్రత్యేక పుటలు.\n* <span class=\"mw-specialpagerestricted\">నియంత్రిత ప్రత్యేక పుటలు.</span>",
        "specialpages-group-maintenance": "నిర్వహణా నివేదికలు",
        "specialpages-group-other": "ఇతర ప్రత్యేక పేజీలు",
        "specialpages-group-login": "ప్రవేశించండి / ఖాతాను సృష్టించుకోండి",
index 92f157d..146647e 100644 (file)
@@ -70,7 +70,8 @@
                        "Олександр",
                        "Similartothissimilartothat",
                        "Bunyk",
-                       "Choomaq"
+                       "Choomaq",
+                       "SimondR"
                ]
        },
        "tog-underline": "Підкреслювання посилань:",
        "rcfilters-invalid-filter": "Недійсний фільтр",
        "rcfilters-empty-filter": "Без фільтрів. Показано всі зміни.",
        "rcfilters-filterlist-title": "Фільтри",
-       "rcfilters-filterlist-whatsthis": "Що Ñ\86е?",
+       "rcfilters-filterlist-whatsthis": "Як Ñ\86е Ð¿Ñ\80аÑ\86Ñ\8eÑ\94?",
        "rcfilters-filterlist-feedbacklink": "Надайте відгук про нові (бета) фільтри",
        "rcfilters-highlightbutton-title": "Виділити результати",
        "rcfilters-highlightmenu-title": "Вибрати колір",
        "rcfilters-noresults-conflict": "Результатів не знайдено через конфлікт у пошукових критеріях",
        "rcfilters-state-message-subset": "Цей фільтр не має впливу, оскільки його результати включені в результати {{PLURAL:$2|цього, ширшого, фільтра|цих, ширших, фільтрів}} (спробуйте увімкнути виділення, щоб вирізнити їх): $1",
        "rcfilters-state-message-fullcoverage": "Вибір усіх фільтрів у групі — це все одно, що не вибирати жодного з них, тобто таке фільтрування не має впливу. Гупа містить: $1",
-       "rcfilters-filtergroup-registration": "Реєстрація користувача",
-       "rcfilters-filter-registered-label": "Зареєстровані",
-       "rcfilters-filter-registered-description": "Користувачі, що увійшли в систему.",
-       "rcfilters-filter-unregistered-label": "Незареєстровані",
-       "rcfilters-filter-unregistered-description": "Користувачі, які не ввійшли в систему.",
-       "rcfilters-filter-unregistered-conflicts-user-experience-level": "Цей фільтр конфліктує з {{PLURAL:$2|таким фільтром|такими фільтрами}} досвіду, {{PLURAL:$2|який знаходить|які знаходять}} лише зареєстрованих користувачів: $1",
        "rcfilters-filtergroup-authorship": "Авторство внеску",
        "rcfilters-filter-editsbyself-label": "Зміни, здійснені Вами",
        "rcfilters-filter-editsbyself-description": "Ваш власний внесок.",
        "rcfilters-filter-editsbyother-label": "Зміни, здійснені іншими",
        "rcfilters-filter-editsbyother-description": "Усі зміни, за винятком Ваших власних.",
        "rcfilters-filtergroup-userExpLevel": "Рівень досвіду (тільки для зареєстрованих користувачів)",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "Фільтри досвіду знаходять лише зареєстрованих користувачів, тож цей фільтр конфліктує з фільтром «Незареєстровані».",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "Фільтр «Незареєстровані» конфліктує з одним або більше фільтрами досвіду, які знаходять лише зареєстрованих користувачів. Конфліктні фільтри позначені вище в ділянці активних фільтрів.",
+       "rcfilters-filter-user-experience-level-registered-label": "Зареєстровані",
+       "rcfilters-filter-user-experience-level-registered-description": "Користувачі, що увійшли в систему.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Незареєстровані",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Користувачі, які не ввійшли в систему.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Новачки",
        "rcfilters-filter-user-experience-level-newcomer-description": "Менше ніж 10 редагувань і 4 дні активності.",
        "rcfilters-filter-user-experience-level-learner-label": "Учні",
        "fileduplicatesearch-noresults": "Файл з назвою «$1» не знайдено.",
        "specialpages": "Спеціальні сторінки",
        "specialpages-note-top": "Легенда",
-       "specialpages-note": "* Звичайні службові сторінки\n* <span class=\"mw-specialpagerestricted\">Сторінки з обмеженим доступом.</span>",
        "specialpages-group-maintenance": "Технічні звіти",
        "specialpages-group-other": "Інші",
        "specialpages-group-login": "Вхід до системи / реєстрація",
index d415870..8dd0c53 100644 (file)
        "fileduplicatesearch-noresults": "Không tìm thấy tập tin nào tên “$1”.",
        "specialpages": "Các trang đặc biệt",
        "specialpages-note-top": "Chú giải",
-       "specialpages-note": "* Trang đặc biệt thông thường.\n* <strong class=\"mw-specialpagerestricted\">Trang đặc biệt được hạn chế.</strong>",
        "specialpages-group-maintenance": "Báo cáo bảo quản",
        "specialpages-group-other": "Trang đặc biệt khác",
        "specialpages-group-login": "Đăng nhập / Mở tài khoản",
index 991aa68..da6046e 100644 (file)
        "rcfilters-invalid-filter": "אומגילטיגער פֿילטער",
        "rcfilters-empty-filter": "קיין אַקטיווע פילטערס. אלע ביישטייערונגען געוויזן.",
        "rcfilters-filterlist-title": "פֿילטערס",
-       "rcfilters-filterlist-whatsthis": "וואס איז דאס?",
+       "rcfilters-filterlist-whatsthis": "ווי ארבעט דאס?",
+       "rcfilters-highlightbutton-title": "ארויסשטאַרצן רעזולטאַטן",
        "rcfilters-highlightmenu-title": "אויסקלויבן א קאליר",
        "rcfilters-filterlist-noresults": "קיין פֿילטערס נישט געטראפֿן",
        "rcfilters-filter-editsbyself-label": "ענדערונגען פון אייך",
        "rcfilters-filter-minor-label": "מינערדיקע רעדאַקטירונגען",
        "rcfilters-filter-pageedits-label": "בלאט רעדאקטירונגען",
        "rcfilters-filter-newpages-label": "בלאַט־שאַפֿונגען",
-       "rcfilters-filtergroup-lastRevision": "לעצטע ווערסיע",
+       "rcfilters-filtergroup-lastRevision": "לעצטע ווערסיעס",
        "rcfilters-filter-lastrevision-label": "לעצטע ווערסיע",
-       "rcfilters-filter-previousrevision-label": "פֿר×\99ער×\93×\99קע ווערסיעס",
+       "rcfilters-filter-previousrevision-label": "× ×\99ש×\98 ×\93×\99 ×\9cעצ×\98ע ווערסיעס",
        "rcfilters-filter-excluded": "אויסגעשלאסן",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:נישט</strong> $1",
        "rcnotefrom": "פֿאלגנד {{PLURAL:$5|איז די ענדערונג| זענען די ענדערונגען}} זײַט <strong>$3, $4</strong> (ביז <strong>$1</strong>).",
        "version-entrypoints-header-url": "URL",
        "version-libraries-library": "ביבליאטעק",
        "version-libraries-version": "ווערסיע",
+       "redirect": "ווייטערפֿירן לויט טעקע, באַניצער, בלאַט, ווערסיע אדער לאגבוך אידענטיפֿצירער",
        "redirect-submit": "גייט",
        "redirect-lookup": "זוכן:",
        "redirect-value": "ווערט:",
        "fileduplicatesearch-noresults": "קיין טעקע מיטן נאמען \"$1\" נישט געטראפֿן.",
        "specialpages": "ספעציעלע בלעטער",
        "specialpages-note-top": "לעגענדע",
-       "specialpages-note": "* נארמאַלע באַזונדערע בלעטער.\n* <span class=\"mw-specialpagerestricted\">באַגרענעצטע באַזונדערע בלעטער.</span>",
        "specialpages-group-maintenance": "אויפֿהאַלטונג באַריכטן",
        "specialpages-group-other": "אַנדערע ספעציעלע בלעטער",
        "specialpages-group-login": "ארײַנלאגירן / שאַפֿן קאנטע",
index 7c9cd49..2f02916 100644 (file)
        "recentchanges-legend-heading": "<strong>標記:</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (睇埋[[Special:NewPages|新開版]])",
        "recentchanges-submit": "顯示",
-       "rcfilters-filter-editsbyself-description": "你改嘅嘢。",
+       "rcfilters-filter-editsbyself-description": "你嘅貢獻。",
        "rcfilters-filter-editsbyother-label": "其他人改嘅嘢",
-       "rcfilters-filter-editsbyother-description": "其他人(唔係你)改嘅嘢",
+       "rcfilters-filter-editsbyother-description": "所有改過嘅嘢(除咗你自己)",
        "rcfilters-filtergroup-userExpLevel": "經驗級別(只限簽咗到嘅用戶)",
        "rcfilters-filter-user-experience-level-newcomer-label": "新手",
-       "rcfilters-filter-user-experience-level-newcomer-description": "少過4日、10次編輯",
+       "rcfilters-filter-user-experience-level-newcomer-description": "少過4日、10次編輯嘅用戶",
        "rcfilters-filter-user-experience-level-learner-label": "學徒",
        "rcfilters-filter-user-experience-level-learner-description": "編輯數同經驗多過「新手」但少過「老手」。",
        "rcfilters-filter-user-experience-level-experienced-label": "老手",
-       "rcfilters-filter-user-experience-level-experienced-description": "超過30日同埋500次編輯",
+       "rcfilters-filter-user-experience-level-experienced-description": "超過30日同埋500次編輯嘅用戶",
        "rcfilters-filtergroup-automated": "自動貢獻",
        "rcfilters-filter-bots-label": "機械人",
        "rcfilters-filter-bots-description": "用自動工具做嘅貢獻",
        "fileduplicatesearch-result-n": "個檔案 \"$1\" 有$2項完全相同嘅重覆。",
        "fileduplicatesearch-noresults": "檔案名\"$1\"找不到",
        "specialpages": "特別頁",
-       "specialpages-note": "* 標準特別頁。\n* <span class=\"mw-specialpagerestricted\">有限制嘅特別頁。</span>",
        "specialpages-group-maintenance": "維護報告",
        "specialpages-group-other": "其它特別頁",
        "specialpages-group-login": "簽到/開新戶口",
index 1b5c014..c46ad3a 100644 (file)
@@ -96,7 +96,8 @@
                        "D41D8CD98F",
                        "Wmr",
                        "逆襲的天邪鬼",
-                       "WhitePhosphorus"
+                       "WhitePhosphorus",
+                       "A2093064"
                ]
        },
        "tog-underline": "链接下划线:",
        "rcfilters-view-namespaces-tooltip": "按名字空间过滤结果",
        "rcfilters-view-tags-tooltip": "按编辑标签过滤结果",
        "rcfilters-view-return-to-default-tooltip": "返回主过滤菜单",
-       "rcfilters-liveupdates-button": "å\9c¨çº¿更新",
+       "rcfilters-liveupdates-button": "å®\9eæ\97更新",
        "rcnotefrom": "下面{{PLURAL:$5|是}}<strong>$3 $4</strong>之后的更改(最多显示<strong>$1</strong>个)。",
        "rclistfromreset": "重置时间选择",
        "rclistfrom": "显示$3 $2之后的新更改",
        "fileduplicatesearch-noresults": "没有文件命名为\"$1\"发现。",
        "specialpages": "特殊页面",
        "specialpages-note-top": "说明",
-       "specialpages-note": "*普通特殊页面。\n*<span class=\"mw-specialpagerestricted\">受限特殊页面。</span>",
+       "specialpages-note-restricted": "* 普通特殊页面。\n* <span class=\"mw-specialpagerestricted\">受限特殊页面。</span>",
        "specialpages-group-maintenance": "维护报告",
        "specialpages-group-other": "其它特殊页面",
        "specialpages-group-login": "登录/创建账户",
index 813dca2..933567b 100644 (file)
        "rcfilters-filter-excluded": "已排除",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:不是</strong>$1",
        "rcfilters-view-tags": "標記的編輯",
+       "rcfilters-liveupdates-button": "實時更新",
        "rcnotefrom": "以下{{PLURAL:$5|為}}自 <strong>$3 $4</strong> 以來的變更 (最多顯示 <strong>$1</strong> 筆)。",
        "rclistfromreset": "重設日期選擇",
        "rclistfrom": "顯示自 $3 $2 以來的新變更",
        "fileduplicatesearch-noresults": "查無名稱為 \"$1\" 的檔案。",
        "specialpages": "特殊頁面",
        "specialpages-note-top": "說明",
-       "specialpages-note": "* 一般特殊頁面。\n* <span class=\"mw-specialpagerestricted\">受限制的特殊頁面。</span>",
        "specialpages-group-maintenance": "維護報表",
        "specialpages-group-other": "其它特殊頁面",
        "specialpages-group-login": "登入 / 建立帳號",
diff --git a/languages/messages/MessagesSkr.php b/languages/messages/MessagesSkr.php
new file mode 100644 (file)
index 0000000..2072b8d
--- /dev/null
@@ -0,0 +1,11 @@
+<?php
+/** Saraiki (multiple scripts)
+ *
+ * To improve a translation please visit https://translatewiki.net
+ *
+ * @ingroup Language
+ * @file
+ *
+ */
+
+$fallback = 'skr-arab';
diff --git a/languages/messages/MessagesSkr_arab.php b/languages/messages/MessagesSkr_arab.php
new file mode 100644 (file)
index 0000000..901e2aa
--- /dev/null
@@ -0,0 +1,13 @@
+<?php
+/** Saraiki (Arabic script) (سرائیکی)
+ *
+ * To improve a translation please visit https://translatewiki.net
+ *
+ * @ingroup Language
+ * @file
+ *
+ */
+
+$fallback = 'ur, pnb';
+
+$rtl = true;
index 2262338..b21bfbb 100644 (file)
@@ -39,7 +39,7 @@ class AddRFCAndPMIDInterwiki extends LoggedUpdateMaintenance {
        }
 
        protected function updateSkippedMessage() {
-               return 'RFC and PMID already added to interwiki database table';
+               return 'RFC and PMID already added to interwiki database table.';
        }
 
        protected function doDBUpdates() {
diff --git a/maintenance/populatePPSortKey.php b/maintenance/populatePPSortKey.php
new file mode 100644 (file)
index 0000000..7e3c2c3
--- /dev/null
@@ -0,0 +1,106 @@
+<?php
+/**
+ * Populate the pp_sortkey fields in the page_props table
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Maintenance
+ */
+
+require_once __DIR__ . '/Maintenance.php';
+
+use Wikimedia\Rdbms\IDatabase;
+
+/**
+ * Usage:
+ *  populatePPSortKey.php
+ */
+class PopulatePPSortKey extends LoggedUpdateMaintenance {
+       public function __construct() {
+               parent::__construct();
+               $this->addDescription( 'Populate the pp_sortkey field' );
+               $this->setBatchSize( 100 );
+       }
+
+       protected function doDBUpdates() {
+               $dbw = $this->getDB( DB_MASTER );
+
+               $lastProp = null;
+               $lastPageValue = 0;
+               $editedRowCount = 0;
+
+               $this->output( "Populating page_props.pp_sortkey...\n" );
+               while ( true ) {
+                       $conditions = [ 'pp_sortkey IS NULL' ];
+                       if ( $lastPageValue !== 0 ) {
+                               $conditions[] = 'pp_page > ' . $dbw->addQuotes( $lastPageValue ) . ' OR ' .
+                                       '( pp_page = ' . $dbw->addQuotes( $lastPageValue ) .
+                                       ' AND pp_propname > ' . $dbw->addQuotes( $lastProp ) . ' )';
+                       }
+
+                       $res = $dbw->select(
+                               'page_props',
+                               [ 'pp_propname', 'pp_page', 'pp_sortkey', 'pp_value' ],
+                               $conditions,
+                               __METHOD__,
+                               [
+                                       'ORDER BY' => 'pp_page, pp_propname',
+                                       'LIMIT' => $this->mBatchSize
+                               ]
+                       );
+
+                       if ( $res->numRows() === 0 ) {
+                               break;
+                       }
+
+                       $this->beginTransaction( $dbw, __METHOD__ );
+
+                       foreach ( $res as $row ) {
+                               if ( !is_numeric( $row->pp_value ) ) {
+                                       continue;
+                               }
+                               $dbw->update(
+                                       'page_props',
+                                       [ 'pp_sortkey' => $row->pp_value ],
+                                       [
+                                               'pp_page' => $row->pp_page,
+                                               'pp_propname' => $row->pp_propname
+                                       ],
+                                       __METHOD__
+                               );
+                               $editedRowCount++;
+                       }
+
+                       $this->output( "Updated " . $editedRowCount . " rows\n" );
+                       $this->commitTransaction( $dbw, __METHOD__ );
+
+                       // We need to get the last element's page ID
+                       $lastPageValue = $row->pp_page;
+                       // And the propname...
+                       $lastProp = $row->pp_propname;
+               }
+
+               $this->output( "Populating page_props.pp_sortkey complete.\n" );
+       }
+
+       protected function getUpdateKey() {
+               return 'populate pp_sortkey';
+       }
+}
+
+$maintClass = 'PopulatePPSortKey';
+require_once RUN_MAINTENANCE_IF_MAIN;
index ae68fc4..4749222 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * QUnit 1.23.1
+ * QUnit 2.4.0
  * https://qunitjs.com/
  *
  * Copyright jQuery Foundation and other contributors
  * Released under the MIT license
  * https://jquery.org/license
  *
- * Date: 2016-04-12T17:29Z
+ * Date: 2017-07-08T15:20Z
  */
 
 /** Font Family and Sizes */
@@ -27,7 +27,7 @@
 }
 
 
-/** Header */
+/** Header (excluding toolbar) */
 
 #qunit-header {
        padding: 0.5em 0 0.5em 1em;
        color: #FFF;
 }
 
-#qunit-testrunner-toolbar label {
-       display: inline-block;
-       padding: 0 0.5em 0 0.1em;
-}
-
 #qunit-banner {
        height: 5px;
 }
 
-#qunit-testrunner-toolbar {
-       padding: 0.5em 1em 0.5em 1em;
-       color: #5E740B;
-       background-color: #EEE;
-       overflow: hidden;
-}
-
 #qunit-filteredTest {
        padding: 0.5em 1em 0.5em 1em;
-       background-color: #F4FF77;
        color: #366097;
+       background-color: #F4FF77;
 }
 
 #qunit-userAgent {
        padding: 0.5em 1em 0.5em 1em;
-       background-color: #2B81AF;
        color: #FFF;
+       background-color: #2B81AF;
        text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
 }
 
-#qunit-modulefilter-container {
-       float: right;
-       padding: 0.2em;
+
+/** Toolbar */
+
+#qunit-testrunner-toolbar {
+       padding: 0.5em 1em 0.5em 1em;
+       color: #5E740B;
+       background-color: #EEE;
+}
+
+#qunit-testrunner-toolbar .clearfix {
+       height: 0;
+       clear: both;
 }
 
-.qunit-url-config {
+#qunit-testrunner-toolbar label {
        display: inline-block;
-       padding: 0.1em;
 }
 
-.qunit-filter {
-       display: block;
+#qunit-testrunner-toolbar input[type=checkbox],
+#qunit-testrunner-toolbar input[type=radio] {
+       margin: 3px;
+       vertical-align: -2px;
+}
+
+#qunit-testrunner-toolbar input[type=text] {
+       box-sizing: border-box;
+       height: 1.6em;
+}
+
+.qunit-url-config,
+.qunit-filter,
+#qunit-modulefilter {
+       display: inline-block;
+       line-height: 2.1em;
+}
+
+.qunit-filter,
+#qunit-modulefilter {
        float: right;
+       position: relative;
        margin-left: 1em;
 }
 
+.qunit-url-config label {
+       margin-right: 0.5em;
+}
+
+#qunit-modulefilter-search {
+       box-sizing: border-box;
+       width: 400px;
+}
+
+#qunit-modulefilter-search-container:after {
+       position: absolute;
+       right: 0.3em;
+       content: "\25bc";
+       color: black;
+}
+
+#qunit-modulefilter-dropdown {
+       /* align with #qunit-modulefilter-search */
+       box-sizing: border-box;
+       width: 400px;
+       position: absolute;
+       right: 0;
+       top: 50%;
+       margin-top: 0.8em;
+
+       border: 1px solid #D3D3D3;
+       border-top: none;
+       border-radius: 0 0 .25em .25em;
+       color: #000;
+       background-color: #F5F5F5;
+       z-index: 99;
+}
+
+#qunit-modulefilter-dropdown a {
+       color: inherit;
+       text-decoration: none;
+}
+
+#qunit-modulefilter-dropdown .clickable.checked {
+       font-weight: bold;
+       color: #000;
+       background-color: #D2E0E6;
+}
+
+#qunit-modulefilter-dropdown .clickable:hover {
+       color: #FFF;
+       background-color: #0D3349;
+}
+
+#qunit-modulefilter-actions {
+       display: block;
+       overflow: auto;
+
+       /* align with #qunit-modulefilter-dropdown-list */
+       font: smaller/1.5em sans-serif;
+}
+
+#qunit-modulefilter-dropdown #qunit-modulefilter-actions > * {
+       box-sizing: border-box;
+       max-height: 2.8em;
+       display: block;
+       padding: 0.4em;
+}
+
+#qunit-modulefilter-dropdown #qunit-modulefilter-actions > button {
+       float: right;
+       font: inherit;
+}
+
+#qunit-modulefilter-dropdown #qunit-modulefilter-actions > :last-child {
+       /* insert padding to align with checkbox margins */
+       padding-left: 3px;
+}
+
+#qunit-modulefilter-dropdown-list {
+       max-height: 200px;
+       overflow-y: auto;
+       margin: 0;
+       border-top: 2px groove threedhighlight;
+       padding: 0.4em 0 0;
+       font: smaller/1.5em sans-serif;
+}
+
+#qunit-modulefilter-dropdown-list li {
+       white-space: nowrap;
+       overflow: hidden;
+       text-overflow: ellipsis;
+}
+
+#qunit-modulefilter-dropdown-list .clickable {
+       display: block;
+       padding-left: 0.15em;
+}
+
+
 /** Tests: Pass/Fail */
 
 #qunit-tests {
 #qunit-tests li.running,
 #qunit-tests li.pass,
 #qunit-tests li.fail,
-#qunit-tests li.skipped {
+#qunit-tests li.skipped,
+#qunit-tests li.aborted {
        display: list-item;
 }
 
 }
 
 #qunit-tests.hidepass li.running,
-#qunit-tests.hidepass li.pass {
+#qunit-tests.hidepass li.pass:not(.todo) {
        visibility: hidden;
        position: absolute;
        width:   0;
 }
 
 #qunit-tests del {
-       background-color: #E0F2BE;
        color: #374E0C;
+       background-color: #E0F2BE;
        text-decoration: none;
 }
 
 #qunit-tests ins {
-       background-color: #FFCACA;
        color: #500;
+       background-color: #FFCACA;
        text-decoration: none;
 }
 
 
 #qunit-banner.qunit-fail                    { background-color: #EE5757; }
 
+
+/*** Aborted tests */
+#qunit-tests .aborted { color: #000; background-color: orange; }
 /*** Skipped tests */
 
 #qunit-tests .skipped {
        background-color: #EBECE9;
 }
 
+#qunit-tests .qunit-todo-label,
 #qunit-tests .qunit-skipped-label {
        background-color: #F4FF77;
        display: inline-block;
        margin: -0.4em 0.4em -0.4em 0;
 }
 
+#qunit-tests .qunit-todo-label {
+       background-color: #EEE;
+}
+
 /** Result */
 
 #qunit-testresult {
-       padding: 0.5em 1em 0.5em 1em;
-
        color: #2B81AF;
        background-color: #D2E0E6;
 
        border-bottom: 1px solid #FFF;
 }
+#qunit-testresult .clearfix {
+       height: 0;
+       clear: both;
+}
 #qunit-testresult .module-name {
        font-weight: 700;
 }
+#qunit-testresult-display {
+       padding: 0.5em 1em 0.5em 1em;
+       width: 85%;
+       float:left;
+}
+#qunit-testresult-controls {
+       padding: 0.5em 1em 0.5em 1em;
+  width: 10%;
+       float:left;
+}
 
 /** Fixture */
 
index 5df0822..bb8f31d 100644 (file)
 /*!
- * QUnit 1.23.1
+ * QUnit 2.4.0
  * https://qunitjs.com/
  *
  * Copyright jQuery Foundation and other contributors
  * Released under the MIT license
  * https://jquery.org/license
  *
- * Date: 2016-04-12T17:29Z
+ * Date: 2017-07-08T15:20Z
  */
+(function (global$1) {
+  'use strict';
 
-( function( global ) {
-
-var QUnit = {};
-
-var Date = global.Date;
-var now = Date.now || function() {
-       return new Date().getTime();
-};
-
-var setTimeout = global.setTimeout;
-var clearTimeout = global.clearTimeout;
-
-// Store a local window from the global to allow direct references.
-var window = global.window;
-
-var defined = {
-       document: window && window.document !== undefined,
-       setTimeout: setTimeout !== undefined,
-       sessionStorage: ( function() {
-               var x = "qunit-test-string";
-               try {
-                       sessionStorage.setItem( x, x );
-                       sessionStorage.removeItem( x );
-                       return true;
-               } catch ( e ) {
-                       return false;
-               }
-       }() )
-};
-
-var fileName = ( sourceFromStacktrace( 0 ) || "" ).replace( /(:\d+)+\)?/, "" ).replace( /.+\//, "" );
-var globalStartCalled = false;
-var runStarted = false;
-
-var toString = Object.prototype.toString,
-       hasOwn = Object.prototype.hasOwnProperty;
-
-// Returns a new Array with the elements that are in a but not in b
-function diff( a, b ) {
-       var i, j,
-               result = a.slice();
-
-       for ( i = 0; i < result.length; i++ ) {
-               for ( j = 0; j < b.length; j++ ) {
-                       if ( result[ i ] === b[ j ] ) {
-                               result.splice( i, 1 );
-                               i--;
-                               break;
-                       }
-               }
-       }
-       return result;
-}
-
-// From jquery.js
-function inArray( elem, array ) {
-       if ( array.indexOf ) {
-               return array.indexOf( elem );
-       }
-
-       for ( var i = 0, length = array.length; i < length; i++ ) {
-               if ( array[ i ] === elem ) {
-                       return i;
-               }
-       }
-
-       return -1;
-}
-
-/**
- * Makes a clone of an object using only Array or Object as base,
- * and copies over the own enumerable properties.
- *
- * @param {Object} obj
- * @return {Object} New object with only the own properties (recursively).
- */
-function objectValues ( obj ) {
-       var key, val,
-               vals = QUnit.is( "array", obj ) ? [] : {};
-       for ( key in obj ) {
-               if ( hasOwn.call( obj, key ) ) {
-                       val = obj[ key ];
-                       vals[ key ] = val === Object( val ) ? objectValues( val ) : val;
-               }
-       }
-       return vals;
-}
-
-function extend( a, b, undefOnly ) {
-       for ( var prop in b ) {
-               if ( hasOwn.call( b, prop ) ) {
-
-                       // Avoid "Member not found" error in IE8 caused by messing with window.constructor
-                       // This block runs on every environment, so `global` is being used instead of `window`
-                       // to avoid errors on node.
-                       if ( prop !== "constructor" || a !== global ) {
-                               if ( b[ prop ] === undefined ) {
-                                       delete a[ prop ];
-                               } else if ( !( undefOnly && typeof a[ prop ] !== "undefined" ) ) {
-                                       a[ prop ] = b[ prop ];
-                               }
-                       }
-               }
-       }
-
-       return a;
-}
-
-function objectType( obj ) {
-       if ( typeof obj === "undefined" ) {
-               return "undefined";
-       }
-
-       // Consider: typeof null === object
-       if ( obj === null ) {
-               return "null";
-       }
-
-       var match = toString.call( obj ).match( /^\[object\s(.*)\]$/ ),
-               type = match && match[ 1 ];
-
-       switch ( type ) {
-               case "Number":
-                       if ( isNaN( obj ) ) {
-                               return "nan";
-                       }
-                       return "number";
-               case "String":
-               case "Boolean":
-               case "Array":
-               case "Set":
-               case "Map":
-               case "Date":
-               case "RegExp":
-               case "Function":
-               case "Symbol":
-                       return type.toLowerCase();
-       }
-       if ( typeof obj === "object" ) {
-               return "object";
-       }
-}
-
-// Safe object type checking
-function is( type, obj ) {
-       return QUnit.objectType( obj ) === type;
-}
-
-// Doesn't support IE6 to IE9, it will return undefined on these browsers
-// See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack
-function extractStacktrace( e, offset ) {
-       offset = offset === undefined ? 4 : offset;
-
-       var stack, include, i;
-
-       if ( e.stack ) {
-               stack = e.stack.split( "\n" );
-               if ( /^error$/i.test( stack[ 0 ] ) ) {
-                       stack.shift();
-               }
-               if ( fileName ) {
-                       include = [];
-                       for ( i = offset; i < stack.length; i++ ) {
-                               if ( stack[ i ].indexOf( fileName ) !== -1 ) {
-                                       break;
-                               }
-                               include.push( stack[ i ] );
-                       }
-                       if ( include.length ) {
-                               return include.join( "\n" );
-                       }
-               }
-               return stack[ offset ];
-
-       // Support: Safari <=6 only
-       } else if ( e.sourceURL ) {
-
-               // Exclude useless self-reference for generated Error objects
-               if ( /qunit.js$/.test( e.sourceURL ) ) {
-                       return;
-               }
-
-               // For actual exceptions, this is useful
-               return e.sourceURL + ":" + e.line;
-       }
-}
-
-function sourceFromStacktrace( offset ) {
-       var error = new Error();
-
-       // Support: Safari <=7 only, IE <=10 - 11 only
-       // Not all browsers generate the `stack` property for `new Error()`, see also #636
-       if ( !error.stack ) {
-               try {
-                       throw error;
-               } catch ( err ) {
-                       error = err;
-               }
-       }
-
-       return extractStacktrace( error, offset );
-}
-
-/**
- * Config object: Maintain internal state
- * Later exposed as QUnit.config
- * `config` initialized at top of scope
- */
-var config = {
+  global$1 = global$1 && 'default' in global$1 ? global$1['default'] : global$1;
 
-       // The queue of tests to run
-       queue: [],
+  var window = global$1.window;
+  var self$1 = global$1.self;
+  var console = global$1.console;
+  var setTimeout = global$1.setTimeout;
+  var clearTimeout = global$1.clearTimeout;
 
-       // Block until document ready
-       blocking: true,
+  var document = window && window.document;
+  var navigator = window && window.navigator;
 
-       // By default, run previously failed tests first
-       // very useful in combination with "Hide passed tests" checked
-       reorder: true,
+  var localSessionStorage = function () {
+       var x = "qunit-test-string";
+       try {
+               global$1.sessionStorage.setItem(x, x);
+               global$1.sessionStorage.removeItem(x);
+               return global$1.sessionStorage;
+       } catch (e) {
+               return undefined;
+       }
+  }();
 
-       // By default, modify document.title when suite is done
-       altertitle: true,
+  var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
+    return typeof obj;
+  } : function (obj) {
+    return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
+  };
 
-       // HTML Reporter: collapse every test except the first failing test
-       // If false, all failing tests will be expanded
-       collapse: true,
 
-       // By default, scroll to top of the page when suite is done
-       scrolltop: true,
 
-       // Depth up-to which object will be dumped
-       maxDepth: 5,
 
-       // When enabled, all tests must call expect()
-       requireExpects: false,
 
-       // Placeholder for user-configurable form-exposed URL parameters
-       urlConfig: [],
 
-       // Set of all modules.
-       modules: [],
 
-       // Stack of nested modules
-       moduleStack: [],
 
-       // The first unnamed module
-       currentModule: {
-               name: "",
-               tests: []
-       },
 
-       callbacks: {}
-};
-
-// Push a loose unnamed module to the modules collection
-config.modules.push( config.currentModule );
-
-var loggingCallbacks = {};
-
-// Register logging callbacks
-function registerLoggingCallbacks( obj ) {
-       var i, l, key,
-               callbackNames = [ "begin", "done", "log", "testStart", "testDone",
-                       "moduleStart", "moduleDone" ];
-
-       function registerLoggingCallback( key ) {
-               var loggingCallback = function( callback ) {
-                       if ( objectType( callback ) !== "function" ) {
-                               throw new Error(
-                                       "QUnit logging methods require a callback function as their first parameters."
-                               );
-                       }
-
-                       config.callbacks[ key ].push( callback );
-               };
-
-               // DEPRECATED: This will be removed on QUnit 2.0.0+
-               // Stores the registered functions allowing restoring
-               // at verifyLoggingCallbacks() if modified
-               loggingCallbacks[ key ] = loggingCallback;
-
-               return loggingCallback;
-       }
-
-       for ( i = 0, l = callbackNames.length; i < l; i++ ) {
-               key = callbackNames[ i ];
-
-               // Initialize key collection of logging callback
-               if ( objectType( config.callbacks[ key ] ) === "undefined" ) {
-                       config.callbacks[ key ] = [];
-               }
-
-               obj[ key ] = registerLoggingCallback( key );
-       }
-}
-
-function runLoggingCallbacks( key, args ) {
-       var i, l, callbacks;
-
-       callbacks = config.callbacks[ key ];
-       for ( i = 0, l = callbacks.length; i < l; i++ ) {
-               callbacks[ i ]( args );
-       }
-}
-
-// DEPRECATED: This will be removed on 2.0.0+
-// This function verifies if the loggingCallbacks were modified by the user
-// If so, it will restore it, assign the given callback and print a console warning
-function verifyLoggingCallbacks() {
-       var loggingCallback, userCallback;
-
-       for ( loggingCallback in loggingCallbacks ) {
-               if ( QUnit[ loggingCallback ] !== loggingCallbacks[ loggingCallback ] ) {
-
-                       userCallback = QUnit[ loggingCallback ];
-
-                       // Restore the callback function
-                       QUnit[ loggingCallback ] = loggingCallbacks[ loggingCallback ];
-
-                       // Assign the deprecated given callback
-                       QUnit[ loggingCallback ]( userCallback );
-
-                       if ( global.console && global.console.warn ) {
-                               global.console.warn(
-                                       "QUnit." + loggingCallback + " was replaced with a new value.\n" +
-                                       "Please, check out the documentation on how to apply logging callbacks.\n" +
-                                       "Reference: https://api.qunitjs.com/category/callbacks/"
-                               );
-                       }
-               }
-       }
-}
-
-( function() {
-       if ( !defined.document ) {
-               return;
-       }
-
-       // `onErrorFnPrev` initialized at top of scope
-       // Preserve other handlers
-       var onErrorFnPrev = window.onerror;
-
-       // Cover uncaught exceptions
-       // Returning true will suppress the default browser handler,
-       // returning false will let it run.
-       window.onerror = function( error, filePath, linerNr ) {
-               var ret = false;
-               if ( onErrorFnPrev ) {
-                       ret = onErrorFnPrev( error, filePath, linerNr );
-               }
-
-               // Treat return value as window.onerror itself does,
-               // Only do our handling if not suppressed.
-               if ( ret !== true ) {
-                       if ( QUnit.config.current ) {
-                               if ( QUnit.config.current.ignoreGlobalErrors ) {
-                                       return true;
-                               }
-                               QUnit.pushFailure( error, filePath + ":" + linerNr );
-                       } else {
-                               QUnit.test( "global failure", extend( function() {
-                                       QUnit.pushFailure( error, filePath + ":" + linerNr );
-                               }, { validTest: true } ) );
-                       }
-                       return false;
-               }
-
-               return ret;
-       };
-}() );
-
-// Figure out if we're running the tests from a server or not
-QUnit.isLocal = !( defined.document && window.location.protocol !== "file:" );
-
-// Expose the current QUnit version
-QUnit.version = "1.23.1";
-
-extend( QUnit, {
-
-       // Call on start of module test to prepend name to all tests
-       module: function( name, testEnvironment, executeNow ) {
-               var module, moduleFns;
-               var currentModule = config.currentModule;
-
-               if ( arguments.length === 2 ) {
-                       if ( objectType( testEnvironment ) === "function" ) {
-                               executeNow = testEnvironment;
-                               testEnvironment = undefined;
-                       }
-               }
-
-               // DEPRECATED: handles setup/teardown functions,
-               // beforeEach and afterEach should be used instead
-               if ( testEnvironment && testEnvironment.setup ) {
-                       testEnvironment.beforeEach = testEnvironment.setup;
-                       delete testEnvironment.setup;
-               }
-               if ( testEnvironment && testEnvironment.teardown ) {
-                       testEnvironment.afterEach = testEnvironment.teardown;
-                       delete testEnvironment.teardown;
-               }
-
-               module = createModule();
-
-               moduleFns = {
-                       beforeEach: setHook( module, "beforeEach" ),
-                       afterEach: setHook( module, "afterEach" )
-               };
-
-               if ( objectType( executeNow ) === "function" ) {
-                       config.moduleStack.push( module );
-                       setCurrentModule( module );
-                       executeNow.call( module.testEnvironment, moduleFns );
-                       config.moduleStack.pop();
-                       module = module.parentModule || currentModule;
-               }
-
-               setCurrentModule( module );
-
-               function createModule() {
-                       var parentModule = config.moduleStack.length ?
-                               config.moduleStack.slice( -1 )[ 0 ] : null;
-                       var moduleName = parentModule !== null ?
-                               [ parentModule.name, name ].join( " > " ) : name;
-                       var module = {
-                               name: moduleName,
-                               parentModule: parentModule,
-                               tests: [],
-                               moduleId: generateHash( moduleName )
-                       };
-
-                       var env = {};
-                       if ( parentModule ) {
-                               extend( env, parentModule.testEnvironment );
-                               delete env.beforeEach;
-                               delete env.afterEach;
-                       }
-                       extend( env, testEnvironment );
-                       module.testEnvironment = env;
-
-                       config.modules.push( module );
-                       return module;
-               }
-
-               function setCurrentModule( module ) {
-                       config.currentModule = module;
-               }
-
-       },
-
-       // DEPRECATED: QUnit.asyncTest() will be removed in QUnit 2.0.
-       asyncTest: asyncTest,
-
-       test: test,
-
-       skip: skip,
-
-       only: only,
-
-       // DEPRECATED: The functionality of QUnit.start() will be altered in QUnit 2.0.
-       // In QUnit 2.0, invoking it will ONLY affect the `QUnit.config.autostart` blocking behavior.
-       start: function( count ) {
-               var globalStartAlreadyCalled = globalStartCalled;
-
-               if ( !config.current ) {
-                       globalStartCalled = true;
-
-                       if ( runStarted ) {
-                               throw new Error( "Called start() outside of a test context while already started" );
-                       } else if ( globalStartAlreadyCalled || count > 1 ) {
-                               throw new Error( "Called start() outside of a test context too many times" );
-                       } else if ( config.autostart ) {
-                               throw new Error( "Called start() outside of a test context when " +
-                                       "QUnit.config.autostart was true" );
-                       } else if ( !config.pageLoaded ) {
-
-                               // The page isn't completely loaded yet, so bail out and let `QUnit.load` handle it
-                               config.autostart = true;
-                               return;
-                       }
-               } else {
-
-                       // If a test is running, adjust its semaphore
-                       config.current.semaphore -= count || 1;
-
-                       // If semaphore is non-numeric, throw error
-                       if ( isNaN( config.current.semaphore ) ) {
-                               config.current.semaphore = 0;
-
-                               QUnit.pushFailure(
-                                       "Called start() with a non-numeric decrement.",
-                                       sourceFromStacktrace( 2 )
-                               );
-                               return;
-                       }
-
-                       // Don't start until equal number of stop-calls
-                       if ( config.current.semaphore > 0 ) {
-                               return;
-                       }
-
-                       // Throw an Error if start is called more often than stop
-                       if ( config.current.semaphore < 0 ) {
-                               config.current.semaphore = 0;
-
-                               QUnit.pushFailure(
-                                       "Called start() while already started (test's semaphore was 0 already)",
-                                       sourceFromStacktrace( 2 )
-                               );
-                               return;
-                       }
-               }
-
-               resumeProcessing();
-       },
-
-       // DEPRECATED: QUnit.stop() will be removed in QUnit 2.0.
-       stop: function( count ) {
-
-               // If there isn't a test running, don't allow QUnit.stop() to be called
-               if ( !config.current ) {
-                       throw new Error( "Called stop() outside of a test context" );
-               }
-
-               // If a test is running, adjust its semaphore
-               config.current.semaphore += count || 1;
-
-               pauseProcessing();
-       },
-
-       config: config,
-
-       is: is,
-
-       objectType: objectType,
-
-       extend: extend,
-
-       load: function() {
-               config.pageLoaded = true;
-
-               // Initialize the configuration options
-               extend( config, {
-                       stats: { all: 0, bad: 0 },
-                       moduleStats: { all: 0, bad: 0 },
-                       started: 0,
-                       updateRate: 1000,
-                       autostart: true,
-                       filter: ""
-               }, true );
-
-               config.blocking = false;
-
-               if ( config.autostart ) {
-                       resumeProcessing();
-               }
-       },
-
-       stack: function( offset ) {
-               offset = ( offset || 0 ) + 2;
-               return sourceFromStacktrace( offset );
-       }
-} );
-
-registerLoggingCallbacks( QUnit );
-
-function begin() {
-       var i, l,
-               modulesLog = [];
-
-       // If the test run hasn't officially begun yet
-       if ( !config.started ) {
-
-               // Record the time of the test run's beginning
-               config.started = now();
-
-               verifyLoggingCallbacks();
-
-               // Delete the loose unnamed module if unused.
-               if ( config.modules[ 0 ].name === "" && config.modules[ 0 ].tests.length === 0 ) {
-                       config.modules.shift();
-               }
-
-               // Avoid unnecessary information by not logging modules' test environments
-               for ( i = 0, l = config.modules.length; i < l; i++ ) {
-                       modulesLog.push( {
-                               name: config.modules[ i ].name,
-                               tests: config.modules[ i ].tests
-                       } );
-               }
-
-               // The test run is officially beginning now
-               runLoggingCallbacks( "begin", {
-                       totalTests: Test.count,
-                       modules: modulesLog
-               } );
-       }
-
-       config.blocking = false;
-       process( true );
-}
-
-function process( last ) {
-       function next() {
-               process( last );
-       }
-       var start = now();
-       config.depth = ( config.depth || 0 ) + 1;
-
-       while ( config.queue.length && !config.blocking ) {
-               if ( !defined.setTimeout || config.updateRate <= 0 ||
-                               ( ( now() - start ) < config.updateRate ) ) {
-                       if ( config.current ) {
-
-                               // Reset async tracking for each phase of the Test lifecycle
-                               config.current.usedAsync = false;
-                       }
-                       config.queue.shift()();
-               } else {
-                       setTimeout( next, 13 );
-                       break;
-               }
-       }
-       config.depth--;
-       if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) {
-               done();
-       }
-}
-
-function pauseProcessing() {
-       config.blocking = true;
-
-       if ( config.testTimeout && defined.setTimeout ) {
-               clearTimeout( config.timeout );
-               config.timeout = setTimeout( function() {
-                       if ( config.current ) {
-                               config.current.semaphore = 0;
-                               QUnit.pushFailure( "Test timed out", sourceFromStacktrace( 2 ) );
-                       } else {
-                               throw new Error( "Test timed out" );
-                       }
-                       resumeProcessing();
-               }, config.testTimeout );
-       }
-}
-
-function resumeProcessing() {
-       runStarted = true;
-
-       // A slight delay to allow this iteration of the event loop to finish (more assertions, etc.)
-       if ( defined.setTimeout ) {
-               setTimeout( function() {
-                       if ( config.current && config.current.semaphore > 0 ) {
-                               return;
-                       }
-                       if ( config.timeout ) {
-                               clearTimeout( config.timeout );
-                       }
-
-                       begin();
-               }, 13 );
-       } else {
-               begin();
-       }
-}
-
-function done() {
-       var runtime, passed;
-
-       config.autorun = true;
-
-       // Log the last module results
-       if ( config.previousModule ) {
-               runLoggingCallbacks( "moduleDone", {
-                       name: config.previousModule.name,
-                       tests: config.previousModule.tests,
-                       failed: config.moduleStats.bad,
-                       passed: config.moduleStats.all - config.moduleStats.bad,
-                       total: config.moduleStats.all,
-                       runtime: now() - config.moduleStats.started
-               } );
-       }
-       delete config.previousModule;
-
-       runtime = now() - config.started;
-       passed = config.stats.all - config.stats.bad;
-
-       runLoggingCallbacks( "done", {
-               failed: config.stats.bad,
-               passed: passed,
-               total: config.stats.all,
-               runtime: runtime
-       } );
-}
-
-function setHook( module, hookName ) {
-       if ( module.testEnvironment === undefined ) {
-               module.testEnvironment = {};
-       }
-
-       return function( callback ) {
-               module.testEnvironment[ hookName ] = callback;
-       };
-}
-
-var focused = false;
-var priorityCount = 0;
-var unitSampler;
-
-function Test( settings ) {
-       var i, l;
-
-       ++Test.count;
-
-       extend( this, settings );
-       this.assertions = [];
-       this.semaphore = 0;
-       this.usedAsync = false;
-       this.module = config.currentModule;
-       this.stack = sourceFromStacktrace( 3 );
-
-       // Register unique strings
-       for ( i = 0, l = this.module.tests; i < l.length; i++ ) {
-               if ( this.module.tests[ i ].name === this.testName ) {
-                       this.testName += " ";
-               }
-       }
-
-       this.testId = generateHash( this.module.name, this.testName );
-
-       this.module.tests.push( {
-               name: this.testName,
-               testId: this.testId
-       } );
-
-       if ( settings.skip ) {
-
-               // Skipped tests will fully ignore any sent callback
-               this.callback = function() {};
-               this.async = false;
-               this.expected = 0;
-       } else {
-               this.assert = new Assert( this );
-       }
-}
-
-Test.count = 0;
-
-Test.prototype = {
-       before: function() {
-               if (
-
-                       // Emit moduleStart when we're switching from one module to another
-                       this.module !== config.previousModule ||
-
-                               // They could be equal (both undefined) but if the previousModule property doesn't
-                               // yet exist it means this is the first test in a suite that isn't wrapped in a
-                               // module, in which case we'll just emit a moduleStart event for 'undefined'.
-                               // Without this, reporters can get testStart before moduleStart  which is a problem.
-                               !hasOwn.call( config, "previousModule" )
-               ) {
-                       if ( hasOwn.call( config, "previousModule" ) ) {
-                               runLoggingCallbacks( "moduleDone", {
-                                       name: config.previousModule.name,
-                                       tests: config.previousModule.tests,
-                                       failed: config.moduleStats.bad,
-                                       passed: config.moduleStats.all - config.moduleStats.bad,
-                                       total: config.moduleStats.all,
-                                       runtime: now() - config.moduleStats.started
-                               } );
-                       }
-                       config.previousModule = this.module;
-                       config.moduleStats = { all: 0, bad: 0, started: now() };
-                       runLoggingCallbacks( "moduleStart", {
-                               name: this.module.name,
-                               tests: this.module.tests
-                       } );
-               }
-
-               config.current = this;
-
-               if ( this.module.testEnvironment ) {
-                       delete this.module.testEnvironment.beforeEach;
-                       delete this.module.testEnvironment.afterEach;
-               }
-               this.testEnvironment = extend( {}, this.module.testEnvironment );
-
-               this.started = now();
-               runLoggingCallbacks( "testStart", {
-                       name: this.testName,
-                       module: this.module.name,
-                       testId: this.testId
-               } );
-
-               if ( !config.pollution ) {
-                       saveGlobal();
-               }
-       },
-
-       run: function() {
-               var promise;
-
-               config.current = this;
-
-               if ( this.async ) {
-                       QUnit.stop();
-               }
-
-               this.callbackStarted = now();
-
-               if ( config.notrycatch ) {
-                       runTest( this );
-                       return;
-               }
-
-               try {
-                       runTest( this );
-               } catch ( e ) {
-                       this.pushFailure( "Died on test #" + ( this.assertions.length + 1 ) + " " +
-                               this.stack + ": " + ( e.message || e ), extractStacktrace( e, 0 ) );
-
-                       // Else next test will carry the responsibility
-                       saveGlobal();
-
-                       // Restart the tests if they're blocking
-                       if ( config.blocking ) {
-                               QUnit.start();
-                       }
-               }
-
-               function runTest( test ) {
-                       promise = test.callback.call( test.testEnvironment, test.assert );
-                       test.resolvePromise( promise );
-               }
-       },
-
-       after: function() {
-               checkPollution();
-       },
-
-       queueHook: function( hook, hookName ) {
-               var promise,
-                       test = this;
-               return function runHook() {
-                       config.current = test;
-                       if ( config.notrycatch ) {
-                               callHook();
-                               return;
-                       }
-                       try {
-                               callHook();
-                       } catch ( error ) {
-                               test.pushFailure( hookName + " failed on " + test.testName + ": " +
-                               ( error.message || error ), extractStacktrace( error, 0 ) );
-                       }
-
-                       function callHook() {
-                               promise = hook.call( test.testEnvironment, test.assert );
-                               test.resolvePromise( promise, hookName );
-                       }
-               };
-       },
-
-       // Currently only used for module level hooks, can be used to add global level ones
-       hooks: function( handler ) {
-               var hooks = [];
-
-               function processHooks( test, module ) {
-                       if ( module.parentModule ) {
-                               processHooks( test, module.parentModule );
-                       }
-                       if ( module.testEnvironment &&
-                               QUnit.objectType( module.testEnvironment[ handler ] ) === "function" ) {
-                               hooks.push( test.queueHook( module.testEnvironment[ handler ], handler ) );
-                       }
-               }
-
-               // Hooks are ignored on skipped tests
-               if ( !this.skip ) {
-                       processHooks( this, this.module );
-               }
-               return hooks;
-       },
-
-       finish: function() {
-               config.current = this;
-               if ( config.requireExpects && this.expected === null ) {
-                       this.pushFailure( "Expected number of assertions to be defined, but expect() was " +
-                               "not called.", this.stack );
-               } else if ( this.expected !== null && this.expected !== this.assertions.length ) {
-                       this.pushFailure( "Expected " + this.expected + " assertions, but " +
-                               this.assertions.length + " were run", this.stack );
-               } else if ( this.expected === null && !this.assertions.length ) {
-                       this.pushFailure( "Expected at least one assertion, but none were run - call " +
-                               "expect(0) to accept zero assertions.", this.stack );
-               }
-
-               var i,
-                       bad = 0;
-
-               this.runtime = now() - this.started;
-               config.stats.all += this.assertions.length;
-               config.moduleStats.all += this.assertions.length;
-
-               for ( i = 0; i < this.assertions.length; i++ ) {
-                       if ( !this.assertions[ i ].result ) {
-                               bad++;
-                               config.stats.bad++;
-                               config.moduleStats.bad++;
-                       }
-               }
-
-               runLoggingCallbacks( "testDone", {
-                       name: this.testName,
-                       module: this.module.name,
-                       skipped: !!this.skip,
-                       failed: bad,
-                       passed: this.assertions.length - bad,
-                       total: this.assertions.length,
-                       runtime: this.runtime,
-
-                       // HTML Reporter use
-                       assertions: this.assertions,
-                       testId: this.testId,
-
-                       // Source of Test
-                       source: this.stack,
-
-                       // DEPRECATED: this property will be removed in 2.0.0, use runtime instead
-                       duration: this.runtime
-               } );
-
-               // QUnit.reset() is deprecated and will be replaced for a new
-               // fixture reset function on QUnit 2.0/2.1.
-               // It's still called here for backwards compatibility handling
-               QUnit.reset();
-
-               config.current = undefined;
-       },
-
-       queue: function() {
-               var priority,
-                       test = this;
-
-               if ( !this.valid() ) {
-                       return;
-               }
-
-               function run() {
-
-                       // Each of these can by async
-                       synchronize( [
-                               function() {
-                                       test.before();
-                               },
-
-                               test.hooks( "beforeEach" ),
-                               function() {
-                                       test.run();
-                               },
-
-                               test.hooks( "afterEach" ).reverse(),
-
-                               function() {
-                                       test.after();
-                               },
-                               function() {
-                                       test.finish();
-                               }
-                       ] );
-               }
-
-               // Prioritize previously failed tests, detected from sessionStorage
-               priority = QUnit.config.reorder && defined.sessionStorage &&
-                               +sessionStorage.getItem( "qunit-test-" + this.module.name + "-" + this.testName );
-
-               return synchronize( run, priority, config.seed );
-       },
-
-       pushResult: function( resultInfo ) {
-
-               // Destructure of resultInfo = { result, actual, expected, message, negative }
-               var source,
-                       details = {
-                               module: this.module.name,
-                               name: this.testName,
-                               result: resultInfo.result,
-                               message: resultInfo.message,
-                               actual: resultInfo.actual,
-                               expected: resultInfo.expected,
-                               testId: this.testId,
-                               negative: resultInfo.negative || false,
-                               runtime: now() - this.started
-                       };
-
-               if ( !resultInfo.result ) {
-                       source = sourceFromStacktrace();
-
-                       if ( source ) {
-                               details.source = source;
-                       }
-               }
-
-               runLoggingCallbacks( "log", details );
-
-               this.assertions.push( {
-                       result: !!resultInfo.result,
-                       message: resultInfo.message
-               } );
-       },
-
-       pushFailure: function( message, source, actual ) {
-               if ( !( this instanceof Test ) ) {
-                       throw new Error( "pushFailure() assertion outside test context, was " +
-                               sourceFromStacktrace( 2 ) );
-               }
-
-               var details = {
-                               module: this.module.name,
-                               name: this.testName,
-                               result: false,
-                               message: message || "error",
-                               actual: actual || null,
-                               testId: this.testId,
-                               runtime: now() - this.started
-                       };
-
-               if ( source ) {
-                       details.source = source;
-               }
-
-               runLoggingCallbacks( "log", details );
-
-               this.assertions.push( {
-                       result: false,
-                       message: message
-               } );
-       },
-
-       resolvePromise: function( promise, phase ) {
-               var then, message,
-                       test = this;
-               if ( promise != null ) {
-                       then = promise.then;
-                       if ( QUnit.objectType( then ) === "function" ) {
-                               QUnit.stop();
-                               then.call(
-                                       promise,
-                                       function() { QUnit.start(); },
-                                       function( error ) {
-                                               message = "Promise rejected " +
-                                                       ( !phase ? "during" : phase.replace( /Each$/, "" ) ) +
-                                                       " " + test.testName + ": " + ( error.message || error );
-                                               test.pushFailure( message, extractStacktrace( error, 0 ) );
-
-                                               // Else next test will carry the responsibility
-                                               saveGlobal();
-
-                                               // Unblock
-                                               QUnit.start();
-                                       }
-                               );
-                       }
-               }
-       },
-
-       valid: function() {
-               var filter = config.filter,
-                       regexFilter = /^(!?)\/([\w\W]*)\/(i?$)/.exec( filter ),
-                       module = config.module && config.module.toLowerCase(),
-                       fullName = ( this.module.name + ": " + this.testName );
-
-               function moduleChainNameMatch( testModule ) {
-                       var testModuleName = testModule.name ? testModule.name.toLowerCase() : null;
-                       if ( testModuleName === module ) {
-                               return true;
-                       } else if ( testModule.parentModule ) {
-                               return moduleChainNameMatch( testModule.parentModule );
-                       } else {
-                               return false;
-                       }
-               }
-
-               function moduleChainIdMatch( testModule ) {
-                       return inArray( testModule.moduleId, config.moduleId ) > -1 ||
-                               testModule.parentModule && moduleChainIdMatch( testModule.parentModule );
-               }
-
-               // Internally-generated tests are always valid
-               if ( this.callback && this.callback.validTest ) {
-                       return true;
-               }
-
-               if ( config.moduleId && config.moduleId.length > 0 &&
-                       !moduleChainIdMatch( this.module ) ) {
-
-                       return false;
-               }
-
-               if ( config.testId && config.testId.length > 0 &&
-                       inArray( this.testId, config.testId ) < 0 ) {
-
-                       return false;
-               }
-
-               if ( module && !moduleChainNameMatch( this.module ) ) {
-                       return false;
-               }
-
-               if ( !filter ) {
-                       return true;
-               }
-
-               return regexFilter ?
-                       this.regexFilter( !!regexFilter[ 1 ], regexFilter[ 2 ], regexFilter[ 3 ], fullName ) :
-                       this.stringFilter( filter, fullName );
-       },
-
-       regexFilter: function( exclude, pattern, flags, fullName ) {
-               var regex = new RegExp( pattern, flags );
-               var match = regex.test( fullName );
-
-               return match !== exclude;
-       },
-
-       stringFilter: function( filter, fullName ) {
-               filter = filter.toLowerCase();
-               fullName = fullName.toLowerCase();
-
-               var include = filter.charAt( 0 ) !== "!";
-               if ( !include ) {
-                       filter = filter.slice( 1 );
-               }
-
-               // If the filter matches, we need to honour include
-               if ( fullName.indexOf( filter ) !== -1 ) {
-                       return include;
-               }
-
-               // Otherwise, do the opposite
-               return !include;
-       }
-};
-
-// Resets the test setup. Useful for tests that modify the DOM.
-/*
-DEPRECATED: Use multiple tests instead of resetting inside a test.
-Use testStart or testDone for custom cleanup.
-This method will throw an error in 2.0, and will be removed in 2.1
-*/
-QUnit.reset = function() {
-
-       // Return on non-browser environments
-       // This is necessary to not break on node tests
-       if ( !defined.document ) {
-               return;
-       }
-
-       var fixture = defined.document && document.getElementById &&
-                       document.getElementById( "qunit-fixture" );
-
-       if ( fixture ) {
-               fixture.innerHTML = config.fixture;
-       }
-};
-
-QUnit.pushFailure = function() {
-       if ( !QUnit.config.current ) {
-               throw new Error( "pushFailure() assertion outside test context, in " +
-                       sourceFromStacktrace( 2 ) );
-       }
-
-       // Gets current test obj
-       var currentTest = QUnit.config.current;
-
-       return currentTest.pushFailure.apply( currentTest, arguments );
-};
-
-// Based on Java's String.hashCode, a simple but not
-// rigorously collision resistant hashing function
-function generateHash( module, testName ) {
-       var hex,
-               i = 0,
-               hash = 0,
-               str = module + "\x1C" + testName,
-               len = str.length;
-
-       for ( ; i < len; i++ ) {
-               hash  = ( ( hash << 5 ) - hash ) + str.charCodeAt( i );
-               hash |= 0;
-       }
-
-       // Convert the possibly negative integer hash code into an 8 character hex string, which isn't
-       // strictly necessary but increases user understanding that the id is a SHA-like hash
-       hex = ( 0x100000000 + hash ).toString( 16 );
-       if ( hex.length < 8 ) {
-               hex = "0000000" + hex;
-       }
-
-       return hex.slice( -8 );
-}
-
-function synchronize( callback, priority, seed ) {
-       var last = !priority,
-               index;
-
-       if ( QUnit.objectType( callback ) === "array" ) {
-               while ( callback.length ) {
-                       synchronize( callback.shift() );
-               }
-               return;
-       }
-
-       if ( priority ) {
-               config.queue.splice( priorityCount++, 0, callback );
-       } else if ( seed ) {
-               if ( !unitSampler ) {
-                       unitSampler = unitSamplerGenerator( seed );
-               }
-
-               // Insert into a random position after all priority items
-               index = Math.floor( unitSampler() * ( config.queue.length - priorityCount + 1 ) );
-               config.queue.splice( priorityCount + index, 0, callback );
-       } else {
-               config.queue.push( callback );
-       }
-
-       if ( config.autorun && !config.blocking ) {
-               process( last );
-       }
-}
-
-function unitSamplerGenerator( seed ) {
-
-       // 32-bit xorshift, requires only a nonzero seed
-       // http://excamera.com/sphinx/article-xorshift.html
-       var sample = parseInt( generateHash( seed ), 16 ) || -1;
-       return function() {
-               sample ^= sample << 13;
-               sample ^= sample >>> 17;
-               sample ^= sample << 5;
-
-               // ECMAScript has no unsigned number type
-               if ( sample < 0 ) {
-                       sample += 0x100000000;
-               }
-
-               return sample / 0x100000000;
-       };
-}
-
-function saveGlobal() {
-       config.pollution = [];
-
-       if ( config.noglobals ) {
-               for ( var key in global ) {
-                       if ( hasOwn.call( global, key ) ) {
-
-                               // In Opera sometimes DOM element ids show up here, ignore them
-                               if ( /^qunit-test-output/.test( key ) ) {
-                                       continue;
-                               }
-                               config.pollution.push( key );
-                       }
-               }
-       }
-}
-
-function checkPollution() {
-       var newGlobals,
-               deletedGlobals,
-               old = config.pollution;
-
-       saveGlobal();
-
-       newGlobals = diff( config.pollution, old );
-       if ( newGlobals.length > 0 ) {
-               QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join( ", " ) );
-       }
-
-       deletedGlobals = diff( old, config.pollution );
-       if ( deletedGlobals.length > 0 ) {
-               QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join( ", " ) );
-       }
-}
-
-// Will be exposed as QUnit.asyncTest
-function asyncTest( testName, expected, callback ) {
-       if ( arguments.length === 2 ) {
-               callback = expected;
-               expected = null;
-       }
-
-       QUnit.test( testName, expected, callback, true );
-}
-
-// Will be exposed as QUnit.test
-function test( testName, expected, callback, async ) {
-       if ( focused )  { return; }
-
-       var newTest;
-
-       if ( arguments.length === 2 ) {
-               callback = expected;
-               expected = null;
-       }
-
-       newTest = new Test( {
-               testName: testName,
-               expected: expected,
-               async: async,
-               callback: callback
-       } );
-
-       newTest.queue();
-}
-
-// Will be exposed as QUnit.skip
-function skip( testName ) {
-       if ( focused )  { return; }
-
-       var test = new Test( {
-               testName: testName,
-               skip: true
-       } );
-
-       test.queue();
-}
-
-// Will be exposed as QUnit.only
-function only( testName, expected, callback, async ) {
-       var newTest;
-
-       if ( focused )  { return; }
-
-       QUnit.config.queue.length = 0;
-       focused = true;
-
-       if ( arguments.length === 2 ) {
-               callback = expected;
-               expected = null;
-       }
-
-       newTest = new Test( {
-               testName: testName,
-               expected: expected,
-               async: async,
-               callback: callback
-       } );
-
-       newTest.queue();
-}
-
-function Assert( testContext ) {
-       this.test = testContext;
-}
-
-// Assert helpers
-QUnit.assert = Assert.prototype = {
-
-       // Specify the number of expected assertions to guarantee that failed test
-       // (no assertions are run at all) don't slip through.
-       expect: function( asserts ) {
-               if ( arguments.length === 1 ) {
-                       this.test.expected = asserts;
-               } else {
-                       return this.test.expected;
-               }
-       },
-
-       // Increment this Test's semaphore counter, then return a function that
-       // decrements that counter a maximum of once.
-       async: function( count ) {
-               var test = this.test,
-                       popped = false,
-                       acceptCallCount = count;
-
-               if ( typeof acceptCallCount === "undefined" ) {
-                       acceptCallCount = 1;
-               }
-
-               test.semaphore += 1;
-               test.usedAsync = true;
-               pauseProcessing();
-
-               return function done() {
-
-                       if ( popped ) {
-                               test.pushFailure( "Too many calls to the `assert.async` callback",
-                                       sourceFromStacktrace( 2 ) );
-                               return;
-                       }
-                       acceptCallCount -= 1;
-                       if ( acceptCallCount > 0 ) {
-                               return;
-                       }
-
-                       test.semaphore -= 1;
-                       popped = true;
-                       resumeProcessing();
-               };
-       },
-
-       // Exports test.push() to the user API
-       // Alias of pushResult.
-       push: function( result, actual, expected, message, negative ) {
-               var currentAssert = this instanceof Assert ? this : QUnit.config.current.assert;
-               return currentAssert.pushResult( {
-                       result: result,
-                       actual: actual,
-                       expected: expected,
-                       message: message,
-                       negative: negative
-               } );
-       },
-
-       pushResult: function( resultInfo ) {
-
-               // Destructure of resultInfo = { result, actual, expected, message, negative }
-               var assert = this,
-                       currentTest = ( assert instanceof Assert && assert.test ) || QUnit.config.current;
-
-               // Backwards compatibility fix.
-               // Allows the direct use of global exported assertions and QUnit.assert.*
-               // Although, it's use is not recommended as it can leak assertions
-               // to other tests from async tests, because we only get a reference to the current test,
-               // not exactly the test where assertion were intended to be called.
-               if ( !currentTest ) {
-                       throw new Error( "assertion outside test context, in " + sourceFromStacktrace( 2 ) );
-               }
-
-               if ( currentTest.usedAsync === true && currentTest.semaphore === 0 ) {
-                       currentTest.pushFailure( "Assertion after the final `assert.async` was resolved",
-                               sourceFromStacktrace( 2 ) );
-
-                       // Allow this assertion to continue running anyway...
-               }
-
-               if ( !( assert instanceof Assert ) ) {
-                       assert = currentTest.assert;
-               }
-
-               return assert.test.pushResult( resultInfo );
-       },
-
-       ok: function( result, message ) {
-               message = message || ( result ? "okay" : "failed, expected argument to be truthy, was: " +
-                       QUnit.dump.parse( result ) );
-               this.pushResult( {
-                       result: !!result,
-                       actual: result,
-                       expected: true,
-                       message: message
-               } );
-       },
-
-       notOk: function( result, message ) {
-               message = message || ( !result ? "okay" : "failed, expected argument to be falsy, was: " +
-                       QUnit.dump.parse( result ) );
-               this.pushResult( {
-                       result: !result,
-                       actual: result,
-                       expected: false,
-                       message: message
-               } );
-       },
-
-       equal: function( actual, expected, message ) {
-               /*jshint eqeqeq:false */
-               this.pushResult( {
-                       result: expected == actual,
-                       actual: actual,
-                       expected: expected,
-                       message: message
-               } );
-       },
-
-       notEqual: function( actual, expected, message ) {
-               /*jshint eqeqeq:false */
-               this.pushResult( {
-                       result: expected != actual,
-                       actual: actual,
-                       expected: expected,
-                       message: message,
-                       negative: true
-               } );
-       },
-
-       propEqual: function( actual, expected, message ) {
-               actual = objectValues( actual );
-               expected = objectValues( expected );
-               this.pushResult( {
-                       result: QUnit.equiv( actual, expected ),
-                       actual: actual,
-                       expected: expected,
-                       message: message
-               } );
-       },
-
-       notPropEqual: function( actual, expected, message ) {
-               actual = objectValues( actual );
-               expected = objectValues( expected );
-               this.pushResult( {
-                       result: !QUnit.equiv( actual, expected ),
-                       actual: actual,
-                       expected: expected,
-                       message: message,
-                       negative: true
-               } );
-       },
-
-       deepEqual: function( actual, expected, message ) {
-               this.pushResult( {
-                       result: QUnit.equiv( actual, expected ),
-                       actual: actual,
-                       expected: expected,
-                       message: message
-               } );
-       },
-
-       notDeepEqual: function( actual, expected, message ) {
-               this.pushResult( {
-                       result: !QUnit.equiv( actual, expected ),
-                       actual: actual,
-                       expected: expected,
-                       message: message,
-                       negative: true
-               } );
-       },
-
-       strictEqual: function( actual, expected, message ) {
-               this.pushResult( {
-                       result: expected === actual,
-                       actual: actual,
-                       expected: expected,
-                       message: message
-               } );
-       },
-
-       notStrictEqual: function( actual, expected, message ) {
-               this.pushResult( {
-                       result: expected !== actual,
-                       actual: actual,
-                       expected: expected,
-                       message: message,
-                       negative: true
-               } );
-       },
-
-       "throws": function( block, expected, message ) {
-               var actual, expectedType,
-                       expectedOutput = expected,
-                       ok = false,
-                       currentTest = ( this instanceof Assert && this.test ) || QUnit.config.current;
-
-               // 'expected' is optional unless doing string comparison
-               if ( message == null && typeof expected === "string" ) {
-                       message = expected;
-                       expected = null;
-               }
-
-               currentTest.ignoreGlobalErrors = true;
-               try {
-                       block.call( currentTest.testEnvironment );
-               } catch ( e ) {
-                       actual = e;
-               }
-               currentTest.ignoreGlobalErrors = false;
-
-               if ( actual ) {
-                       expectedType = QUnit.objectType( expected );
-
-                       // We don't want to validate thrown error
-                       if ( !expected ) {
-                               ok = true;
-                               expectedOutput = null;
-
-                       // Expected is a regexp
-                       } else if ( expectedType === "regexp" ) {
-                               ok = expected.test( errorString( actual ) );
-
-                       // Expected is a string
-                       } else if ( expectedType === "string" ) {
-                               ok = expected === errorString( actual );
-
-                       // Expected is a constructor, maybe an Error constructor
-                       } else if ( expectedType === "function" && actual instanceof expected ) {
-                               ok = true;
-
-                       // Expected is an Error object
-                       } else if ( expectedType === "object" ) {
-                               ok = actual instanceof expected.constructor &&
-                                       actual.name === expected.name &&
-                                       actual.message === expected.message;
-
-                       // Expected is a validation function which returns true if validation passed
-                       } else if ( expectedType === "function" && expected.call( {}, actual ) === true ) {
-                               expectedOutput = null;
-                               ok = true;
-                       }
-               }
-
-               currentTest.assert.pushResult( {
-                       result: ok,
-                       actual: actual,
-                       expected: expectedOutput,
-                       message: message
-               } );
-       }
-};
-
-// Provide an alternative to assert.throws(), for environments that consider throws a reserved word
-// Known to us are: Closure Compiler, Narwhal
-( function() {
-       /*jshint sub:true */
-       Assert.prototype.raises = Assert.prototype [ "throws" ]; //jscs:ignore requireDotNotation
-}() );
-
-function errorString( error ) {
-       var name, message,
-               resultErrorString = error.toString();
-       if ( resultErrorString.substring( 0, 7 ) === "[object" ) {
-               name = error.name ? error.name.toString() : "Error";
-               message = error.message ? error.message.toString() : "";
-               if ( name && message ) {
-                       return name + ": " + message;
-               } else if ( name ) {
-                       return name;
-               } else if ( message ) {
-                       return message;
-               } else {
-                       return "Error";
-               }
-       } else {
-               return resultErrorString;
-       }
-}
-
-// Test for equality any JavaScript type.
-// Author: Philippe Rathé <prathe@gmail.com>
-QUnit.equiv = ( function() {
-
-       // Stack to decide between skip/abort functions
-       var callers = [];
-
-       // Stack to avoiding loops from circular referencing
-       var parents = [];
-       var parentsB = [];
-
-       var getProto = Object.getPrototypeOf || function( obj ) {
-
-               /*jshint proto: true */
-               return obj.__proto__;
-       };
-
-       function useStrictEquality( b, a ) {
-
-               // To catch short annotation VS 'new' annotation of a declaration. e.g.:
-               // `var i = 1;`
-               // `var j = new Number(1);`
-               if ( typeof a === "object" ) {
-                       a = a.valueOf();
-               }
-               if ( typeof b === "object" ) {
-                       b = b.valueOf();
-               }
-
-               return a === b;
-       }
-
-       function compareConstructors( a, b ) {
-               var protoA = getProto( a );
-               var protoB = getProto( b );
-
-               // Comparing constructors is more strict than using `instanceof`
-               if ( a.constructor === b.constructor ) {
-                       return true;
-               }
-
-               // Ref #851
-               // If the obj prototype descends from a null constructor, treat it
-               // as a null prototype.
-               if ( protoA && protoA.constructor === null ) {
-                       protoA = null;
-               }
-               if ( protoB && protoB.constructor === null ) {
-                       protoB = null;
-               }
-
-               // Allow objects with no prototype to be equivalent to
-               // objects with Object as their constructor.
-               if ( ( protoA === null && protoB === Object.prototype ) ||
-                               ( protoB === null && protoA === Object.prototype ) ) {
-                       return true;
-               }
-
-               return false;
-       }
-
-       function getRegExpFlags( regexp ) {
-               return "flags" in regexp ? regexp.flags : regexp.toString().match( /[gimuy]*$/ )[ 0 ];
-       }
-
-       var callbacks = {
-               "string": useStrictEquality,
-               "boolean": useStrictEquality,
-               "number": useStrictEquality,
-               "null": useStrictEquality,
-               "undefined": useStrictEquality,
-               "symbol": useStrictEquality,
-               "date": useStrictEquality,
-
-               "nan": function() {
-                       return true;
-               },
-
-               "regexp": function( b, a ) {
-                       return a.source === b.source &&
-
-                               // Include flags in the comparison
-                               getRegExpFlags( a ) === getRegExpFlags( b );
-               },
-
-               // - skip when the property is a method of an instance (OOP)
-               // - abort otherwise,
-               // initial === would have catch identical references anyway
-               "function": function() {
-                       var caller = callers[ callers.length - 1 ];
-                       return caller !== Object && typeof caller !== "undefined";
-               },
-
-               "array": function( b, a ) {
-                       var i, j, len, loop, aCircular, bCircular;
-
-                       len = a.length;
-                       if ( len !== b.length ) {
-
-                               // Safe and faster
-                               return false;
-                       }
-
-                       // Track reference to avoid circular references
-                       parents.push( a );
-                       parentsB.push( b );
-                       for ( i = 0; i < len; i++ ) {
-                               loop = false;
-                               for ( j = 0; j < parents.length; j++ ) {
-                                       aCircular = parents[ j ] === a[ i ];
-                                       bCircular = parentsB[ j ] === b[ i ];
-                                       if ( aCircular || bCircular ) {
-                                               if ( a[ i ] === b[ i ] || aCircular && bCircular ) {
-                                                       loop = true;
-                                               } else {
-                                                       parents.pop();
-                                                       parentsB.pop();
-                                                       return false;
-                                               }
-                                       }
-                               }
-                               if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) {
-                                       parents.pop();
-                                       parentsB.pop();
-                                       return false;
-                               }
-                       }
-                       parents.pop();
-                       parentsB.pop();
-                       return true;
-               },
-
-               "set": function( b, a ) {
-                       var innerEq,
-                               outerEq = true;
-
-                       if ( a.size !== b.size ) {
-                               return false;
-                       }
-
-                       a.forEach( function( aVal ) {
-                               innerEq = false;
-
-                               b.forEach( function( bVal ) {
-                                       if ( innerEquiv( bVal, aVal ) ) {
-                                               innerEq = true;
-                                       }
-                               } );
-
-                               if ( !innerEq ) {
-                                       outerEq = false;
-                               }
-                       } );
-
-                       return outerEq;
-               },
-
-               "map": function( b, a ) {
-                       var innerEq,
-                               outerEq = true;
-
-                       if ( a.size !== b.size ) {
-                               return false;
-                       }
-
-                       a.forEach( function( aVal, aKey ) {
-                               innerEq = false;
-
-                               b.forEach( function( bVal, bKey ) {
-                                       if ( innerEquiv( [ bVal, bKey ], [ aVal, aKey ] ) ) {
-                                               innerEq = true;
-                                       }
-                               } );
-
-                               if ( !innerEq ) {
-                                       outerEq = false;
-                               }
-                       } );
-
-                       return outerEq;
-               },
-
-               "object": function( b, a ) {
-                       var i, j, loop, aCircular, bCircular;
-
-                       // Default to true
-                       var eq = true;
-                       var aProperties = [];
-                       var bProperties = [];
-
-                       if ( compareConstructors( a, b ) === false ) {
-                               return false;
-                       }
-
-                       // Stack constructor before traversing properties
-                       callers.push( a.constructor );
-
-                       // Track reference to avoid circular references
-                       parents.push( a );
-                       parentsB.push( b );
-
-                       // Be strict: don't ensure hasOwnProperty and go deep
-                       for ( i in a ) {
-                               loop = false;
-                               for ( j = 0; j < parents.length; j++ ) {
-                                       aCircular = parents[ j ] === a[ i ];
-                                       bCircular = parentsB[ j ] === b[ i ];
-                                       if ( aCircular || bCircular ) {
-                                               if ( a[ i ] === b[ i ] || aCircular && bCircular ) {
-                                                       loop = true;
-                                               } else {
-                                                       eq = false;
-                                                       break;
-                                               }
-                                       }
-                               }
-                               aProperties.push( i );
-                               if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) {
-                                       eq = false;
-                                       break;
-                               }
-                       }
-
-                       parents.pop();
-                       parentsB.pop();
-
-                       // Unstack, we are done
-                       callers.pop();
-
-                       for ( i in b ) {
-
-                               // Collect b's properties
-                               bProperties.push( i );
-                       }
-
-                       // Ensures identical properties name
-                       return eq && innerEquiv( aProperties.sort(), bProperties.sort() );
-               }
-       };
-
-       function typeEquiv( a, b ) {
-               var type = QUnit.objectType( a );
-               return QUnit.objectType( b ) === type && callbacks[ type ]( b, a );
-       }
-
-       // The real equiv function
-       function innerEquiv( a, b ) {
-
-               // We're done when there's nothing more to compare
-               if ( arguments.length < 2 ) {
-                       return true;
-               }
-
-               // Require type-specific equality
-               return ( a === b || typeEquiv( a, b ) ) &&
-
-                       // ...across all consecutive argument pairs
-                       ( arguments.length === 2 || innerEquiv.apply( this, [].slice.call( arguments, 1 ) ) );
-       }
-
-       return innerEquiv;
-}() );
-
-// Based on jsDump by Ariel Flesler
-// http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html
-QUnit.dump = ( function() {
-       function quote( str ) {
-               return "\"" + str.toString().replace( /\\/g, "\\\\" ).replace( /"/g, "\\\"" ) + "\"";
-       }
-       function literal( o ) {
-               return o + "";
-       }
-       function join( pre, arr, post ) {
-               var s = dump.separator(),
-                       base = dump.indent(),
-                       inner = dump.indent( 1 );
-               if ( arr.join ) {
-                       arr = arr.join( "," + s + inner );
-               }
-               if ( !arr ) {
-                       return pre + post;
-               }
-               return [ pre, inner + arr, base + post ].join( s );
-       }
-       function array( arr, stack ) {
-               var i = arr.length,
-                       ret = new Array( i );
-
-               if ( dump.maxDepth && dump.depth > dump.maxDepth ) {
-                       return "[object Array]";
-               }
-
-               this.up();
-               while ( i-- ) {
-                       ret[ i ] = this.parse( arr[ i ], undefined, stack );
-               }
-               this.down();
-               return join( "[", ret, "]" );
-       }
-
-       var reName = /^function (\w+)/,
-               dump = {
-
-                       // The objType is used mostly internally, you can fix a (custom) type in advance
-                       parse: function( obj, objType, stack ) {
-                               stack = stack || [];
-                               var res, parser, parserType,
-                                       inStack = inArray( obj, stack );
-
-                               if ( inStack !== -1 ) {
-                                       return "recursion(" + ( inStack - stack.length ) + ")";
-                               }
-
-                               objType = objType || this.typeOf( obj  );
-                               parser = this.parsers[ objType ];
-                               parserType = typeof parser;
-
-                               if ( parserType === "function" ) {
-                                       stack.push( obj );
-                                       res = parser.call( this, obj, stack );
-                                       stack.pop();
-                                       return res;
-                               }
-                               return ( parserType === "string" ) ? parser : this.parsers.error;
-                       },
-                       typeOf: function( obj ) {
-                               var type;
-                               if ( obj === null ) {
-                                       type = "null";
-                               } else if ( typeof obj === "undefined" ) {
-                                       type = "undefined";
-                               } else if ( QUnit.is( "regexp", obj ) ) {
-                                       type = "regexp";
-                               } else if ( QUnit.is( "date", obj ) ) {
-                                       type = "date";
-                               } else if ( QUnit.is( "function", obj ) ) {
-                                       type = "function";
-                               } else if ( obj.setInterval !== undefined &&
-                                               obj.document !== undefined &&
-                                               obj.nodeType === undefined ) {
-                                       type = "window";
-                               } else if ( obj.nodeType === 9 ) {
-                                       type = "document";
-                               } else if ( obj.nodeType ) {
-                                       type = "node";
-                               } else if (
-
-                                       // Native arrays
-                                       toString.call( obj ) === "[object Array]" ||
-
-                                       // NodeList objects
-                                       ( typeof obj.length === "number" && obj.item !== undefined &&
-                                       ( obj.length ? obj.item( 0 ) === obj[ 0 ] : ( obj.item( 0 ) === null &&
-                                       obj[ 0 ] === undefined ) ) )
-                               ) {
-                                       type = "array";
-                               } else if ( obj.constructor === Error.prototype.constructor ) {
-                                       type = "error";
-                               } else {
-                                       type = typeof obj;
-                               }
-                               return type;
-                       },
-
-                       separator: function() {
-                               return this.multiline ? this.HTML ? "<br />" : "\n" : this.HTML ? "&#160;" : " ";
-                       },
-
-                       // Extra can be a number, shortcut for increasing-calling-decreasing
-                       indent: function( extra ) {
-                               if ( !this.multiline ) {
-                                       return "";
-                               }
-                               var chr = this.indentChar;
-                               if ( this.HTML ) {
-                                       chr = chr.replace( /\t/g, "   " ).replace( / /g, "&#160;" );
-                               }
-                               return new Array( this.depth + ( extra || 0 ) ).join( chr );
-                       },
-                       up: function( a ) {
-                               this.depth += a || 1;
-                       },
-                       down: function( a ) {
-                               this.depth -= a || 1;
-                       },
-                       setParser: function( name, parser ) {
-                               this.parsers[ name ] = parser;
-                       },
-
-                       // The next 3 are exposed so you can use them
-                       quote: quote,
-                       literal: literal,
-                       join: join,
-                       depth: 1,
-                       maxDepth: QUnit.config.maxDepth,
-
-                       // This is the list of parsers, to modify them, use dump.setParser
-                       parsers: {
-                               window: "[Window]",
-                               document: "[Document]",
-                               error: function( error ) {
-                                       return "Error(\"" + error.message + "\")";
-                               },
-                               unknown: "[Unknown]",
-                               "null": "null",
-                               "undefined": "undefined",
-                               "function": function( fn ) {
-                                       var ret = "function",
-
-                                               // Functions never have name in IE
-                                               name = "name" in fn ? fn.name : ( reName.exec( fn ) || [] )[ 1 ];
-
-                                       if ( name ) {
-                                               ret += " " + name;
-                                       }
-                                       ret += "(";
-
-                                       ret = [ ret, dump.parse( fn, "functionArgs" ), "){" ].join( "" );
-                                       return join( ret, dump.parse( fn, "functionCode" ), "}" );
-                               },
-                               array: array,
-                               nodelist: array,
-                               "arguments": array,
-                               object: function( map, stack ) {
-                                       var keys, key, val, i, nonEnumerableProperties,
-                                               ret = [];
-
-                                       if ( dump.maxDepth && dump.depth > dump.maxDepth ) {
-                                               return "[object Object]";
-                                       }
-
-                                       dump.up();
-                                       keys = [];
-                                       for ( key in map ) {
-                                               keys.push( key );
-                                       }
-
-                                       // Some properties are not always enumerable on Error objects.
-                                       nonEnumerableProperties = [ "message", "name" ];
-                                       for ( i in nonEnumerableProperties ) {
-                                               key = nonEnumerableProperties[ i ];
-                                               if ( key in map && inArray( key, keys ) < 0 ) {
-                                                       keys.push( key );
-                                               }
-                                       }
-                                       keys.sort();
-                                       for ( i = 0; i < keys.length; i++ ) {
-                                               key = keys[ i ];
-                                               val = map[ key ];
-                                               ret.push( dump.parse( key, "key" ) + ": " +
-                                                       dump.parse( val, undefined, stack ) );
-                                       }
-                                       dump.down();
-                                       return join( "{", ret, "}" );
-                               },
-                               node: function( node ) {
-                                       var len, i, val,
-                                               open = dump.HTML ? "&lt;" : "<",
-                                               close = dump.HTML ? "&gt;" : ">",
-                                               tag = node.nodeName.toLowerCase(),
-                                               ret = open + tag,
-                                               attrs = node.attributes;
-
-                                       if ( attrs ) {
-                                               for ( i = 0, len = attrs.length; i < len; i++ ) {
-                                                       val = attrs[ i ].nodeValue;
-
-                                                       // IE6 includes all attributes in .attributes, even ones not explicitly
-                                                       // set. Those have values like undefined, null, 0, false, "" or
-                                                       // "inherit".
-                                                       if ( val && val !== "inherit" ) {
-                                                               ret += " " + attrs[ i ].nodeName + "=" +
-                                                                       dump.parse( val, "attribute" );
-                                                       }
-                                               }
-                                       }
-                                       ret += close;
-
-                                       // Show content of TextNode or CDATASection
-                                       if ( node.nodeType === 3 || node.nodeType === 4 ) {
-                                               ret += node.nodeValue;
-                                       }
-
-                                       return ret + open + "/" + tag + close;
-                               },
-
-                               // Function calls it internally, it's the arguments part of the function
-                               functionArgs: function( fn ) {
-                                       var args,
-                                               l = fn.length;
-
-                                       if ( !l ) {
-                                               return "";
-                                       }
-
-                                       args = new Array( l );
-                                       while ( l-- ) {
-
-                                               // 97 is 'a'
-                                               args[ l ] = String.fromCharCode( 97 + l );
-                                       }
-                                       return " " + args.join( ", " ) + " ";
-                               },
-
-                               // Object calls it internally, the key part of an item in a map
-                               key: quote,
-
-                               // Function calls it internally, it's the content of the function
-                               functionCode: "[code]",
-
-                               // Node calls it internally, it's a html attribute value
-                               attribute: quote,
-                               string: quote,
-                               date: quote,
-                               regexp: literal,
-                               number: literal,
-                               "boolean": literal
-                       },
-
-                       // If true, entities are escaped ( <, >, \t, space and \n )
-                       HTML: false,
-
-                       // Indentation unit
-                       indentChar: "  ",
-
-                       // If true, items in a collection, are separated by a \n, else just a space.
-                       multiline: true
-               };
-
-       return dump;
-}() );
-
-// Back compat
-QUnit.jsDump = QUnit.dump;
-
-// Deprecated
-// Extend assert methods to QUnit for Backwards compatibility
-( function() {
-       var i,
-               assertions = Assert.prototype;
-
-       function applyCurrent( current ) {
-               return function() {
-                       var assert = new Assert( QUnit.config.current );
-                       current.apply( assert, arguments );
-               };
-       }
-
-       for ( i in assertions ) {
-               QUnit[ i ] = applyCurrent( assertions[ i ] );
-       }
-}() );
-
-// For browser, export only select globals
-if ( defined.document ) {
-
-       ( function() {
-               var i, l,
-                       keys = [
-                               "test",
-                               "module",
-                               "expect",
-                               "asyncTest",
-                               "start",
-                               "stop",
-                               "ok",
-                               "notOk",
-                               "equal",
-                               "notEqual",
-                               "propEqual",
-                               "notPropEqual",
-                               "deepEqual",
-                               "notDeepEqual",
-                               "strictEqual",
-                               "notStrictEqual",
-                               "throws",
-                               "raises"
-                       ];
-
-               for ( i = 0, l = keys.length; i < l; i++ ) {
-                       window[ keys[ i ] ] = QUnit[ keys[ i ] ];
-               }
-       }() );
-
-       window.QUnit = QUnit;
-}
-
-// For nodejs
-if ( typeof module !== "undefined" && module && module.exports ) {
-       module.exports = QUnit;
-
-       // For consistency with CommonJS environments' exports
-       module.exports.QUnit = QUnit;
-}
-
-// For CommonJS with exports, but without module.exports, like Rhino
-if ( typeof exports !== "undefined" && exports ) {
-       exports.QUnit = QUnit;
-}
-
-if ( typeof define === "function" && define.amd ) {
-       define( function() {
-               return QUnit;
-       } );
-       QUnit.config.autostart = false;
-}
-
-// Get a reference to the global object, like window in browsers
-}( ( function() {
-       return this;
-}() ) ) );
-
-( function() {
-
-// Only interact with URLs via window.location
-var location = typeof window !== "undefined" && window.location;
-if ( !location ) {
-       return;
-}
-
-var urlParams = getUrlParams();
-
-QUnit.urlParams = urlParams;
-
-// Match module/test by inclusion in an array
-QUnit.config.moduleId = [].concat( urlParams.moduleId || [] );
-QUnit.config.testId = [].concat( urlParams.testId || [] );
-
-// Exact case-insensitive match of the module name
-QUnit.config.module = urlParams.module;
-
-// Regular expression or case-insenstive substring match against "moduleName: testName"
-QUnit.config.filter = urlParams.filter;
-
-// Test order randomization
-if ( urlParams.seed === true ) {
-
-       // Generate a random seed if the option is specified without a value
-       QUnit.config.seed = Math.random().toString( 36 ).slice( 2 );
-} else if ( urlParams.seed ) {
-       QUnit.config.seed = urlParams.seed;
-}
-
-// Add URL-parameter-mapped config values with UI form rendering data
-QUnit.config.urlConfig.push(
-       {
-               id: "hidepassed",
-               label: "Hide passed tests",
-               tooltip: "Only show tests and assertions that fail. Stored as query-strings."
-       },
-       {
-               id: "noglobals",
-               label: "Check for Globals",
-               tooltip: "Enabling this will test if any test introduces new properties on the " +
-                       "global object (`window` in Browsers). Stored as query-strings."
-       },
-       {
-               id: "notrycatch",
-               label: "No try-catch",
-               tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging " +
-                       "exceptions in IE reasonable. Stored as query-strings."
-       }
-);
-
-QUnit.begin( function() {
-       var i, option,
-               urlConfig = QUnit.config.urlConfig;
-
-       for ( i = 0; i < urlConfig.length; i++ ) {
-
-               // Options can be either strings or objects with nonempty "id" properties
-               option = QUnit.config.urlConfig[ i ];
-               if ( typeof option !== "string" ) {
-                       option = option.id;
-               }
-
-               if ( QUnit.config[ option ] === undefined ) {
-                       QUnit.config[ option ] = urlParams[ option ];
-               }
-       }
-} );
-
-function getUrlParams() {
-       var i, param, name, value;
-       var urlParams = {};
-       var params = location.search.slice( 1 ).split( "&" );
-       var length = params.length;
-
-       for ( i = 0; i < length; i++ ) {
-               if ( params[ i ] ) {
-                       param = params[ i ].split( "=" );
-                       name = decodeURIComponent( param[ 0 ] );
-
-                       // Allow just a key to turn on a flag, e.g., test.html?noglobals
-                       value = param.length === 1 ||
-                               decodeURIComponent( param.slice( 1 ).join( "=" ) ) ;
-                       if ( urlParams[ name ] ) {
-                               urlParams[ name ] = [].concat( urlParams[ name ], value );
-                       } else {
-                               urlParams[ name ] = value;
-                       }
-               }
-       }
-
-       return urlParams;
-}
-
-// Don't load the HTML Reporter on non-browser environments
-if ( typeof window === "undefined" || !window.document ) {
-       return;
-}
-
-// Deprecated QUnit.init - Ref #530
-// Re-initialize the configuration options
-QUnit.init = function() {
-       var config = QUnit.config;
-
-       config.stats = { all: 0, bad: 0 };
-       config.moduleStats = { all: 0, bad: 0 };
-       config.started = 0;
-       config.updateRate = 1000;
-       config.blocking = false;
-       config.autostart = true;
-       config.autorun = false;
-       config.filter = "";
-       config.queue = [];
-
-       appendInterface();
-};
-
-var config = QUnit.config,
-       document = window.document,
-       collapseNext = false,
-       hasOwn = Object.prototype.hasOwnProperty,
-       unfilteredUrl = setUrl( { filter: undefined, module: undefined,
-               moduleId: undefined, testId: undefined } ),
-       defined = {
-               sessionStorage: ( function() {
-                       var x = "qunit-test-string";
-                       try {
-                               sessionStorage.setItem( x, x );
-                               sessionStorage.removeItem( x );
-                               return true;
-                       } catch ( e ) {
-                               return false;
-                       }
-               }() )
-       },
-       modulesList = [];
-
-/**
-* Escape text for attribute or text content.
-*/
-function escapeText( s ) {
-       if ( !s ) {
-               return "";
-       }
-       s = s + "";
-
-       // Both single quotes and double quotes (for attributes)
-       return s.replace( /['"<>&]/g, function( s ) {
-               switch ( s ) {
-               case "'":
-                       return "&#039;";
-               case "\"":
-                       return "&quot;";
-               case "<":
-                       return "&lt;";
-               case ">":
-                       return "&gt;";
-               case "&":
-                       return "&amp;";
-               }
-       } );
-}
-
-/**
- * @param {HTMLElement} elem
- * @param {string} type
- * @param {Function} fn
- */
-function addEvent( elem, type, fn ) {
-       if ( elem.addEventListener ) {
-
-               // Standards-based browsers
-               elem.addEventListener( type, fn, false );
-       } else if ( elem.attachEvent ) {
-
-               // Support: IE <9
-               elem.attachEvent( "on" + type, function() {
-                       var event = window.event;
-                       if ( !event.target ) {
-                               event.target = event.srcElement || document;
-                       }
-
-                       fn.call( elem, event );
-               } );
-       }
-}
-
-/**
- * @param {Array|NodeList} elems
- * @param {string} type
- * @param {Function} fn
- */
-function addEvents( elems, type, fn ) {
-       var i = elems.length;
-       while ( i-- ) {
-               addEvent( elems[ i ], type, fn );
-       }
-}
-
-function hasClass( elem, name ) {
-       return ( " " + elem.className + " " ).indexOf( " " + name + " " ) >= 0;
-}
-
-function addClass( elem, name ) {
-       if ( !hasClass( elem, name ) ) {
-               elem.className += ( elem.className ? " " : "" ) + name;
-       }
-}
-
-function toggleClass( elem, name, force ) {
-       if ( force || typeof force === "undefined" && !hasClass( elem, name ) ) {
-               addClass( elem, name );
-       } else {
-               removeClass( elem, name );
-       }
-}
-
-function removeClass( elem, name ) {
-       var set = " " + elem.className + " ";
-
-       // Class name may appear multiple times
-       while ( set.indexOf( " " + name + " " ) >= 0 ) {
-               set = set.replace( " " + name + " ", " " );
-       }
-
-       // Trim for prettiness
-       elem.className = typeof set.trim === "function" ? set.trim() : set.replace( /^\s+|\s+$/g, "" );
-}
-
-function id( name ) {
-       return document.getElementById && document.getElementById( name );
-}
-
-function getUrlConfigHtml() {
-       var i, j, val,
-               escaped, escapedTooltip,
-               selection = false,
-               urlConfig = config.urlConfig,
-               urlConfigHtml = "";
-
-       for ( i = 0; i < urlConfig.length; i++ ) {
-
-               // Options can be either strings or objects with nonempty "id" properties
-               val = config.urlConfig[ i ];
-               if ( typeof val === "string" ) {
-                       val = {
-                               id: val,
-                               label: val
-                       };
-               }
-
-               escaped = escapeText( val.id );
-               escapedTooltip = escapeText( val.tooltip );
-
-               if ( !val.value || typeof val.value === "string" ) {
-                       urlConfigHtml += "<input id='qunit-urlconfig-" + escaped +
-                               "' name='" + escaped + "' type='checkbox'" +
-                               ( val.value ? " value='" + escapeText( val.value ) + "'" : "" ) +
-                               ( config[ val.id ] ? " checked='checked'" : "" ) +
-                               " title='" + escapedTooltip + "' /><label for='qunit-urlconfig-" + escaped +
-                               "' title='" + escapedTooltip + "'>" + val.label + "</label>";
-               } else {
-                       urlConfigHtml += "<label for='qunit-urlconfig-" + escaped +
-                               "' title='" + escapedTooltip + "'>" + val.label +
-                               ": </label><select id='qunit-urlconfig-" + escaped +
-                               "' name='" + escaped + "' title='" + escapedTooltip + "'><option></option>";
-
-                       if ( QUnit.is( "array", val.value ) ) {
-                               for ( j = 0; j < val.value.length; j++ ) {
-                                       escaped = escapeText( val.value[ j ] );
-                                       urlConfigHtml += "<option value='" + escaped + "'" +
-                                               ( config[ val.id ] === val.value[ j ] ?
-                                                       ( selection = true ) && " selected='selected'" : "" ) +
-                                               ">" + escaped + "</option>";
-                               }
-                       } else {
-                               for ( j in val.value ) {
-                                       if ( hasOwn.call( val.value, j ) ) {
-                                               urlConfigHtml += "<option value='" + escapeText( j ) + "'" +
-                                                       ( config[ val.id ] === j ?
-                                                               ( selection = true ) && " selected='selected'" : "" ) +
-                                                       ">" + escapeText( val.value[ j ] ) + "</option>";
-                                       }
-                               }
-                       }
-                       if ( config[ val.id ] && !selection ) {
-                               escaped = escapeText( config[ val.id ] );
-                               urlConfigHtml += "<option value='" + escaped +
-                                       "' selected='selected' disabled='disabled'>" + escaped + "</option>";
-                       }
-                       urlConfigHtml += "</select>";
-               }
-       }
-
-       return urlConfigHtml;
-}
-
-// Handle "click" events on toolbar checkboxes and "change" for select menus.
-// Updates the URL with the new state of `config.urlConfig` values.
-function toolbarChanged() {
-       var updatedUrl, value, tests,
-               field = this,
-               params = {};
-
-       // Detect if field is a select menu or a checkbox
-       if ( "selectedIndex" in field ) {
-               value = field.options[ field.selectedIndex ].value || undefined;
-       } else {
-               value = field.checked ? ( field.defaultValue || true ) : undefined;
-       }
-
-       params[ field.name ] = value;
-       updatedUrl = setUrl( params );
-
-       // Check if we can apply the change without a page refresh
-       if ( "hidepassed" === field.name && "replaceState" in window.history ) {
-               QUnit.urlParams[ field.name ] = value;
-               config[ field.name ] = value || false;
-               tests = id( "qunit-tests" );
-               if ( tests ) {
-                       toggleClass( tests, "hidepass", value || false );
-               }
-               window.history.replaceState( null, "", updatedUrl );
-       } else {
-               window.location = updatedUrl;
-       }
-}
-
-function setUrl( params ) {
-       var key, arrValue, i,
-               querystring = "?",
-               location = window.location;
-
-       params = QUnit.extend( QUnit.extend( {}, QUnit.urlParams ), params );
-
-       for ( key in params ) {
-
-               // Skip inherited or undefined properties
-               if ( hasOwn.call( params, key ) && params[ key ] !== undefined ) {
-
-                       // Output a parameter for each value of this key (but usually just one)
-                       arrValue = [].concat( params[ key ] );
-                       for ( i = 0; i < arrValue.length; i++ ) {
-                               querystring += encodeURIComponent( key );
-                               if ( arrValue[ i ] !== true ) {
-                                       querystring += "=" + encodeURIComponent( arrValue[ i ] );
-                               }
-                               querystring += "&";
-                       }
-               }
-       }
-       return location.protocol + "//" + location.host +
-               location.pathname + querystring.slice( 0, -1 );
-}
-
-function applyUrlParams() {
-       var selectedModule,
-               modulesList = id( "qunit-modulefilter" ),
-               filter = id( "qunit-filter-input" ).value;
-
-       selectedModule = modulesList ?
-               decodeURIComponent( modulesList.options[ modulesList.selectedIndex ].value ) :
-               undefined;
-
-       window.location = setUrl( {
-               module: ( selectedModule === "" ) ? undefined : selectedModule,
-               filter: ( filter === "" ) ? undefined : filter,
-
-               // Remove moduleId and testId filters
-               moduleId: undefined,
-               testId: undefined
-       } );
-}
-
-function toolbarUrlConfigContainer() {
-       var urlConfigContainer = document.createElement( "span" );
-
-       urlConfigContainer.innerHTML = getUrlConfigHtml();
-       addClass( urlConfigContainer, "qunit-url-config" );
-
-       // For oldIE support:
-       // * Add handlers to the individual elements instead of the container
-       // * Use "click" instead of "change" for checkboxes
-       addEvents( urlConfigContainer.getElementsByTagName( "input" ), "click", toolbarChanged );
-       addEvents( urlConfigContainer.getElementsByTagName( "select" ), "change", toolbarChanged );
-
-       return urlConfigContainer;
-}
-
-function toolbarLooseFilter() {
-       var filter = document.createElement( "form" ),
-               label = document.createElement( "label" ),
-               input = document.createElement( "input" ),
-               button = document.createElement( "button" );
-
-       addClass( filter, "qunit-filter" );
-
-       label.innerHTML = "Filter: ";
-
-       input.type = "text";
-       input.value = config.filter || "";
-       input.name = "filter";
-       input.id = "qunit-filter-input";
-
-       button.innerHTML = "Go";
-
-       label.appendChild( input );
-
-       filter.appendChild( label );
-       filter.appendChild( button );
-       addEvent( filter, "submit", function( ev ) {
-               applyUrlParams();
-
-               if ( ev && ev.preventDefault ) {
-                       ev.preventDefault();
-               }
-
-               return false;
-       } );
-
-       return filter;
-}
-
-function toolbarModuleFilterHtml() {
-       var i,
-               moduleFilterHtml = "";
-
-       if ( !modulesList.length ) {
-               return false;
-       }
-
-       moduleFilterHtml += "<label for='qunit-modulefilter'>Module: </label>" +
-               "<select id='qunit-modulefilter' name='modulefilter'><option value='' " +
-               ( QUnit.urlParams.module === undefined ? "selected='selected'" : "" ) +
-               ">< All Modules ></option>";
-
-       for ( i = 0; i < modulesList.length; i++ ) {
-               moduleFilterHtml += "<option value='" +
-                       escapeText( encodeURIComponent( modulesList[ i ] ) ) + "' " +
-                       ( QUnit.urlParams.module === modulesList[ i ] ? "selected='selected'" : "" ) +
-                       ">" + escapeText( modulesList[ i ] ) + "</option>";
-       }
-       moduleFilterHtml += "</select>";
-
-       return moduleFilterHtml;
-}
-
-function toolbarModuleFilter() {
-       var toolbar = id( "qunit-testrunner-toolbar" ),
-               moduleFilter = document.createElement( "span" ),
-               moduleFilterHtml = toolbarModuleFilterHtml();
-
-       if ( !toolbar || !moduleFilterHtml ) {
-               return false;
-       }
-
-       moduleFilter.setAttribute( "id", "qunit-modulefilter-container" );
-       moduleFilter.innerHTML = moduleFilterHtml;
-
-       addEvent( moduleFilter.lastChild, "change", applyUrlParams );
-
-       toolbar.appendChild( moduleFilter );
-}
-
-function appendToolbar() {
-       var toolbar = id( "qunit-testrunner-toolbar" );
-
-       if ( toolbar ) {
-               toolbar.appendChild( toolbarUrlConfigContainer() );
-               toolbar.appendChild( toolbarLooseFilter() );
-               toolbarModuleFilter();
-       }
-}
-
-function appendHeader() {
-       var header = id( "qunit-header" );
-
-       if ( header ) {
-               header.innerHTML = "<a href='" + escapeText( unfilteredUrl ) + "'>" + header.innerHTML +
-                       "</a> ";
-       }
-}
-
-function appendBanner() {
-       var banner = id( "qunit-banner" );
-
-       if ( banner ) {
-               banner.className = "";
-       }
-}
-
-function appendTestResults() {
-       var tests = id( "qunit-tests" ),
-               result = id( "qunit-testresult" );
-
-       if ( result ) {
-               result.parentNode.removeChild( result );
-       }
-
-       if ( tests ) {
-               tests.innerHTML = "";
-               result = document.createElement( "p" );
-               result.id = "qunit-testresult";
-               result.className = "result";
-               tests.parentNode.insertBefore( result, tests );
-               result.innerHTML = "Running...<br />&#160;";
-       }
-}
-
-function storeFixture() {
-       var fixture = id( "qunit-fixture" );
-       if ( fixture ) {
-               config.fixture = fixture.innerHTML;
-       }
-}
-
-function appendFilteredTest() {
-       var testId = QUnit.config.testId;
-       if ( !testId || testId.length <= 0 ) {
-               return "";
-       }
-       return "<div id='qunit-filteredTest'>Rerunning selected tests: " +
-               escapeText( testId.join( ", " ) ) +
-               " <a id='qunit-clearFilter' href='" +
-               escapeText( unfilteredUrl ) +
-               "'>Run all tests</a></div>";
-}
-
-function appendUserAgent() {
-       var userAgent = id( "qunit-userAgent" );
-
-       if ( userAgent ) {
-               userAgent.innerHTML = "";
-               userAgent.appendChild(
-                       document.createTextNode(
-                               "QUnit " + QUnit.version + "; " + navigator.userAgent
-                       )
-               );
-       }
-}
-
-function appendInterface() {
-       var qunit = id( "qunit" );
-
-       if ( qunit ) {
-               qunit.innerHTML =
-                       "<h1 id='qunit-header'>" + escapeText( document.title ) + "</h1>" +
-                       "<h2 id='qunit-banner'></h2>" +
-                       "<div id='qunit-testrunner-toolbar'></div>" +
-                       appendFilteredTest() +
-                       "<h2 id='qunit-userAgent'></h2>" +
-                       "<ol id='qunit-tests'></ol>";
-       }
-
-       appendHeader();
-       appendBanner();
-       appendTestResults();
-       appendUserAgent();
-       appendToolbar();
-}
-
-function appendTestsList( modules ) {
-       var i, l, x, z, test, moduleObj;
-
-       for ( i = 0, l = modules.length; i < l; i++ ) {
-               moduleObj = modules[ i ];
-
-               for ( x = 0, z = moduleObj.tests.length; x < z; x++ ) {
-                       test = moduleObj.tests[ x ];
-
-                       appendTest( test.name, test.testId, moduleObj.name );
-               }
-       }
-}
-
-function appendTest( name, testId, moduleName ) {
-       var title, rerunTrigger, testBlock, assertList,
-               tests = id( "qunit-tests" );
-
-       if ( !tests ) {
-               return;
-       }
-
-       title = document.createElement( "strong" );
-       title.innerHTML = getNameHtml( name, moduleName );
-
-       rerunTrigger = document.createElement( "a" );
-       rerunTrigger.innerHTML = "Rerun";
-       rerunTrigger.href = setUrl( { testId: testId } );
-
-       testBlock = document.createElement( "li" );
-       testBlock.appendChild( title );
-       testBlock.appendChild( rerunTrigger );
-       testBlock.id = "qunit-test-output-" + testId;
-
-       assertList = document.createElement( "ol" );
-       assertList.className = "qunit-assert-list";
-
-       testBlock.appendChild( assertList );
-
-       tests.appendChild( testBlock );
-}
-
-// HTML Reporter initialization and load
-QUnit.begin( function( details ) {
-       var i, moduleObj, tests;
-
-       // Sort modules by name for the picker
-       for ( i = 0; i < details.modules.length; i++ ) {
-               moduleObj = details.modules[ i ];
-               if ( moduleObj.name ) {
-                       modulesList.push( moduleObj.name );
-               }
-       }
-       modulesList.sort( function( a, b ) {
-               return a.localeCompare( b );
-       } );
-
-       // Capture fixture HTML from the page
-       storeFixture();
-
-       // Initialize QUnit elements
-       appendInterface();
-       appendTestsList( details.modules );
-       tests = id( "qunit-tests" );
-       if ( tests && config.hidepassed ) {
-               addClass( tests, "hidepass" );
-       }
-} );
-
-QUnit.done( function( details ) {
-       var i, key,
-               banner = id( "qunit-banner" ),
-               tests = id( "qunit-tests" ),
-               html = [
-                       "Tests completed in ",
-                       details.runtime,
-                       " milliseconds.<br />",
-                       "<span class='passed'>",
-                       details.passed,
-                       "</span> assertions of <span class='total'>",
-                       details.total,
-                       "</span> passed, <span class='failed'>",
-                       details.failed,
-                       "</span> failed."
-               ].join( "" );
-
-       if ( banner ) {
-               banner.className = details.failed ? "qunit-fail" : "qunit-pass";
-       }
-
-       if ( tests ) {
-               id( "qunit-testresult" ).innerHTML = html;
-       }
-
-       if ( config.altertitle && document.title ) {
-
-               // Show ✖ for good, ✔ for bad suite result in title
-               // use escape sequences in case file gets loaded with non-utf-8-charset
-               document.title = [
-                       ( details.failed ? "\u2716" : "\u2714" ),
-                       document.title.replace( /^[\u2714\u2716] /i, "" )
-               ].join( " " );
-       }
-
-       // Clear own sessionStorage items if all tests passed
-       if ( config.reorder && defined.sessionStorage && details.failed === 0 ) {
-               for ( i = 0; i < sessionStorage.length; i++ ) {
-                       key = sessionStorage.key( i++ );
-                       if ( key.indexOf( "qunit-test-" ) === 0 ) {
-                               sessionStorage.removeItem( key );
-                       }
-               }
-       }
-
-       // Scroll back to top to show results
-       if ( config.scrolltop && window.scrollTo ) {
-               window.scrollTo( 0, 0 );
-       }
-} );
-
-function getNameHtml( name, module ) {
-       var nameHtml = "";
-
-       if ( module ) {
-               nameHtml = "<span class='module-name'>" + escapeText( module ) + "</span>: ";
-       }
-
-       nameHtml += "<span class='test-name'>" + escapeText( name ) + "</span>";
-
-       return nameHtml;
-}
-
-QUnit.testStart( function( details ) {
-       var running, testBlock, bad;
-
-       testBlock = id( "qunit-test-output-" + details.testId );
-       if ( testBlock ) {
-               testBlock.className = "running";
-       } else {
-
-               // Report later registered tests
-               appendTest( details.name, details.testId, details.module );
-       }
-
-       running = id( "qunit-testresult" );
-       if ( running ) {
-               bad = QUnit.config.reorder && defined.sessionStorage &&
-                       +sessionStorage.getItem( "qunit-test-" + details.module + "-" + details.name );
-
-               running.innerHTML = ( bad ?
-                       "Rerunning previously failed test: <br />" :
-                       "Running: <br />" ) +
-                       getNameHtml( details.name, details.module );
-       }
-
-} );
-
-function stripHtml( string ) {
-
-       // Strip tags, html entity and whitespaces
-       return string.replace( /<\/?[^>]+(>|$)/g, "" ).replace( /\&quot;/g, "" ).replace( /\s+/g, "" );
-}
-
-QUnit.log( function( details ) {
-       var assertList, assertLi,
-               message, expected, actual, diff,
-               showDiff = false,
-               testItem = id( "qunit-test-output-" + details.testId );
-
-       if ( !testItem ) {
-               return;
-       }
-
-       message = escapeText( details.message ) || ( details.result ? "okay" : "failed" );
-       message = "<span class='test-message'>" + message + "</span>";
-       message += "<span class='runtime'>@ " + details.runtime + " ms</span>";
-
-       // The pushFailure doesn't provide details.expected
-       // when it calls, it's implicit to also not show expected and diff stuff
-       // Also, we need to check details.expected existence, as it can exist and be undefined
-       if ( !details.result && hasOwn.call( details, "expected" ) ) {
-               if ( details.negative ) {
-                       expected = "NOT " + QUnit.dump.parse( details.expected );
-               } else {
-                       expected = QUnit.dump.parse( details.expected );
-               }
-
-               actual = QUnit.dump.parse( details.actual );
-               message += "<table><tr class='test-expected'><th>Expected: </th><td><pre>" +
-                       escapeText( expected ) +
-                       "</pre></td></tr>";
-
-               if ( actual !== expected ) {
-
-                       message += "<tr class='test-actual'><th>Result: </th><td><pre>" +
-                               escapeText( actual ) + "</pre></td></tr>";
-
-                       // Don't show diff if actual or expected are booleans
-                       if ( !( /^(true|false)$/.test( actual ) ) &&
-                                       !( /^(true|false)$/.test( expected ) ) ) {
-                               diff = QUnit.diff( expected, actual );
-                               showDiff = stripHtml( diff ).length !==
-                                       stripHtml( expected ).length +
-                                       stripHtml( actual ).length;
-                       }
-
-                       // Don't show diff if expected and actual are totally different
-                       if ( showDiff ) {
-                               message += "<tr class='test-diff'><th>Diff: </th><td><pre>" +
-                                       diff + "</pre></td></tr>";
-                       }
-               } else if ( expected.indexOf( "[object Array]" ) !== -1 ||
-                               expected.indexOf( "[object Object]" ) !== -1 ) {
-                       message += "<tr class='test-message'><th>Message: </th><td>" +
-                               "Diff suppressed as the depth of object is more than current max depth (" +
-                               QUnit.config.maxDepth + ").<p>Hint: Use <code>QUnit.dump.maxDepth</code> to " +
-                               " run with a higher max depth or <a href='" +
-                               escapeText( setUrl( { maxDepth: -1 } ) ) + "'>" +
-                               "Rerun</a> without max depth.</p></td></tr>";
-               } else {
-                       message += "<tr class='test-message'><th>Message: </th><td>" +
-                               "Diff suppressed as the expected and actual results have an equivalent" +
-                               " serialization</td></tr>";
-               }
-
-               if ( details.source ) {
-                       message += "<tr class='test-source'><th>Source: </th><td><pre>" +
-                               escapeText( details.source ) + "</pre></td></tr>";
-               }
-
-               message += "</table>";
-
-       // This occurs when pushFailure is set and we have an extracted stack trace
-       } else if ( !details.result && details.source ) {
-               message += "<table>" +
-                       "<tr class='test-source'><th>Source: </th><td><pre>" +
-                       escapeText( details.source ) + "</pre></td></tr>" +
-                       "</table>";
-       }
-
-       assertList = testItem.getElementsByTagName( "ol" )[ 0 ];
-
-       assertLi = document.createElement( "li" );
-       assertLi.className = details.result ? "pass" : "fail";
-       assertLi.innerHTML = message;
-       assertList.appendChild( assertLi );
-} );
-
-QUnit.testDone( function( details ) {
-       var testTitle, time, testItem, assertList,
-               good, bad, testCounts, skipped, sourceName,
-               tests = id( "qunit-tests" );
-
-       if ( !tests ) {
-               return;
-       }
-
-       testItem = id( "qunit-test-output-" + details.testId );
-
-       assertList = testItem.getElementsByTagName( "ol" )[ 0 ];
-
-       good = details.passed;
-       bad = details.failed;
-
-       // Store result when possible
-       if ( config.reorder && defined.sessionStorage ) {
-               if ( bad ) {
-                       sessionStorage.setItem( "qunit-test-" + details.module + "-" + details.name, bad );
-               } else {
-                       sessionStorage.removeItem( "qunit-test-" + details.module + "-" + details.name );
-               }
-       }
-
-       if ( bad === 0 ) {
-
-               // Collapse the passing tests
-               addClass( assertList, "qunit-collapsed" );
-       } else if ( bad && config.collapse && !collapseNext ) {
-
-               // Skip collapsing the first failing test
-               collapseNext = true;
-       } else {
-
-               // Collapse remaining tests
-               addClass( assertList, "qunit-collapsed" );
-       }
-
-       // The testItem.firstChild is the test name
-       testTitle = testItem.firstChild;
-
-       testCounts = bad ?
-               "<b class='failed'>" + bad + "</b>, " + "<b class='passed'>" + good + "</b>, " :
-               "";
-
-       testTitle.innerHTML += " <b class='counts'>(" + testCounts +
-               details.assertions.length + ")</b>";
-
-       if ( details.skipped ) {
-               testItem.className = "skipped";
-               skipped = document.createElement( "em" );
-               skipped.className = "qunit-skipped-label";
-               skipped.innerHTML = "skipped";
-               testItem.insertBefore( skipped, testTitle );
-       } else {
-               addEvent( testTitle, "click", function() {
-                       toggleClass( assertList, "qunit-collapsed" );
-               } );
-
-               testItem.className = bad ? "fail" : "pass";
-
-               time = document.createElement( "span" );
-               time.className = "runtime";
-               time.innerHTML = details.runtime + " ms";
-               testItem.insertBefore( time, assertList );
-       }
-
-       // Show the source of the test when showing assertions
-       if ( details.source ) {
-               sourceName = document.createElement( "p" );
-               sourceName.innerHTML = "<strong>Source: </strong>" + details.source;
-               addClass( sourceName, "qunit-source" );
-               if ( bad === 0 ) {
-                       addClass( sourceName, "qunit-collapsed" );
-               }
-               addEvent( testTitle, "click", function() {
-                       toggleClass( sourceName, "qunit-collapsed" );
-               } );
-               testItem.appendChild( sourceName );
-       }
-} );
-
-// Avoid readyState issue with phantomjs
-// Ref: #818
-var notPhantom = ( function( p ) {
-       return !( p && p.version && p.version.major > 0 );
-} )( window.phantom );
-
-if ( notPhantom && document.readyState === "complete" ) {
-       QUnit.load();
-} else {
-       addEvent( window, "load", QUnit.load );
-}
-
-/*
- * This file is a modified version of google-diff-match-patch's JavaScript implementation
- * (https://code.google.com/p/google-diff-match-patch/source/browse/trunk/javascript/diff_match_patch_uncompressed.js),
- * modifications are licensed as more fully set forth in LICENSE.txt.
- *
- * The original source of google-diff-match-patch is attributable and licensed as follows:
- *
- * Copyright 2006 Google Inc.
- * https://code.google.com/p/google-diff-match-patch/
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * More Info:
- *  https://code.google.com/p/google-diff-match-patch/
- *
- * Usage: QUnit.diff(expected, actual)
- *
- */
-QUnit.diff = ( function() {
-       function DiffMatchPatch() {
-       }
-
-       //  DIFF FUNCTIONS
-
-       /**
-        * The data structure representing a diff is an array of tuples:
-        * [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']]
-        * which means: delete 'Hello', add 'Goodbye' and keep ' world.'
-        */
-       var DIFF_DELETE = -1,
-               DIFF_INSERT = 1,
-               DIFF_EQUAL = 0;
-
-       /**
-        * Find the differences between two texts.  Simplifies the problem by stripping
-        * any common prefix or suffix off the texts before diffing.
-        * @param {string} text1 Old string to be diffed.
-        * @param {string} text2 New string to be diffed.
-        * @param {boolean=} optChecklines Optional speedup flag. If present and false,
-        *     then don't run a line-level diff first to identify the changed areas.
-        *     Defaults to true, which does a faster, slightly less optimal diff.
-        * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
-        */
-       DiffMatchPatch.prototype.DiffMain = function( text1, text2, optChecklines ) {
-               var deadline, checklines, commonlength,
-                       commonprefix, commonsuffix, diffs;
-
-               // The diff must be complete in up to 1 second.
-               deadline = ( new Date() ).getTime() + 1000;
-
-               // Check for null inputs.
-               if ( text1 === null || text2 === null ) {
-                       throw new Error( "Null input. (DiffMain)" );
-               }
-
-               // Check for equality (speedup).
-               if ( text1 === text2 ) {
-                       if ( text1 ) {
-                               return [
-                                       [ DIFF_EQUAL, text1 ]
-                               ];
-                       }
-                       return [];
-               }
-
-               if ( typeof optChecklines === "undefined" ) {
-                       optChecklines = true;
-               }
-
-               checklines = optChecklines;
-
-               // Trim off common prefix (speedup).
-               commonlength = this.diffCommonPrefix( text1, text2 );
-               commonprefix = text1.substring( 0, commonlength );
-               text1 = text1.substring( commonlength );
-               text2 = text2.substring( commonlength );
-
-               // Trim off common suffix (speedup).
-               commonlength = this.diffCommonSuffix( text1, text2 );
-               commonsuffix = text1.substring( text1.length - commonlength );
-               text1 = text1.substring( 0, text1.length - commonlength );
-               text2 = text2.substring( 0, text2.length - commonlength );
-
-               // Compute the diff on the middle block.
-               diffs = this.diffCompute( text1, text2, checklines, deadline );
-
-               // Restore the prefix and suffix.
-               if ( commonprefix ) {
-                       diffs.unshift( [ DIFF_EQUAL, commonprefix ] );
-               }
-               if ( commonsuffix ) {
-                       diffs.push( [ DIFF_EQUAL, commonsuffix ] );
-               }
-               this.diffCleanupMerge( diffs );
-               return diffs;
-       };
-
-       /**
-        * Reduce the number of edits by eliminating operationally trivial equalities.
-        * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
-        */
-       DiffMatchPatch.prototype.diffCleanupEfficiency = function( diffs ) {
-               var changes, equalities, equalitiesLength, lastequality,
-                       pointer, preIns, preDel, postIns, postDel;
-               changes = false;
-               equalities = []; // Stack of indices where equalities are found.
-               equalitiesLength = 0; // Keeping our own length var is faster in JS.
-               /** @type {?string} */
-               lastequality = null;
-
-               // Always equal to diffs[equalities[equalitiesLength - 1]][1]
-               pointer = 0; // Index of current position.
-
-               // Is there an insertion operation before the last equality.
-               preIns = false;
-
-               // Is there a deletion operation before the last equality.
-               preDel = false;
-
-               // Is there an insertion operation after the last equality.
-               postIns = false;
-
-               // Is there a deletion operation after the last equality.
-               postDel = false;
-               while ( pointer < diffs.length ) {
-
-                       // Equality found.
-                       if ( diffs[ pointer ][ 0 ] === DIFF_EQUAL ) {
-                               if ( diffs[ pointer ][ 1 ].length < 4 && ( postIns || postDel ) ) {
-
-                                       // Candidate found.
-                                       equalities[ equalitiesLength++ ] = pointer;
-                                       preIns = postIns;
-                                       preDel = postDel;
-                                       lastequality = diffs[ pointer ][ 1 ];
-                               } else {
-
-                                       // Not a candidate, and can never become one.
-                                       equalitiesLength = 0;
-                                       lastequality = null;
-                               }
-                               postIns = postDel = false;
-
-                       // An insertion or deletion.
-                       } else {
-
-                               if ( diffs[ pointer ][ 0 ] === DIFF_DELETE ) {
-                                       postDel = true;
-                               } else {
-                                       postIns = true;
-                               }
-
-                               /*
-                                * Five types to be split:
-                                * <ins>A</ins><del>B</del>XY<ins>C</ins><del>D</del>
-                                * <ins>A</ins>X<ins>C</ins><del>D</del>
-                                * <ins>A</ins><del>B</del>X<ins>C</ins>
-                                * <ins>A</del>X<ins>C</ins><del>D</del>
-                                * <ins>A</ins><del>B</del>X<del>C</del>
-                                */
-                               if ( lastequality && ( ( preIns && preDel && postIns && postDel ) ||
-                                               ( ( lastequality.length < 2 ) &&
-                                               ( preIns + preDel + postIns + postDel ) === 3 ) ) ) {
-
-                                       // Duplicate record.
-                                       diffs.splice(
-                                               equalities[ equalitiesLength - 1 ],
-                                               0,
-                                               [ DIFF_DELETE, lastequality ]
-                                       );
-
-                                       // Change second copy to insert.
-                                       diffs[ equalities[ equalitiesLength - 1 ] + 1 ][ 0 ] = DIFF_INSERT;
-                                       equalitiesLength--; // Throw away the equality we just deleted;
-                                       lastequality = null;
-                                       if ( preIns && preDel ) {
-
-                                               // No changes made which could affect previous entry, keep going.
-                                               postIns = postDel = true;
-                                               equalitiesLength = 0;
-                                       } else {
-                                               equalitiesLength--; // Throw away the previous equality.
-                                               pointer = equalitiesLength > 0 ? equalities[ equalitiesLength - 1 ] : -1;
-                                               postIns = postDel = false;
-                                       }
-                                       changes = true;
-                               }
-                       }
-                       pointer++;
-               }
-
-               if ( changes ) {
-                       this.diffCleanupMerge( diffs );
-               }
-       };
-
-       /**
-        * Convert a diff array into a pretty HTML report.
-        * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
-        * @param {integer} string to be beautified.
-        * @return {string} HTML representation.
-        */
-       DiffMatchPatch.prototype.diffPrettyHtml = function( diffs ) {
-               var op, data, x,
-                       html = [];
-               for ( x = 0; x < diffs.length; x++ ) {
-                       op = diffs[ x ][ 0 ]; // Operation (insert, delete, equal)
-                       data = diffs[ x ][ 1 ]; // Text of change.
-                       switch ( op ) {
-                       case DIFF_INSERT:
-                               html[ x ] = "<ins>" + escapeText( data ) + "</ins>";
-                               break;
-                       case DIFF_DELETE:
-                               html[ x ] = "<del>" + escapeText( data ) + "</del>";
-                               break;
-                       case DIFF_EQUAL:
-                               html[ x ] = "<span>" + escapeText( data ) + "</span>";
-                               break;
-                       }
-               }
-               return html.join( "" );
-       };
-
-       /**
-        * Determine the common prefix of two strings.
-        * @param {string} text1 First string.
-        * @param {string} text2 Second string.
-        * @return {number} The number of characters common to the start of each
-        *     string.
-        */
-       DiffMatchPatch.prototype.diffCommonPrefix = function( text1, text2 ) {
-               var pointermid, pointermax, pointermin, pointerstart;
-
-               // Quick check for common null cases.
-               if ( !text1 || !text2 || text1.charAt( 0 ) !== text2.charAt( 0 ) ) {
-                       return 0;
-               }
-
-               // Binary search.
-               // Performance analysis: https://neil.fraser.name/news/2007/10/09/
-               pointermin = 0;
-               pointermax = Math.min( text1.length, text2.length );
-               pointermid = pointermax;
-               pointerstart = 0;
-               while ( pointermin < pointermid ) {
-                       if ( text1.substring( pointerstart, pointermid ) ===
-                                       text2.substring( pointerstart, pointermid ) ) {
-                               pointermin = pointermid;
-                               pointerstart = pointermin;
-                       } else {
-                               pointermax = pointermid;
-                       }
-                       pointermid = Math.floor( ( pointermax - pointermin ) / 2 + pointermin );
-               }
-               return pointermid;
-       };
-
-       /**
-        * Determine the common suffix of two strings.
-        * @param {string} text1 First string.
-        * @param {string} text2 Second string.
-        * @return {number} The number of characters common to the end of each string.
-        */
-       DiffMatchPatch.prototype.diffCommonSuffix = function( text1, text2 ) {
-               var pointermid, pointermax, pointermin, pointerend;
-
-               // Quick check for common null cases.
-               if ( !text1 ||
-                               !text2 ||
-                               text1.charAt( text1.length - 1 ) !== text2.charAt( text2.length - 1 ) ) {
-                       return 0;
-               }
-
-               // Binary search.
-               // Performance analysis: https://neil.fraser.name/news/2007/10/09/
-               pointermin = 0;
-               pointermax = Math.min( text1.length, text2.length );
-               pointermid = pointermax;
-               pointerend = 0;
-               while ( pointermin < pointermid ) {
-                       if ( text1.substring( text1.length - pointermid, text1.length - pointerend ) ===
-                                       text2.substring( text2.length - pointermid, text2.length - pointerend ) ) {
-                               pointermin = pointermid;
-                               pointerend = pointermin;
-                       } else {
-                               pointermax = pointermid;
-                       }
-                       pointermid = Math.floor( ( pointermax - pointermin ) / 2 + pointermin );
-               }
-               return pointermid;
-       };
-
-       /**
-        * Find the differences between two texts.  Assumes that the texts do not
-        * have any common prefix or suffix.
-        * @param {string} text1 Old string to be diffed.
-        * @param {string} text2 New string to be diffed.
-        * @param {boolean} checklines Speedup flag.  If false, then don't run a
-        *     line-level diff first to identify the changed areas.
-        *     If true, then run a faster, slightly less optimal diff.
-        * @param {number} deadline Time when the diff should be complete by.
-        * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
-        * @private
-        */
-       DiffMatchPatch.prototype.diffCompute = function( text1, text2, checklines, deadline ) {
-               var diffs, longtext, shorttext, i, hm,
-                       text1A, text2A, text1B, text2B,
-                       midCommon, diffsA, diffsB;
-
-               if ( !text1 ) {
-
-                       // Just add some text (speedup).
-                       return [
-                               [ DIFF_INSERT, text2 ]
-                       ];
-               }
-
-               if ( !text2 ) {
-
-                       // Just delete some text (speedup).
-                       return [
-                               [ DIFF_DELETE, text1 ]
-                       ];
-               }
-
-               longtext = text1.length > text2.length ? text1 : text2;
-               shorttext = text1.length > text2.length ? text2 : text1;
-               i = longtext.indexOf( shorttext );
-               if ( i !== -1 ) {
-
-                       // Shorter text is inside the longer text (speedup).
-                       diffs = [
-                               [ DIFF_INSERT, longtext.substring( 0, i ) ],
-                               [ DIFF_EQUAL, shorttext ],
-                               [ DIFF_INSERT, longtext.substring( i + shorttext.length ) ]
-                       ];
-
-                       // Swap insertions for deletions if diff is reversed.
-                       if ( text1.length > text2.length ) {
-                               diffs[ 0 ][ 0 ] = diffs[ 2 ][ 0 ] = DIFF_DELETE;
-                       }
-                       return diffs;
-               }
-
-               if ( shorttext.length === 1 ) {
-
-                       // Single character string.
-                       // After the previous speedup, the character can't be an equality.
-                       return [
-                               [ DIFF_DELETE, text1 ],
-                               [ DIFF_INSERT, text2 ]
-                       ];
-               }
-
-               // Check to see if the problem can be split in two.
-               hm = this.diffHalfMatch( text1, text2 );
-               if ( hm ) {
-
-                       // A half-match was found, sort out the return data.
-                       text1A = hm[ 0 ];
-                       text1B = hm[ 1 ];
-                       text2A = hm[ 2 ];
-                       text2B = hm[ 3 ];
-                       midCommon = hm[ 4 ];
-
-                       // Send both pairs off for separate processing.
-                       diffsA = this.DiffMain( text1A, text2A, checklines, deadline );
-                       diffsB = this.DiffMain( text1B, text2B, checklines, deadline );
-
-                       // Merge the results.
-                       return diffsA.concat( [
-                               [ DIFF_EQUAL, midCommon ]
-                       ], diffsB );
-               }
-
-               if ( checklines && text1.length > 100 && text2.length > 100 ) {
-                       return this.diffLineMode( text1, text2, deadline );
-               }
-
-               return this.diffBisect( text1, text2, deadline );
-       };
-
-       /**
-        * Do the two texts share a substring which is at least half the length of the
-        * longer text?
-        * This speedup can produce non-minimal diffs.
-        * @param {string} text1 First string.
-        * @param {string} text2 Second string.
-        * @return {Array.<string>} Five element Array, containing the prefix of
-        *     text1, the suffix of text1, the prefix of text2, the suffix of
-        *     text2 and the common middle.  Or null if there was no match.
-        * @private
-        */
-       DiffMatchPatch.prototype.diffHalfMatch = function( text1, text2 ) {
-               var longtext, shorttext, dmp,
-                       text1A, text2B, text2A, text1B, midCommon,
-                       hm1, hm2, hm;
-
-               longtext = text1.length > text2.length ? text1 : text2;
-               shorttext = text1.length > text2.length ? text2 : text1;
-               if ( longtext.length < 4 || shorttext.length * 2 < longtext.length ) {
-                       return null; // Pointless.
-               }
-               dmp = this; // 'this' becomes 'window' in a closure.
-
-               /**
-                * Does a substring of shorttext exist within longtext such that the substring
-                * is at least half the length of longtext?
-                * Closure, but does not reference any external variables.
-                * @param {string} longtext Longer string.
-                * @param {string} shorttext Shorter string.
-                * @param {number} i Start index of quarter length substring within longtext.
-                * @return {Array.<string>} Five element Array, containing the prefix of
-                *     longtext, the suffix of longtext, the prefix of shorttext, the suffix
-                *     of shorttext and the common middle.  Or null if there was no match.
-                * @private
-                */
-               function diffHalfMatchI( longtext, shorttext, i ) {
-                       var seed, j, bestCommon, prefixLength, suffixLength,
-                               bestLongtextA, bestLongtextB, bestShorttextA, bestShorttextB;
-
-                       // Start with a 1/4 length substring at position i as a seed.
-                       seed = longtext.substring( i, i + Math.floor( longtext.length / 4 ) );
-                       j = -1;
-                       bestCommon = "";
-                       while ( ( j = shorttext.indexOf( seed, j + 1 ) ) !== -1 ) {
-                               prefixLength = dmp.diffCommonPrefix( longtext.substring( i ),
-                                       shorttext.substring( j ) );
-                               suffixLength = dmp.diffCommonSuffix( longtext.substring( 0, i ),
-                                       shorttext.substring( 0, j ) );
-                               if ( bestCommon.length < suffixLength + prefixLength ) {
-                                       bestCommon = shorttext.substring( j - suffixLength, j ) +
-                                               shorttext.substring( j, j + prefixLength );
-                                       bestLongtextA = longtext.substring( 0, i - suffixLength );
-                                       bestLongtextB = longtext.substring( i + prefixLength );
-                                       bestShorttextA = shorttext.substring( 0, j - suffixLength );
-                                       bestShorttextB = shorttext.substring( j + prefixLength );
-                               }
-                       }
-                       if ( bestCommon.length * 2 >= longtext.length ) {
-                               return [ bestLongtextA, bestLongtextB,
-                                       bestShorttextA, bestShorttextB, bestCommon
-                               ];
-                       } else {
-                               return null;
-                       }
-               }
-
-               // First check if the second quarter is the seed for a half-match.
-               hm1 = diffHalfMatchI( longtext, shorttext,
-                       Math.ceil( longtext.length / 4 ) );
-
-               // Check again based on the third quarter.
-               hm2 = diffHalfMatchI( longtext, shorttext,
-                       Math.ceil( longtext.length / 2 ) );
-               if ( !hm1 && !hm2 ) {
-                       return null;
-               } else if ( !hm2 ) {
-                       hm = hm1;
-               } else if ( !hm1 ) {
-                       hm = hm2;
-               } else {
-
-                       // Both matched.  Select the longest.
-                       hm = hm1[ 4 ].length > hm2[ 4 ].length ? hm1 : hm2;
-               }
-
-               // A half-match was found, sort out the return data.
-               text1A, text1B, text2A, text2B;
-               if ( text1.length > text2.length ) {
-                       text1A = hm[ 0 ];
-                       text1B = hm[ 1 ];
-                       text2A = hm[ 2 ];
-                       text2B = hm[ 3 ];
-               } else {
-                       text2A = hm[ 0 ];
-                       text2B = hm[ 1 ];
-                       text1A = hm[ 2 ];
-                       text1B = hm[ 3 ];
-               }
-               midCommon = hm[ 4 ];
-               return [ text1A, text1B, text2A, text2B, midCommon ];
-       };
-
-       /**
-        * Do a quick line-level diff on both strings, then rediff the parts for
-        * greater accuracy.
-        * This speedup can produce non-minimal diffs.
-        * @param {string} text1 Old string to be diffed.
-        * @param {string} text2 New string to be diffed.
-        * @param {number} deadline Time when the diff should be complete by.
-        * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
-        * @private
-        */
-       DiffMatchPatch.prototype.diffLineMode = function( text1, text2, deadline ) {
-               var a, diffs, linearray, pointer, countInsert,
-                       countDelete, textInsert, textDelete, j;
-
-               // Scan the text on a line-by-line basis first.
-               a = this.diffLinesToChars( text1, text2 );
-               text1 = a.chars1;
-               text2 = a.chars2;
-               linearray = a.lineArray;
-
-               diffs = this.DiffMain( text1, text2, false, deadline );
-
-               // Convert the diff back to original text.
-               this.diffCharsToLines( diffs, linearray );
-
-               // Eliminate freak matches (e.g. blank lines)
-               this.diffCleanupSemantic( diffs );
-
-               // Rediff any replacement blocks, this time character-by-character.
-               // Add a dummy entry at the end.
-               diffs.push( [ DIFF_EQUAL, "" ] );
-               pointer = 0;
-               countDelete = 0;
-               countInsert = 0;
-               textDelete = "";
-               textInsert = "";
-               while ( pointer < diffs.length ) {
-                       switch ( diffs[ pointer ][ 0 ] ) {
-                       case DIFF_INSERT:
-                               countInsert++;
-                               textInsert += diffs[ pointer ][ 1 ];
-                               break;
-                       case DIFF_DELETE:
-                               countDelete++;
-                               textDelete += diffs[ pointer ][ 1 ];
-                               break;
-                       case DIFF_EQUAL:
-
-                               // Upon reaching an equality, check for prior redundancies.
-                               if ( countDelete >= 1 && countInsert >= 1 ) {
-
-                                       // Delete the offending records and add the merged ones.
-                                       diffs.splice( pointer - countDelete - countInsert,
-                                               countDelete + countInsert );
-                                       pointer = pointer - countDelete - countInsert;
-                                       a = this.DiffMain( textDelete, textInsert, false, deadline );
-                                       for ( j = a.length - 1; j >= 0; j-- ) {
-                                               diffs.splice( pointer, 0, a[ j ] );
-                                       }
-                                       pointer = pointer + a.length;
-                               }
-                               countInsert = 0;
-                               countDelete = 0;
-                               textDelete = "";
-                               textInsert = "";
-                               break;
-                       }
-                       pointer++;
-               }
-               diffs.pop(); // Remove the dummy entry at the end.
-
-               return diffs;
-       };
-
-       /**
-        * Find the 'middle snake' of a diff, split the problem in two
-        * and return the recursively constructed diff.
-        * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations.
-        * @param {string} text1 Old string to be diffed.
-        * @param {string} text2 New string to be diffed.
-        * @param {number} deadline Time at which to bail if not yet complete.
-        * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
-        * @private
-        */
-       DiffMatchPatch.prototype.diffBisect = function( text1, text2, deadline ) {
-               var text1Length, text2Length, maxD, vOffset, vLength,
-                       v1, v2, x, delta, front, k1start, k1end, k2start,
-                       k2end, k2Offset, k1Offset, x1, x2, y1, y2, d, k1, k2;
-
-               // Cache the text lengths to prevent multiple calls.
-               text1Length = text1.length;
-               text2Length = text2.length;
-               maxD = Math.ceil( ( text1Length + text2Length ) / 2 );
-               vOffset = maxD;
-               vLength = 2 * maxD;
-               v1 = new Array( vLength );
-               v2 = new Array( vLength );
-
-               // Setting all elements to -1 is faster in Chrome & Firefox than mixing
-               // integers and undefined.
-               for ( x = 0; x < vLength; x++ ) {
-                       v1[ x ] = -1;
-                       v2[ x ] = -1;
-               }
-               v1[ vOffset + 1 ] = 0;
-               v2[ vOffset + 1 ] = 0;
-               delta = text1Length - text2Length;
-
-               // If the total number of characters is odd, then the front path will collide
-               // with the reverse path.
-               front = ( delta % 2 !== 0 );
-
-               // Offsets for start and end of k loop.
-               // Prevents mapping of space beyond the grid.
-               k1start = 0;
-               k1end = 0;
-               k2start = 0;
-               k2end = 0;
-               for ( d = 0; d < maxD; d++ ) {
-
-                       // Bail out if deadline is reached.
-                       if ( ( new Date() ).getTime() > deadline ) {
-                               break;
-                       }
-
-                       // Walk the front path one step.
-                       for ( k1 = -d + k1start; k1 <= d - k1end; k1 += 2 ) {
-                               k1Offset = vOffset + k1;
-                               if ( k1 === -d || ( k1 !== d && v1[ k1Offset - 1 ] < v1[ k1Offset + 1 ] ) ) {
-                                       x1 = v1[ k1Offset + 1 ];
-                               } else {
-                                       x1 = v1[ k1Offset - 1 ] + 1;
-                               }
-                               y1 = x1 - k1;
-                               while ( x1 < text1Length && y1 < text2Length &&
-                                       text1.charAt( x1 ) === text2.charAt( y1 ) ) {
-                                       x1++;
-                                       y1++;
-                               }
-                               v1[ k1Offset ] = x1;
-                               if ( x1 > text1Length ) {
-
-                                       // Ran off the right of the graph.
-                                       k1end += 2;
-                               } else if ( y1 > text2Length ) {
-
-                                       // Ran off the bottom of the graph.
-                                       k1start += 2;
-                               } else if ( front ) {
-                                       k2Offset = vOffset + delta - k1;
-                                       if ( k2Offset >= 0 && k2Offset < vLength && v2[ k2Offset ] !== -1 ) {
-
-                                               // Mirror x2 onto top-left coordinate system.
-                                               x2 = text1Length - v2[ k2Offset ];
-                                               if ( x1 >= x2 ) {
-
-                                                       // Overlap detected.
-                                                       return this.diffBisectSplit( text1, text2, x1, y1, deadline );
-                                               }
-                                       }
-                               }
-                       }
-
-                       // Walk the reverse path one step.
-                       for ( k2 = -d + k2start; k2 <= d - k2end; k2 += 2 ) {
-                               k2Offset = vOffset + k2;
-                               if ( k2 === -d || ( k2 !== d && v2[ k2Offset - 1 ] < v2[ k2Offset + 1 ] ) ) {
-                                       x2 = v2[ k2Offset + 1 ];
-                               } else {
-                                       x2 = v2[ k2Offset - 1 ] + 1;
-                               }
-                               y2 = x2 - k2;
-                               while ( x2 < text1Length && y2 < text2Length &&
-                                       text1.charAt( text1Length - x2 - 1 ) ===
-                                       text2.charAt( text2Length - y2 - 1 ) ) {
-                                       x2++;
-                                       y2++;
-                               }
-                               v2[ k2Offset ] = x2;
-                               if ( x2 > text1Length ) {
-
-                                       // Ran off the left of the graph.
-                                       k2end += 2;
-                               } else if ( y2 > text2Length ) {
-
-                                       // Ran off the top of the graph.
-                                       k2start += 2;
-                               } else if ( !front ) {
-                                       k1Offset = vOffset + delta - k2;
-                                       if ( k1Offset >= 0 && k1Offset < vLength && v1[ k1Offset ] !== -1 ) {
-                                               x1 = v1[ k1Offset ];
-                                               y1 = vOffset + x1 - k1Offset;
-
-                                               // Mirror x2 onto top-left coordinate system.
-                                               x2 = text1Length - x2;
-                                               if ( x1 >= x2 ) {
-
-                                                       // Overlap detected.
-                                                       return this.diffBisectSplit( text1, text2, x1, y1, deadline );
-                                               }
-                                       }
-                               }
-                       }
-               }
-
-               // Diff took too long and hit the deadline or
-               // number of diffs equals number of characters, no commonality at all.
-               return [
-                       [ DIFF_DELETE, text1 ],
-                       [ DIFF_INSERT, text2 ]
-               ];
-       };
-
-       /**
-        * Given the location of the 'middle snake', split the diff in two parts
-        * and recurse.
-        * @param {string} text1 Old string to be diffed.
-        * @param {string} text2 New string to be diffed.
-        * @param {number} x Index of split point in text1.
-        * @param {number} y Index of split point in text2.
-        * @param {number} deadline Time at which to bail if not yet complete.
-        * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
-        * @private
-        */
-       DiffMatchPatch.prototype.diffBisectSplit = function( text1, text2, x, y, deadline ) {
-               var text1a, text1b, text2a, text2b, diffs, diffsb;
-               text1a = text1.substring( 0, x );
-               text2a = text2.substring( 0, y );
-               text1b = text1.substring( x );
-               text2b = text2.substring( y );
-
-               // Compute both diffs serially.
-               diffs = this.DiffMain( text1a, text2a, false, deadline );
-               diffsb = this.DiffMain( text1b, text2b, false, deadline );
-
-               return diffs.concat( diffsb );
-       };
-
-       /**
-        * Reduce the number of edits by eliminating semantically trivial equalities.
-        * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
-        */
-       DiffMatchPatch.prototype.diffCleanupSemantic = function( diffs ) {
-               var changes, equalities, equalitiesLength, lastequality,
-                       pointer, lengthInsertions2, lengthDeletions2, lengthInsertions1,
-                       lengthDeletions1, deletion, insertion, overlapLength1, overlapLength2;
-               changes = false;
-               equalities = []; // Stack of indices where equalities are found.
-               equalitiesLength = 0; // Keeping our own length var is faster in JS.
-               /** @type {?string} */
-               lastequality = null;
-
-               // Always equal to diffs[equalities[equalitiesLength - 1]][1]
-               pointer = 0; // Index of current position.
-
-               // Number of characters that changed prior to the equality.
-               lengthInsertions1 = 0;
-               lengthDeletions1 = 0;
-
-               // Number of characters that changed after the equality.
-               lengthInsertions2 = 0;
-               lengthDeletions2 = 0;
-               while ( pointer < diffs.length ) {
-                       if ( diffs[ pointer ][ 0 ] === DIFF_EQUAL ) { // Equality found.
-                               equalities[ equalitiesLength++ ] = pointer;
-                               lengthInsertions1 = lengthInsertions2;
-                               lengthDeletions1 = lengthDeletions2;
-                               lengthInsertions2 = 0;
-                               lengthDeletions2 = 0;
-                               lastequality = diffs[ pointer ][ 1 ];
-                       } else { // An insertion or deletion.
-                               if ( diffs[ pointer ][ 0 ] === DIFF_INSERT ) {
-                                       lengthInsertions2 += diffs[ pointer ][ 1 ].length;
-                               } else {
-                                       lengthDeletions2 += diffs[ pointer ][ 1 ].length;
-                               }
-
-                               // Eliminate an equality that is smaller or equal to the edits on both
-                               // sides of it.
-                               if ( lastequality && ( lastequality.length <=
-                                               Math.max( lengthInsertions1, lengthDeletions1 ) ) &&
-                                               ( lastequality.length <= Math.max( lengthInsertions2,
-                                                       lengthDeletions2 ) ) ) {
-
-                                       // Duplicate record.
-                                       diffs.splice(
-                                               equalities[ equalitiesLength - 1 ],
-                                               0,
-                                               [ DIFF_DELETE, lastequality ]
-                                       );
-
-                                       // Change second copy to insert.
-                                       diffs[ equalities[ equalitiesLength - 1 ] + 1 ][ 0 ] = DIFF_INSERT;
-
-                                       // Throw away the equality we just deleted.
-                                       equalitiesLength--;
-
-                                       // Throw away the previous equality (it needs to be reevaluated).
-                                       equalitiesLength--;
-                                       pointer = equalitiesLength > 0 ? equalities[ equalitiesLength - 1 ] : -1;
-
-                                       // Reset the counters.
-                                       lengthInsertions1 = 0;
-                                       lengthDeletions1 = 0;
-                                       lengthInsertions2 = 0;
-                                       lengthDeletions2 = 0;
-                                       lastequality = null;
-                                       changes = true;
-                               }
-                       }
-                       pointer++;
-               }
-
-               // Normalize the diff.
-               if ( changes ) {
-                       this.diffCleanupMerge( diffs );
-               }
-
-               // Find any overlaps between deletions and insertions.
-               // e.g: <del>abcxxx</del><ins>xxxdef</ins>
-               //   -> <del>abc</del>xxx<ins>def</ins>
-               // e.g: <del>xxxabc</del><ins>defxxx</ins>
-               //   -> <ins>def</ins>xxx<del>abc</del>
-               // Only extract an overlap if it is as big as the edit ahead or behind it.
-               pointer = 1;
-               while ( pointer < diffs.length ) {
-                       if ( diffs[ pointer - 1 ][ 0 ] === DIFF_DELETE &&
-                                       diffs[ pointer ][ 0 ] === DIFF_INSERT ) {
-                               deletion = diffs[ pointer - 1 ][ 1 ];
-                               insertion = diffs[ pointer ][ 1 ];
-                               overlapLength1 = this.diffCommonOverlap( deletion, insertion );
-                               overlapLength2 = this.diffCommonOverlap( insertion, deletion );
-                               if ( overlapLength1 >= overlapLength2 ) {
-                                       if ( overlapLength1 >= deletion.length / 2 ||
-                                                       overlapLength1 >= insertion.length / 2 ) {
-
-                                               // Overlap found.  Insert an equality and trim the surrounding edits.
-                                               diffs.splice(
-                                                       pointer,
-                                                       0,
-                                                       [ DIFF_EQUAL, insertion.substring( 0, overlapLength1 ) ]
-                                               );
-                                               diffs[ pointer - 1 ][ 1 ] =
-                                                       deletion.substring( 0, deletion.length - overlapLength1 );
-                                               diffs[ pointer + 1 ][ 1 ] = insertion.substring( overlapLength1 );
-                                               pointer++;
-                                       }
-                               } else {
-                                       if ( overlapLength2 >= deletion.length / 2 ||
-                                                       overlapLength2 >= insertion.length / 2 ) {
-
-                                               // Reverse overlap found.
-                                               // Insert an equality and swap and trim the surrounding edits.
-                                               diffs.splice(
-                                                       pointer,
-                                                       0,
-                                                       [ DIFF_EQUAL, deletion.substring( 0, overlapLength2 ) ]
-                                               );
-
-                                               diffs[ pointer - 1 ][ 0 ] = DIFF_INSERT;
-                                               diffs[ pointer - 1 ][ 1 ] =
-                                                       insertion.substring( 0, insertion.length - overlapLength2 );
-                                               diffs[ pointer + 1 ][ 0 ] = DIFF_DELETE;
-                                               diffs[ pointer + 1 ][ 1 ] =
-                                                       deletion.substring( overlapLength2 );
-                                               pointer++;
-                                       }
-                               }
-                               pointer++;
-                       }
-                       pointer++;
-               }
-       };
-
-       /**
-        * Determine if the suffix of one string is the prefix of another.
-        * @param {string} text1 First string.
-        * @param {string} text2 Second string.
-        * @return {number} The number of characters common to the end of the first
-        *     string and the start of the second string.
-        * @private
-        */
-       DiffMatchPatch.prototype.diffCommonOverlap = function( text1, text2 ) {
-               var text1Length, text2Length, textLength,
-                       best, length, pattern, found;
-
-               // Cache the text lengths to prevent multiple calls.
-               text1Length = text1.length;
-               text2Length = text2.length;
-
-               // Eliminate the null case.
-               if ( text1Length === 0 || text2Length === 0 ) {
-                       return 0;
-               }
-
-               // Truncate the longer string.
-               if ( text1Length > text2Length ) {
-                       text1 = text1.substring( text1Length - text2Length );
-               } else if ( text1Length < text2Length ) {
-                       text2 = text2.substring( 0, text1Length );
-               }
-               textLength = Math.min( text1Length, text2Length );
-
-               // Quick check for the worst case.
-               if ( text1 === text2 ) {
-                       return textLength;
-               }
-
-               // Start by looking for a single character match
-               // and increase length until no match is found.
-               // Performance analysis: https://neil.fraser.name/news/2010/11/04/
-               best = 0;
-               length = 1;
-               while ( true ) {
-                       pattern = text1.substring( textLength - length );
-                       found = text2.indexOf( pattern );
-                       if ( found === -1 ) {
-                               return best;
-                       }
-                       length += found;
-                       if ( found === 0 || text1.substring( textLength - length ) ===
-                                       text2.substring( 0, length ) ) {
-                               best = length;
-                               length++;
-                       }
-               }
-       };
-
-       /**
-        * Split two texts into an array of strings.  Reduce the texts to a string of
-        * hashes where each Unicode character represents one line.
-        * @param {string} text1 First string.
-        * @param {string} text2 Second string.
-        * @return {{chars1: string, chars2: string, lineArray: !Array.<string>}}
-        *     An object containing the encoded text1, the encoded text2 and
-        *     the array of unique strings.
-        *     The zeroth element of the array of unique strings is intentionally blank.
-        * @private
-        */
-       DiffMatchPatch.prototype.diffLinesToChars = function( text1, text2 ) {
-               var lineArray, lineHash, chars1, chars2;
-               lineArray = []; // E.g. lineArray[4] === 'Hello\n'
-               lineHash = {};  // E.g. lineHash['Hello\n'] === 4
-
-               // '\x00' is a valid character, but various debuggers don't like it.
-               // So we'll insert a junk entry to avoid generating a null character.
-               lineArray[ 0 ] = "";
-
-               /**
-                * Split a text into an array of strings.  Reduce the texts to a string of
-                * hashes where each Unicode character represents one line.
-                * Modifies linearray and linehash through being a closure.
-                * @param {string} text String to encode.
-                * @return {string} Encoded string.
-                * @private
-                */
-               function diffLinesToCharsMunge( text ) {
-                       var chars, lineStart, lineEnd, lineArrayLength, line;
-                       chars = "";
-
-                       // Walk the text, pulling out a substring for each line.
-                       // text.split('\n') would would temporarily double our memory footprint.
-                       // Modifying text would create many large strings to garbage collect.
-                       lineStart = 0;
-                       lineEnd = -1;
-
-                       // Keeping our own length variable is faster than looking it up.
-                       lineArrayLength = lineArray.length;
-                       while ( lineEnd < text.length - 1 ) {
-                               lineEnd = text.indexOf( "\n", lineStart );
-                               if ( lineEnd === -1 ) {
-                                       lineEnd = text.length - 1;
-                               }
-                               line = text.substring( lineStart, lineEnd + 1 );
-                               lineStart = lineEnd + 1;
-
-                               if ( lineHash.hasOwnProperty ? lineHash.hasOwnProperty( line ) :
-                                                       ( lineHash[ line ] !== undefined ) ) {
-                                       chars += String.fromCharCode( lineHash[ line ] );
-                               } else {
-                                       chars += String.fromCharCode( lineArrayLength );
-                                       lineHash[ line ] = lineArrayLength;
-                                       lineArray[ lineArrayLength++ ] = line;
-                               }
-                       }
-                       return chars;
-               }
-
-               chars1 = diffLinesToCharsMunge( text1 );
-               chars2 = diffLinesToCharsMunge( text2 );
-               return {
-                       chars1: chars1,
-                       chars2: chars2,
-                       lineArray: lineArray
-               };
-       };
-
-       /**
-        * Rehydrate the text in a diff from a string of line hashes to real lines of
-        * text.
-        * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
-        * @param {!Array.<string>} lineArray Array of unique strings.
-        * @private
-        */
-       DiffMatchPatch.prototype.diffCharsToLines = function( diffs, lineArray ) {
-               var x, chars, text, y;
-               for ( x = 0; x < diffs.length; x++ ) {
-                       chars = diffs[ x ][ 1 ];
-                       text = [];
-                       for ( y = 0; y < chars.length; y++ ) {
-                               text[ y ] = lineArray[ chars.charCodeAt( y ) ];
-                       }
-                       diffs[ x ][ 1 ] = text.join( "" );
-               }
-       };
-
-       /**
-        * Reorder and merge like edit sections.  Merge equalities.
-        * Any edit section can move as long as it doesn't cross an equality.
-        * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
-        */
-       DiffMatchPatch.prototype.diffCleanupMerge = function( diffs ) {
-               var pointer, countDelete, countInsert, textInsert, textDelete,
-                       commonlength, changes, diffPointer, position;
-               diffs.push( [ DIFF_EQUAL, "" ] ); // Add a dummy entry at the end.
-               pointer = 0;
-               countDelete = 0;
-               countInsert = 0;
-               textDelete = "";
-               textInsert = "";
-               commonlength;
-               while ( pointer < diffs.length ) {
-                       switch ( diffs[ pointer ][ 0 ] ) {
-                       case DIFF_INSERT:
-                               countInsert++;
-                               textInsert += diffs[ pointer ][ 1 ];
-                               pointer++;
-                               break;
-                       case DIFF_DELETE:
-                               countDelete++;
-                               textDelete += diffs[ pointer ][ 1 ];
-                               pointer++;
-                               break;
-                       case DIFF_EQUAL:
-
-                               // Upon reaching an equality, check for prior redundancies.
-                               if ( countDelete + countInsert > 1 ) {
-                                       if ( countDelete !== 0 && countInsert !== 0 ) {
-
-                                               // Factor out any common prefixes.
-                                               commonlength = this.diffCommonPrefix( textInsert, textDelete );
-                                               if ( commonlength !== 0 ) {
-                                                       if ( ( pointer - countDelete - countInsert ) > 0 &&
-                                                                       diffs[ pointer - countDelete - countInsert - 1 ][ 0 ] ===
-                                                                       DIFF_EQUAL ) {
-                                                               diffs[ pointer - countDelete - countInsert - 1 ][ 1 ] +=
-                                                                       textInsert.substring( 0, commonlength );
-                                                       } else {
-                                                               diffs.splice( 0, 0, [ DIFF_EQUAL,
-                                                                       textInsert.substring( 0, commonlength )
-                                                               ] );
-                                                               pointer++;
-                                                       }
-                                                       textInsert = textInsert.substring( commonlength );
-                                                       textDelete = textDelete.substring( commonlength );
-                                               }
-
-                                               // Factor out any common suffixies.
-                                               commonlength = this.diffCommonSuffix( textInsert, textDelete );
-                                               if ( commonlength !== 0 ) {
-                                                       diffs[ pointer ][ 1 ] = textInsert.substring( textInsert.length -
-                                                                       commonlength ) + diffs[ pointer ][ 1 ];
-                                                       textInsert = textInsert.substring( 0, textInsert.length -
-                                                               commonlength );
-                                                       textDelete = textDelete.substring( 0, textDelete.length -
-                                                               commonlength );
-                                               }
-                                       }
-
-                                       // Delete the offending records and add the merged ones.
-                                       if ( countDelete === 0 ) {
-                                               diffs.splice( pointer - countInsert,
-                                                       countDelete + countInsert, [ DIFF_INSERT, textInsert ] );
-                                       } else if ( countInsert === 0 ) {
-                                               diffs.splice( pointer - countDelete,
-                                                       countDelete + countInsert, [ DIFF_DELETE, textDelete ] );
-                                       } else {
-                                               diffs.splice(
-                                                       pointer - countDelete - countInsert,
-                                                       countDelete + countInsert,
-                                                       [ DIFF_DELETE, textDelete ], [ DIFF_INSERT, textInsert ]
-                                               );
-                                       }
-                                       pointer = pointer - countDelete - countInsert +
-                                               ( countDelete ? 1 : 0 ) + ( countInsert ? 1 : 0 ) + 1;
-                               } else if ( pointer !== 0 && diffs[ pointer - 1 ][ 0 ] === DIFF_EQUAL ) {
-
-                                       // Merge this equality with the previous one.
-                                       diffs[ pointer - 1 ][ 1 ] += diffs[ pointer ][ 1 ];
-                                       diffs.splice( pointer, 1 );
-                               } else {
-                                       pointer++;
-                               }
-                               countInsert = 0;
-                               countDelete = 0;
-                               textDelete = "";
-                               textInsert = "";
-                               break;
-                       }
-               }
-               if ( diffs[ diffs.length - 1 ][ 1 ] === "" ) {
-                       diffs.pop(); // Remove the dummy entry at the end.
-               }
-
-               // Second pass: look for single edits surrounded on both sides by equalities
-               // which can be shifted sideways to eliminate an equality.
-               // e.g: A<ins>BA</ins>C -> <ins>AB</ins>AC
-               changes = false;
-               pointer = 1;
-
-               // Intentionally ignore the first and last element (don't need checking).
-               while ( pointer < diffs.length - 1 ) {
-                       if ( diffs[ pointer - 1 ][ 0 ] === DIFF_EQUAL &&
-                                       diffs[ pointer + 1 ][ 0 ] === DIFF_EQUAL ) {
-
-                               diffPointer = diffs[ pointer ][ 1 ];
-                               position = diffPointer.substring(
-                                       diffPointer.length - diffs[ pointer - 1 ][ 1 ].length
-                               );
-
-                               // This is a single edit surrounded by equalities.
-                               if ( position === diffs[ pointer - 1 ][ 1 ] ) {
-
-                                       // Shift the edit over the previous equality.
-                                       diffs[ pointer ][ 1 ] = diffs[ pointer - 1 ][ 1 ] +
-                                               diffs[ pointer ][ 1 ].substring( 0, diffs[ pointer ][ 1 ].length -
-                                                       diffs[ pointer - 1 ][ 1 ].length );
-                                       diffs[ pointer + 1 ][ 1 ] =
-                                               diffs[ pointer - 1 ][ 1 ] + diffs[ pointer + 1 ][ 1 ];
-                                       diffs.splice( pointer - 1, 1 );
-                                       changes = true;
-                               } else if ( diffPointer.substring( 0, diffs[ pointer + 1 ][ 1 ].length ) ===
-                                               diffs[ pointer + 1 ][ 1 ] ) {
-
-                                       // Shift the edit over the next equality.
-                                       diffs[ pointer - 1 ][ 1 ] += diffs[ pointer + 1 ][ 1 ];
-                                       diffs[ pointer ][ 1 ] =
-                                               diffs[ pointer ][ 1 ].substring( diffs[ pointer + 1 ][ 1 ].length ) +
-                                               diffs[ pointer + 1 ][ 1 ];
-                                       diffs.splice( pointer + 1, 1 );
-                                       changes = true;
-                               }
-                       }
-                       pointer++;
-               }
-
-               // If shifts were made, the diff needs reordering and another shift sweep.
-               if ( changes ) {
-                       this.diffCleanupMerge( diffs );
-               }
-       };
-
-       return function( o, n ) {
-               var diff, output, text;
-               diff = new DiffMatchPatch();
-               output = diff.DiffMain( o, n );
-               diff.diffCleanupEfficiency( output );
-               text = diff.diffPrettyHtml( output );
-
-               return text;
-       };
-}() );
-
-}() );
+
+
+  var classCallCheck = function (instance, Constructor) {
+    if (!(instance instanceof Constructor)) {
+      throw new TypeError("Cannot call a class as a function");
+    }
+  };
+
+  var createClass = function () {
+    function defineProperties(target, props) {
+      for (var i = 0; i < props.length; i++) {
+        var descriptor = props[i];
+        descriptor.enumerable = descriptor.enumerable || false;
+        descriptor.configurable = true;
+        if ("value" in descriptor) descriptor.writable = true;
+        Object.defineProperty(target, descriptor.key, descriptor);
+      }
+    }
+
+    return function (Constructor, protoProps, staticProps) {
+      if (protoProps) defineProperties(Constructor.prototype, protoProps);
+      if (staticProps) defineProperties(Constructor, staticProps);
+      return Constructor;
+    };
+  }();
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+  var toConsumableArray = function (arr) {
+    if (Array.isArray(arr)) {
+      for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];
+
+      return arr2;
+    } else {
+      return Array.from(arr);
+    }
+  };
+
+  var toString = Object.prototype.toString;
+  var hasOwn = Object.prototype.hasOwnProperty;
+  var now = Date.now || function () {
+       return new Date().getTime();
+  };
+
+  var defined = {
+       document: window && window.document !== undefined,
+       setTimeout: setTimeout !== undefined
+  };
+
+  // Returns a new Array with the elements that are in a but not in b
+  function diff(a, b) {
+       var i,
+           j,
+           result = a.slice();
+
+       for (i = 0; i < result.length; i++) {
+               for (j = 0; j < b.length; j++) {
+                       if (result[i] === b[j]) {
+                               result.splice(i, 1);
+                               i--;
+                               break;
+                       }
+               }
+       }
+       return result;
+  }
+
+  /**
+   * Determines whether an element exists in a given array or not.
+   *
+   * @method inArray
+   * @param {Any} elem
+   * @param {Array} array
+   * @return {Boolean}
+   */
+  function inArray(elem, array) {
+       return array.indexOf(elem) !== -1;
+  }
+
+  /**
+   * Makes a clone of an object using only Array or Object as base,
+   * and copies over the own enumerable properties.
+   *
+   * @param {Object} obj
+   * @return {Object} New object with only the own properties (recursively).
+   */
+  function objectValues(obj) {
+       var key,
+           val,
+           vals = is("array", obj) ? [] : {};
+       for (key in obj) {
+               if (hasOwn.call(obj, key)) {
+                       val = obj[key];
+                       vals[key] = val === Object(val) ? objectValues(val) : val;
+               }
+       }
+       return vals;
+  }
+
+  function extend(a, b, undefOnly) {
+       for (var prop in b) {
+               if (hasOwn.call(b, prop)) {
+                       if (b[prop] === undefined) {
+                               delete a[prop];
+                       } else if (!(undefOnly && typeof a[prop] !== "undefined")) {
+                               a[prop] = b[prop];
+                       }
+               }
+       }
+
+       return a;
+  }
+
+  function objectType(obj) {
+       if (typeof obj === "undefined") {
+               return "undefined";
+       }
+
+       // Consider: typeof null === object
+       if (obj === null) {
+               return "null";
+       }
+
+       var match = toString.call(obj).match(/^\[object\s(.*)\]$/),
+           type = match && match[1];
+
+       switch (type) {
+               case "Number":
+                       if (isNaN(obj)) {
+                               return "nan";
+                       }
+                       return "number";
+               case "String":
+               case "Boolean":
+               case "Array":
+               case "Set":
+               case "Map":
+               case "Date":
+               case "RegExp":
+               case "Function":
+               case "Symbol":
+                       return type.toLowerCase();
+               default:
+                       return typeof obj === "undefined" ? "undefined" : _typeof(obj);
+       }
+  }
+
+  // Safe object type checking
+  function is(type, obj) {
+       return objectType(obj) === type;
+  }
+
+  // Based on Java's String.hashCode, a simple but not
+  // rigorously collision resistant hashing function
+  function generateHash(module, testName) {
+       var str = module + "\x1C" + testName;
+       var hash = 0;
+
+       for (var i = 0; i < str.length; i++) {
+               hash = (hash << 5) - hash + str.charCodeAt(i);
+               hash |= 0;
+       }
+
+       // Convert the possibly negative integer hash code into an 8 character hex string, which isn't
+       // strictly necessary but increases user understanding that the id is a SHA-like hash
+       var hex = (0x100000000 + hash).toString(16);
+       if (hex.length < 8) {
+               hex = "0000000" + hex;
+       }
+
+       return hex.slice(-8);
+  }
+
+  // Test for equality any JavaScript type.
+  // Authors: Philippe Rathé <prathe@gmail.com>, David Chan <david@troi.org>
+  var equiv = (function () {
+
+       // Value pairs queued for comparison. Used for breadth-first processing order, recursion
+       // detection and avoiding repeated comparison (see below for details).
+       // Elements are { a: val, b: val }.
+       var pairs = [];
+
+       var getProto = Object.getPrototypeOf || function (obj) {
+               return obj.__proto__;
+       };
+
+       function useStrictEquality(a, b) {
+
+               // This only gets called if a and b are not strict equal, and is used to compare on
+               // the primitive values inside object wrappers. For example:
+               // `var i = 1;`
+               // `var j = new Number(1);`
+               // Neither a nor b can be null, as a !== b and they have the same type.
+               if ((typeof a === "undefined" ? "undefined" : _typeof(a)) === "object") {
+                       a = a.valueOf();
+               }
+               if ((typeof b === "undefined" ? "undefined" : _typeof(b)) === "object") {
+                       b = b.valueOf();
+               }
+
+               return a === b;
+       }
+
+       function compareConstructors(a, b) {
+               var protoA = getProto(a);
+               var protoB = getProto(b);
+
+               // Comparing constructors is more strict than using `instanceof`
+               if (a.constructor === b.constructor) {
+                       return true;
+               }
+
+               // Ref #851
+               // If the obj prototype descends from a null constructor, treat it
+               // as a null prototype.
+               if (protoA && protoA.constructor === null) {
+                       protoA = null;
+               }
+               if (protoB && protoB.constructor === null) {
+                       protoB = null;
+               }
+
+               // Allow objects with no prototype to be equivalent to
+               // objects with Object as their constructor.
+               if (protoA === null && protoB === Object.prototype || protoB === null && protoA === Object.prototype) {
+                       return true;
+               }
+
+               return false;
+       }
+
+       function getRegExpFlags(regexp) {
+               return "flags" in regexp ? regexp.flags : regexp.toString().match(/[gimuy]*$/)[0];
+       }
+
+       function isContainer(val) {
+               return ["object", "array", "map", "set"].indexOf(objectType(val)) !== -1;
+       }
+
+       function breadthFirstCompareChild(a, b) {
+
+               // If a is a container not reference-equal to b, postpone the comparison to the
+               // end of the pairs queue -- unless (a, b) has been seen before, in which case skip
+               // over the pair.
+               if (a === b) {
+                       return true;
+               }
+               if (!isContainer(a)) {
+                       return typeEquiv(a, b);
+               }
+               if (pairs.every(function (pair) {
+                       return pair.a !== a || pair.b !== b;
+               })) {
+
+                       // Not yet started comparing this pair
+                       pairs.push({ a: a, b: b });
+               }
+               return true;
+       }
+
+       var callbacks = {
+               "string": useStrictEquality,
+               "boolean": useStrictEquality,
+               "number": useStrictEquality,
+               "null": useStrictEquality,
+               "undefined": useStrictEquality,
+               "symbol": useStrictEquality,
+               "date": useStrictEquality,
+
+               "nan": function nan() {
+                       return true;
+               },
+
+               "regexp": function regexp(a, b) {
+                       return a.source === b.source &&
+
+                       // Include flags in the comparison
+                       getRegExpFlags(a) === getRegExpFlags(b);
+               },
+
+               // abort (identical references / instance methods were skipped earlier)
+               "function": function _function() {
+                       return false;
+               },
+
+               "array": function array(a, b) {
+                       var i, len;
+
+                       len = a.length;
+                       if (len !== b.length) {
+
+                               // Safe and faster
+                               return false;
+                       }
+
+                       for (i = 0; i < len; i++) {
+
+                               // Compare non-containers; queue non-reference-equal containers
+                               if (!breadthFirstCompareChild(a[i], b[i])) {
+                                       return false;
+                               }
+                       }
+                       return true;
+               },
+
+               // Define sets a and b to be equivalent if for each element aVal in a, there
+               // is some element bVal in b such that aVal and bVal are equivalent. Element
+               // repetitions are not counted, so these are equivalent:
+               // a = new Set( [ {}, [], [] ] );
+               // b = new Set( [ {}, {}, [] ] );
+               "set": function set$$1(a, b) {
+                       var innerEq,
+                           outerEq = true;
+
+                       if (a.size !== b.size) {
+
+                               // This optimization has certain quirks because of the lack of
+                               // repetition counting. For instance, adding the same
+                               // (reference-identical) element to two equivalent sets can
+                               // make them non-equivalent.
+                               return false;
+                       }
+
+                       a.forEach(function (aVal) {
+
+                               // Short-circuit if the result is already known. (Using for...of
+                               // with a break clause would be cleaner here, but it would cause
+                               // a syntax error on older Javascript implementations even if
+                               // Set is unused)
+                               if (!outerEq) {
+                                       return;
+                               }
+
+                               innerEq = false;
+
+                               b.forEach(function (bVal) {
+                                       var parentPairs;
+
+                                       // Likewise, short-circuit if the result is already known
+                                       if (innerEq) {
+                                               return;
+                                       }
+
+                                       // Swap out the global pairs list, as the nested call to
+                                       // innerEquiv will clobber its contents
+                                       parentPairs = pairs;
+                                       if (innerEquiv(bVal, aVal)) {
+                                               innerEq = true;
+                                       }
+
+                                       // Replace the global pairs list
+                                       pairs = parentPairs;
+                               });
+
+                               if (!innerEq) {
+                                       outerEq = false;
+                               }
+                       });
+
+                       return outerEq;
+               },
+
+               // Define maps a and b to be equivalent if for each key-value pair (aKey, aVal)
+               // in a, there is some key-value pair (bKey, bVal) in b such that
+               // [ aKey, aVal ] and [ bKey, bVal ] are equivalent. Key repetitions are not
+               // counted, so these are equivalent:
+               // a = new Map( [ [ {}, 1 ], [ {}, 1 ], [ [], 1 ] ] );
+               // b = new Map( [ [ {}, 1 ], [ [], 1 ], [ [], 1 ] ] );
+               "map": function map(a, b) {
+                       var innerEq,
+                           outerEq = true;
+
+                       if (a.size !== b.size) {
+
+                               // This optimization has certain quirks because of the lack of
+                               // repetition counting. For instance, adding the same
+                               // (reference-identical) key-value pair to two equivalent maps
+                               // can make them non-equivalent.
+                               return false;
+                       }
+
+                       a.forEach(function (aVal, aKey) {
+
+                               // Short-circuit if the result is already known. (Using for...of
+                               // with a break clause would be cleaner here, but it would cause
+                               // a syntax error on older Javascript implementations even if
+                               // Map is unused)
+                               if (!outerEq) {
+                                       return;
+                               }
+
+                               innerEq = false;
+
+                               b.forEach(function (bVal, bKey) {
+                                       var parentPairs;
+
+                                       // Likewise, short-circuit if the result is already known
+                                       if (innerEq) {
+                                               return;
+                                       }
+
+                                       // Swap out the global pairs list, as the nested call to
+                                       // innerEquiv will clobber its contents
+                                       parentPairs = pairs;
+                                       if (innerEquiv([bVal, bKey], [aVal, aKey])) {
+                                               innerEq = true;
+                                       }
+
+                                       // Replace the global pairs list
+                                       pairs = parentPairs;
+                               });
+
+                               if (!innerEq) {
+                                       outerEq = false;
+                               }
+                       });
+
+                       return outerEq;
+               },
+
+               "object": function object(a, b) {
+                       var i,
+                           aProperties = [],
+                           bProperties = [];
+
+                       if (compareConstructors(a, b) === false) {
+                               return false;
+                       }
+
+                       // Be strict: don't ensure hasOwnProperty and go deep
+                       for (i in a) {
+
+                               // Collect a's properties
+                               aProperties.push(i);
+
+                               // Skip OOP methods that look the same
+                               if (a.constructor !== Object && typeof a.constructor !== "undefined" && typeof a[i] === "function" && typeof b[i] === "function" && a[i].toString() === b[i].toString()) {
+                                       continue;
+                               }
+
+                               // Compare non-containers; queue non-reference-equal containers
+                               if (!breadthFirstCompareChild(a[i], b[i])) {
+                                       return false;
+                               }
+                       }
+
+                       for (i in b) {
+
+                               // Collect b's properties
+                               bProperties.push(i);
+                       }
+
+                       // Ensures identical properties name
+                       return typeEquiv(aProperties.sort(), bProperties.sort());
+               }
+       };
+
+       function typeEquiv(a, b) {
+               var type = objectType(a);
+
+               // Callbacks for containers will append to the pairs queue to achieve breadth-first
+               // search order. The pairs queue is also used to avoid reprocessing any pair of
+               // containers that are reference-equal to a previously visited pair (a special case
+               // this being recursion detection).
+               //
+               // Because of this approach, once typeEquiv returns a false value, it should not be
+               // called again without clearing the pair queue else it may wrongly report a visited
+               // pair as being equivalent.
+               return objectType(b) === type && callbacks[type](a, b);
+       }
+
+       function innerEquiv(a, b) {
+               var i, pair;
+
+               // We're done when there's nothing more to compare
+               if (arguments.length < 2) {
+                       return true;
+               }
+
+               // Clear the global pair queue and add the top-level values being compared
+               pairs = [{ a: a, b: b }];
+
+               for (i = 0; i < pairs.length; i++) {
+                       pair = pairs[i];
+
+                       // Perform type-specific comparison on any pairs that are not strictly
+                       // equal. For container types, that comparison will postpone comparison
+                       // of any sub-container pair to the end of the pair queue. This gives
+                       // breadth-first search order. It also avoids the reprocessing of
+                       // reference-equal siblings, cousins etc, which can have a significant speed
+                       // impact when comparing a container of small objects each of which has a
+                       // reference to the same (singleton) large object.
+                       if (pair.a !== pair.b && !typeEquiv(pair.a, pair.b)) {
+                               return false;
+                       }
+               }
+
+               // ...across all consecutive argument pairs
+               return arguments.length === 2 || innerEquiv.apply(this, [].slice.call(arguments, 1));
+       }
+
+       return function () {
+               var result = innerEquiv.apply(undefined, arguments);
+
+               // Release any retained objects
+               pairs.length = 0;
+               return result;
+       };
+  })();
+
+  /**
+   * Config object: Maintain internal state
+   * Later exposed as QUnit.config
+   * `config` initialized at top of scope
+   */
+  var config = {
+
+       // The queue of tests to run
+       queue: [],
+
+       // Block until document ready
+       blocking: true,
+
+       // By default, run previously failed tests first
+       // very useful in combination with "Hide passed tests" checked
+       reorder: true,
+
+       // By default, modify document.title when suite is done
+       altertitle: true,
+
+       // HTML Reporter: collapse every test except the first failing test
+       // If false, all failing tests will be expanded
+       collapse: true,
+
+       // By default, scroll to top of the page when suite is done
+       scrolltop: true,
+
+       // Depth up-to which object will be dumped
+       maxDepth: 5,
+
+       // When enabled, all tests must call expect()
+       requireExpects: false,
+
+       // Placeholder for user-configurable form-exposed URL parameters
+       urlConfig: [],
+
+       // Set of all modules.
+       modules: [],
+
+       // The first unnamed module
+       currentModule: {
+               name: "",
+               tests: [],
+               childModules: [],
+               testsRun: 0,
+               unskippedTestsRun: 0,
+               hooks: {
+                       before: [],
+                       beforeEach: [],
+                       afterEach: [],
+                       after: []
+               }
+       },
+
+       callbacks: {},
+
+       // The storage module to use for reordering tests
+       storage: localSessionStorage
+  };
+
+  // take a predefined QUnit.config and extend the defaults
+  var globalConfig = window && window.QUnit && window.QUnit.config;
+
+  // only extend the global config if there is no QUnit overload
+  if (window && window.QUnit && !window.QUnit.version) {
+       extend(config, globalConfig);
+  }
+
+  // Push a loose unnamed module to the modules collection
+  config.modules.push(config.currentModule);
+
+  // Based on jsDump by Ariel Flesler
+  // http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html
+  var dump = (function () {
+       function quote(str) {
+               return "\"" + str.toString().replace(/\\/g, "\\\\").replace(/"/g, "\\\"") + "\"";
+       }
+       function literal(o) {
+               return o + "";
+       }
+       function join(pre, arr, post) {
+               var s = dump.separator(),
+                   base = dump.indent(),
+                   inner = dump.indent(1);
+               if (arr.join) {
+                       arr = arr.join("," + s + inner);
+               }
+               if (!arr) {
+                       return pre + post;
+               }
+               return [pre, inner + arr, base + post].join(s);
+       }
+       function array(arr, stack) {
+               var i = arr.length,
+                   ret = new Array(i);
+
+               if (dump.maxDepth && dump.depth > dump.maxDepth) {
+                       return "[object Array]";
+               }
+
+               this.up();
+               while (i--) {
+                       ret[i] = this.parse(arr[i], undefined, stack);
+               }
+               this.down();
+               return join("[", ret, "]");
+       }
+
+       function isArray(obj) {
+               return (
+
+                       //Native Arrays
+                       toString.call(obj) === "[object Array]" ||
+
+                       // NodeList objects
+                       typeof obj.length === "number" && obj.item !== undefined && (obj.length ? obj.item(0) === obj[0] : obj.item(0) === null && obj[0] === undefined)
+               );
+       }
+
+       var reName = /^function (\w+)/,
+           dump = {
+
+               // The objType is used mostly internally, you can fix a (custom) type in advance
+               parse: function parse(obj, objType, stack) {
+                       stack = stack || [];
+                       var res,
+                           parser,
+                           parserType,
+                           objIndex = stack.indexOf(obj);
+
+                       if (objIndex !== -1) {
+                               return "recursion(" + (objIndex - stack.length) + ")";
+                       }
+
+                       objType = objType || this.typeOf(obj);
+                       parser = this.parsers[objType];
+                       parserType = typeof parser === "undefined" ? "undefined" : _typeof(parser);
+
+                       if (parserType === "function") {
+                               stack.push(obj);
+                               res = parser.call(this, obj, stack);
+                               stack.pop();
+                               return res;
+                       }
+                       return parserType === "string" ? parser : this.parsers.error;
+               },
+               typeOf: function typeOf(obj) {
+                       var type;
+
+                       if (obj === null) {
+                               type = "null";
+                       } else if (typeof obj === "undefined") {
+                               type = "undefined";
+                       } else if (is("regexp", obj)) {
+                               type = "regexp";
+                       } else if (is("date", obj)) {
+                               type = "date";
+                       } else if (is("function", obj)) {
+                               type = "function";
+                       } else if (obj.setInterval !== undefined && obj.document !== undefined && obj.nodeType === undefined) {
+                               type = "window";
+                       } else if (obj.nodeType === 9) {
+                               type = "document";
+                       } else if (obj.nodeType) {
+                               type = "node";
+                       } else if (isArray(obj)) {
+                               type = "array";
+                       } else if (obj.constructor === Error.prototype.constructor) {
+                               type = "error";
+                       } else {
+                               type = typeof obj === "undefined" ? "undefined" : _typeof(obj);
+                       }
+                       return type;
+               },
+
+               separator: function separator() {
+                       if (this.multiline) {
+                               return this.HTML ? "<br />" : "\n";
+                       } else {
+                               return this.HTML ? "&#160;" : " ";
+                       }
+               },
+
+               // Extra can be a number, shortcut for increasing-calling-decreasing
+               indent: function indent(extra) {
+                       if (!this.multiline) {
+                               return "";
+                       }
+                       var chr = this.indentChar;
+                       if (this.HTML) {
+                               chr = chr.replace(/\t/g, "   ").replace(/ /g, "&#160;");
+                       }
+                       return new Array(this.depth + (extra || 0)).join(chr);
+               },
+               up: function up(a) {
+                       this.depth += a || 1;
+               },
+               down: function down(a) {
+                       this.depth -= a || 1;
+               },
+               setParser: function setParser(name, parser) {
+                       this.parsers[name] = parser;
+               },
+
+               // The next 3 are exposed so you can use them
+               quote: quote,
+               literal: literal,
+               join: join,
+               depth: 1,
+               maxDepth: config.maxDepth,
+
+               // This is the list of parsers, to modify them, use dump.setParser
+               parsers: {
+                       window: "[Window]",
+                       document: "[Document]",
+                       error: function error(_error) {
+                               return "Error(\"" + _error.message + "\")";
+                       },
+                       unknown: "[Unknown]",
+                       "null": "null",
+                       "undefined": "undefined",
+                       "function": function _function(fn) {
+                               var ret = "function",
+
+
+                               // Functions never have name in IE
+                               name = "name" in fn ? fn.name : (reName.exec(fn) || [])[1];
+
+                               if (name) {
+                                       ret += " " + name;
+                               }
+                               ret += "(";
+
+                               ret = [ret, dump.parse(fn, "functionArgs"), "){"].join("");
+                               return join(ret, dump.parse(fn, "functionCode"), "}");
+                       },
+                       array: array,
+                       nodelist: array,
+                       "arguments": array,
+                       object: function object(map, stack) {
+                               var keys,
+                                   key,
+                                   val,
+                                   i,
+                                   nonEnumerableProperties,
+                                   ret = [];
+
+                               if (dump.maxDepth && dump.depth > dump.maxDepth) {
+                                       return "[object Object]";
+                               }
+
+                               dump.up();
+                               keys = [];
+                               for (key in map) {
+                                       keys.push(key);
+                               }
+
+                               // Some properties are not always enumerable on Error objects.
+                               nonEnumerableProperties = ["message", "name"];
+                               for (i in nonEnumerableProperties) {
+                                       key = nonEnumerableProperties[i];
+                                       if (key in map && !inArray(key, keys)) {
+                                               keys.push(key);
+                                       }
+                               }
+                               keys.sort();
+                               for (i = 0; i < keys.length; i++) {
+                                       key = keys[i];
+                                       val = map[key];
+                                       ret.push(dump.parse(key, "key") + ": " + dump.parse(val, undefined, stack));
+                               }
+                               dump.down();
+                               return join("{", ret, "}");
+                       },
+                       node: function node(_node) {
+                               var len,
+                                   i,
+                                   val,
+                                   open = dump.HTML ? "&lt;" : "<",
+                                   close = dump.HTML ? "&gt;" : ">",
+                                   tag = _node.nodeName.toLowerCase(),
+                                   ret = open + tag,
+                                   attrs = _node.attributes;
+
+                               if (attrs) {
+                                       for (i = 0, len = attrs.length; i < len; i++) {
+                                               val = attrs[i].nodeValue;
+
+                                               // IE6 includes all attributes in .attributes, even ones not explicitly
+                                               // set. Those have values like undefined, null, 0, false, "" or
+                                               // "inherit".
+                                               if (val && val !== "inherit") {
+                                                       ret += " " + attrs[i].nodeName + "=" + dump.parse(val, "attribute");
+                                               }
+                                       }
+                               }
+                               ret += close;
+
+                               // Show content of TextNode or CDATASection
+                               if (_node.nodeType === 3 || _node.nodeType === 4) {
+                                       ret += _node.nodeValue;
+                               }
+
+                               return ret + open + "/" + tag + close;
+                       },
+
+                       // Function calls it internally, it's the arguments part of the function
+                       functionArgs: function functionArgs(fn) {
+                               var args,
+                                   l = fn.length;
+
+                               if (!l) {
+                                       return "";
+                               }
+
+                               args = new Array(l);
+                               while (l--) {
+
+                                       // 97 is 'a'
+                                       args[l] = String.fromCharCode(97 + l);
+                               }
+                               return " " + args.join(", ") + " ";
+                       },
+
+                       // Object calls it internally, the key part of an item in a map
+                       key: quote,
+
+                       // Function calls it internally, it's the content of the function
+                       functionCode: "[code]",
+
+                       // Node calls it internally, it's a html attribute value
+                       attribute: quote,
+                       string: quote,
+                       date: quote,
+                       regexp: literal,
+                       number: literal,
+                       "boolean": literal,
+                       symbol: function symbol(sym) {
+                               return sym.toString();
+                       }
+               },
+
+               // If true, entities are escaped ( <, >, \t, space and \n )
+               HTML: false,
+
+               // Indentation unit
+               indentChar: "  ",
+
+               // If true, items in a collection, are separated by a \n, else just a space.
+               multiline: true
+       };
+
+       return dump;
+  })();
+
+  var LISTENERS = Object.create(null);
+  var SUPPORTED_EVENTS = ["runStart", "suiteStart", "testStart", "assertion", "testEnd", "suiteEnd", "runEnd"];
+
+  /**
+   * Emits an event with the specified data to all currently registered listeners.
+   * Callbacks will fire in the order in which they are registered (FIFO). This
+   * function is not exposed publicly; it is used by QUnit internals to emit
+   * logging events.
+   *
+   * @private
+   * @method emit
+   * @param {String} eventName
+   * @param {Object} data
+   * @return {Void}
+   */
+  function emit(eventName, data) {
+       if (objectType(eventName) !== "string") {
+               throw new TypeError("eventName must be a string when emitting an event");
+       }
+
+       // Clone the callbacks in case one of them registers a new callback
+       var originalCallbacks = LISTENERS[eventName];
+       var callbacks = originalCallbacks ? [].concat(toConsumableArray(originalCallbacks)) : [];
+
+       for (var i = 0; i < callbacks.length; i++) {
+               callbacks[i](data);
+       }
+  }
+
+  /**
+   * Registers a callback as a listener to the specified event.
+   *
+   * @public
+   * @method on
+   * @param {String} eventName
+   * @param {Function} callback
+   * @return {Void}
+   */
+  function on(eventName, callback) {
+       if (objectType(eventName) !== "string") {
+               throw new TypeError("eventName must be a string when registering a listener");
+       } else if (!inArray(eventName, SUPPORTED_EVENTS)) {
+               var events = SUPPORTED_EVENTS.join(", ");
+               throw new Error("\"" + eventName + "\" is not a valid event; must be one of: " + events + ".");
+       } else if (objectType(callback) !== "function") {
+               throw new TypeError("callback must be a function when registering a listener");
+       }
+
+       if (!LISTENERS[eventName]) {
+               LISTENERS[eventName] = [];
+       }
+
+       // Don't register the same callback more than once
+       if (!inArray(callback, LISTENERS[eventName])) {
+               LISTENERS[eventName].push(callback);
+       }
+  }
+
+  // Register logging callbacks
+  function registerLoggingCallbacks(obj) {
+       var i,
+           l,
+           key,
+           callbackNames = ["begin", "done", "log", "testStart", "testDone", "moduleStart", "moduleDone"];
+
+       function registerLoggingCallback(key) {
+               var loggingCallback = function loggingCallback(callback) {
+                       if (objectType(callback) !== "function") {
+                               throw new Error("QUnit logging methods require a callback function as their first parameters.");
+                       }
+
+                       config.callbacks[key].push(callback);
+               };
+
+               return loggingCallback;
+       }
+
+       for (i = 0, l = callbackNames.length; i < l; i++) {
+               key = callbackNames[i];
+
+               // Initialize key collection of logging callback
+               if (objectType(config.callbacks[key]) === "undefined") {
+                       config.callbacks[key] = [];
+               }
+
+               obj[key] = registerLoggingCallback(key);
+       }
+  }
+
+  function runLoggingCallbacks(key, args) {
+       var i, l, callbacks;
+
+       callbacks = config.callbacks[key];
+       for (i = 0, l = callbacks.length; i < l; i++) {
+               callbacks[i](args);
+       }
+  }
+
+  // Doesn't support IE9, it will return undefined on these browsers
+  // See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack
+  var fileName = (sourceFromStacktrace(0) || "").replace(/(:\d+)+\)?/, "").replace(/.+\//, "");
+
+  function extractStacktrace(e, offset) {
+       offset = offset === undefined ? 4 : offset;
+
+       var stack, include, i;
+
+       if (e && e.stack) {
+               stack = e.stack.split("\n");
+               if (/^error$/i.test(stack[0])) {
+                       stack.shift();
+               }
+               if (fileName) {
+                       include = [];
+                       for (i = offset; i < stack.length; i++) {
+                               if (stack[i].indexOf(fileName) !== -1) {
+                                       break;
+                               }
+                               include.push(stack[i]);
+                       }
+                       if (include.length) {
+                               return include.join("\n");
+                       }
+               }
+               return stack[offset];
+       }
+  }
+
+  function sourceFromStacktrace(offset) {
+       var error = new Error();
+
+       // Support: Safari <=7 only, IE <=10 - 11 only
+       // Not all browsers generate the `stack` property for `new Error()`, see also #636
+       if (!error.stack) {
+               try {
+                       throw error;
+               } catch (err) {
+                       error = err;
+               }
+       }
+
+       return extractStacktrace(error, offset);
+  }
+
+  var priorityCount = 0;
+  var unitSampler = void 0;
+
+  /**
+   * Advances the ProcessingQueue to the next item if it is ready.
+   * @param {Boolean} last
+   */
+  function advance() {
+       var start = now();
+       config.depth = (config.depth || 0) + 1;
+
+       while (config.queue.length && !config.blocking) {
+               var elapsedTime = now() - start;
+
+               if (!defined.setTimeout || config.updateRate <= 0 || elapsedTime < config.updateRate) {
+                       if (priorityCount > 0) {
+                               priorityCount--;
+                       }
+
+                       config.queue.shift()();
+               } else {
+                       setTimeout(advance, 13);
+                       break;
+               }
+       }
+
+       config.depth--;
+
+       if (!config.blocking && !config.queue.length && config.depth === 0) {
+               done();
+       }
+  }
+
+  function addToQueueImmediate(callback) {
+       if (objectType(callback) === "array") {
+               while (callback.length) {
+                       addToQueueImmediate(callback.pop());
+               }
+
+               return;
+       }
+
+       config.queue.unshift(callback);
+       priorityCount++;
+  }
+
+  /**
+   * Adds a function to the ProcessingQueue for execution.
+   * @param {Function|Array} callback
+   * @param {Boolean} priority
+   * @param {String} seed
+   */
+  function addToQueue(callback, prioritize, seed) {
+       if (prioritize) {
+               config.queue.splice(priorityCount++, 0, callback);
+       } else if (seed) {
+               if (!unitSampler) {
+                       unitSampler = unitSamplerGenerator(seed);
+               }
+
+               // Insert into a random position after all prioritized items
+               var index = Math.floor(unitSampler() * (config.queue.length - priorityCount + 1));
+               config.queue.splice(priorityCount + index, 0, callback);
+       } else {
+               config.queue.push(callback);
+       }
+  }
+
+  /**
+   * Creates a seeded "sample" generator which is used for randomizing tests.
+   */
+  function unitSamplerGenerator(seed) {
+
+       // 32-bit xorshift, requires only a nonzero seed
+       // http://excamera.com/sphinx/article-xorshift.html
+       var sample = parseInt(generateHash(seed), 16) || -1;
+       return function () {
+               sample ^= sample << 13;
+               sample ^= sample >>> 17;
+               sample ^= sample << 5;
+
+               // ECMAScript has no unsigned number type
+               if (sample < 0) {
+                       sample += 0x100000000;
+               }
+
+               return sample / 0x100000000;
+       };
+  }
+
+  /**
+   * This function is called when the ProcessingQueue is done processing all
+   * items. It handles emitting the final run events.
+   */
+  function done() {
+       var storage = config.storage;
+
+       ProcessingQueue.finished = true;
+
+       var runtime = now() - config.started;
+       var passed = config.stats.all - config.stats.bad;
+
+       emit("runEnd", globalSuite.end(true));
+       runLoggingCallbacks("done", {
+               passed: passed,
+               failed: config.stats.bad,
+               total: config.stats.all,
+               runtime: runtime
+       });
+
+       // Clear own storage items if all tests passed
+       if (storage && config.stats.bad === 0) {
+               for (var i = storage.length - 1; i >= 0; i--) {
+                       var key = storage.key(i);
+
+                       if (key.indexOf("qunit-test-") === 0) {
+                               storage.removeItem(key);
+                       }
+               }
+       }
+  }
+
+  var ProcessingQueue = {
+       finished: false,
+       add: addToQueue,
+       addImmediate: addToQueueImmediate,
+       advance: advance
+  };
+
+  var TestReport = function () {
+       function TestReport(name, suite, options) {
+               classCallCheck(this, TestReport);
+
+               this.name = name;
+               this.suiteName = suite.name;
+               this.fullName = suite.fullName.concat(name);
+               this.runtime = 0;
+               this.assertions = [];
+
+               this.skipped = !!options.skip;
+               this.todo = !!options.todo;
+
+               this.valid = options.valid;
+
+               this._startTime = 0;
+               this._endTime = 0;
+
+               suite.pushTest(this);
+       }
+
+       createClass(TestReport, [{
+               key: "start",
+               value: function start(recordTime) {
+                       if (recordTime) {
+                               this._startTime = Date.now();
+                       }
+
+                       return {
+                               name: this.name,
+                               suiteName: this.suiteName,
+                               fullName: this.fullName.slice()
+                       };
+               }
+       }, {
+               key: "end",
+               value: function end(recordTime) {
+                       if (recordTime) {
+                               this._endTime = Date.now();
+                       }
+
+                       return extend(this.start(), {
+                               runtime: this.getRuntime(),
+                               status: this.getStatus(),
+                               errors: this.getFailedAssertions(),
+                               assertions: this.getAssertions()
+                       });
+               }
+       }, {
+               key: "pushAssertion",
+               value: function pushAssertion(assertion) {
+                       this.assertions.push(assertion);
+               }
+       }, {
+               key: "getRuntime",
+               value: function getRuntime() {
+                       return this._endTime - this._startTime;
+               }
+       }, {
+               key: "getStatus",
+               value: function getStatus() {
+                       if (this.skipped) {
+                               return "skipped";
+                       }
+
+                       var testPassed = this.getFailedAssertions().length > 0 ? this.todo : !this.todo;
+
+                       if (!testPassed) {
+                               return "failed";
+                       } else if (this.todo) {
+                               return "todo";
+                       } else {
+                               return "passed";
+                       }
+               }
+       }, {
+               key: "getFailedAssertions",
+               value: function getFailedAssertions() {
+                       return this.assertions.filter(function (assertion) {
+                               return !assertion.passed;
+                       });
+               }
+       }, {
+               key: "getAssertions",
+               value: function getAssertions() {
+                       return this.assertions.slice();
+               }
+
+               // Remove actual and expected values from assertions. This is to prevent
+               // leaking memory throughout a test suite.
+
+       }, {
+               key: "slimAssertions",
+               value: function slimAssertions() {
+                       this.assertions = this.assertions.map(function (assertion) {
+                               delete assertion.actual;
+                               delete assertion.expected;
+                               return assertion;
+                       });
+               }
+       }]);
+       return TestReport;
+  }();
+
+  var focused$1 = false;
+
+  function Test(settings) {
+       var i, l;
+
+       ++Test.count;
+
+       this.expected = null;
+       this.assertions = [];
+       this.semaphore = 0;
+       this.module = config.currentModule;
+       this.stack = sourceFromStacktrace(3);
+       this.steps = [];
+       this.timeout = undefined;
+
+       // If a module is skipped, all its tests and the tests of the child suites
+       // should be treated as skipped even if they are defined as `only` or `todo`.
+       // As for `todo` module, all its tests will be treated as `todo` except for
+       // tests defined as `skip` which will be left intact.
+       //
+       // So, if a test is defined as `todo` and is inside a skipped module, we should
+       // then treat that test as if was defined as `skip`.
+       if (this.module.skip) {
+               settings.skip = true;
+               settings.todo = false;
+
+               // Skipped tests should be left intact
+       } else if (this.module.todo && !settings.skip) {
+               settings.todo = true;
+       }
+
+       extend(this, settings);
+
+       this.testReport = new TestReport(settings.testName, this.module.suiteReport, {
+               todo: settings.todo,
+               skip: settings.skip,
+               valid: this.valid()
+       });
+
+       // Register unique strings
+       for (i = 0, l = this.module.tests; i < l.length; i++) {
+               if (this.module.tests[i].name === this.testName) {
+                       this.testName += " ";
+               }
+       }
+
+       this.testId = generateHash(this.module.name, this.testName);
+
+       this.module.tests.push({
+               name: this.testName,
+               testId: this.testId,
+               skip: !!settings.skip
+       });
+
+       if (settings.skip) {
+
+               // Skipped tests will fully ignore any sent callback
+               this.callback = function () {};
+               this.async = false;
+               this.expected = 0;
+       } else {
+               this.assert = new Assert(this);
+       }
+  }
+
+  Test.count = 0;
+
+  function getNotStartedModules(startModule) {
+       var module = startModule,
+           modules = [];
+
+       while (module && module.testsRun === 0) {
+               modules.push(module);
+               module = module.parentModule;
+       }
+
+       return modules;
+  }
+
+  Test.prototype = {
+       before: function before() {
+               var i,
+                   startModule,
+                   module = this.module,
+                   notStartedModules = getNotStartedModules(module);
+
+               for (i = notStartedModules.length - 1; i >= 0; i--) {
+                       startModule = notStartedModules[i];
+                       startModule.stats = { all: 0, bad: 0, started: now() };
+                       emit("suiteStart", startModule.suiteReport.start(true));
+                       runLoggingCallbacks("moduleStart", {
+                               name: startModule.name,
+                               tests: startModule.tests
+                       });
+               }
+
+               config.current = this;
+
+               this.testEnvironment = extend({}, module.testEnvironment);
+
+               this.started = now();
+               emit("testStart", this.testReport.start(true));
+               runLoggingCallbacks("testStart", {
+                       name: this.testName,
+                       module: module.name,
+                       testId: this.testId,
+                       previousFailure: this.previousFailure
+               });
+
+               if (!config.pollution) {
+                       saveGlobal();
+               }
+       },
+
+       run: function run() {
+               var promise;
+
+               config.current = this;
+
+               this.callbackStarted = now();
+
+               if (config.notrycatch) {
+                       runTest(this);
+                       return;
+               }
+
+               try {
+                       runTest(this);
+               } catch (e) {
+                       this.pushFailure("Died on test #" + (this.assertions.length + 1) + " " + this.stack + ": " + (e.message || e), extractStacktrace(e, 0));
+
+                       // Else next test will carry the responsibility
+                       saveGlobal();
+
+                       // Restart the tests if they're blocking
+                       if (config.blocking) {
+                               internalRecover(this);
+                       }
+               }
+
+               function runTest(test) {
+                       promise = test.callback.call(test.testEnvironment, test.assert);
+                       test.resolvePromise(promise);
+
+                       // If the test has a "lock" on it, but the timeout is 0, then we push a
+                       // failure as the test should be synchronous.
+                       if (test.timeout === 0 && test.semaphore !== 0) {
+                               pushFailure("Test did not finish synchronously even though assert.timeout( 0 ) was used.", sourceFromStacktrace(2));
+                       }
+               }
+       },
+
+       after: function after() {
+               checkPollution();
+       },
+
+       queueHook: function queueHook(hook, hookName, hookOwner) {
+               var _this = this;
+
+               var callHook = function callHook() {
+                       var promise = hook.call(_this.testEnvironment, _this.assert);
+                       _this.resolvePromise(promise, hookName);
+               };
+
+               var runHook = function runHook() {
+                       if (hookName === "before") {
+                               if (hookOwner.unskippedTestsRun !== 0) {
+                                       return;
+                               }
+
+                               _this.preserveEnvironment = true;
+                       }
+
+                       if (hookName === "after" && hookOwner.unskippedTestsRun !== numberOfUnskippedTests(hookOwner) - 1 && config.queue.length > 2) {
+                               return;
+                       }
+
+                       config.current = _this;
+                       if (config.notrycatch) {
+                               callHook();
+                               return;
+                       }
+                       try {
+                               callHook();
+                       } catch (error) {
+                               _this.pushFailure(hookName + " failed on " + _this.testName + ": " + (error.message || error), extractStacktrace(error, 0));
+                       }
+               };
+
+               return runHook;
+       },
+
+
+       // Currently only used for module level hooks, can be used to add global level ones
+       hooks: function hooks(handler) {
+               var hooks = [];
+
+               function processHooks(test, module) {
+                       if (module.parentModule) {
+                               processHooks(test, module.parentModule);
+                       }
+
+                       if (module.hooks[handler].length) {
+                               for (var i = 0; i < module.hooks[handler].length; i++) {
+                                       hooks.push(test.queueHook(module.hooks[handler][i], handler, module));
+                               }
+                       }
+               }
+
+               // Hooks are ignored on skipped tests
+               if (!this.skip) {
+                       processHooks(this, this.module);
+               }
+
+               return hooks;
+       },
+
+
+       finish: function finish() {
+               config.current = this;
+               if (config.requireExpects && this.expected === null) {
+                       this.pushFailure("Expected number of assertions to be defined, but expect() was " + "not called.", this.stack);
+               } else if (this.expected !== null && this.expected !== this.assertions.length) {
+                       this.pushFailure("Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack);
+               } else if (this.expected === null && !this.assertions.length) {
+                       this.pushFailure("Expected at least one assertion, but none were run - call " + "expect(0) to accept zero assertions.", this.stack);
+               }
+
+               var i,
+                   module = this.module,
+                   moduleName = module.name,
+                   testName = this.testName,
+                   skipped = !!this.skip,
+                   todo = !!this.todo,
+                   bad = 0,
+                   storage = config.storage;
+
+               this.runtime = now() - this.started;
+
+               config.stats.all += this.assertions.length;
+               module.stats.all += this.assertions.length;
+
+               for (i = 0; i < this.assertions.length; i++) {
+                       if (!this.assertions[i].result) {
+                               bad++;
+                               config.stats.bad++;
+                               module.stats.bad++;
+                       }
+               }
+
+               notifyTestsRan(module, skipped);
+
+               // Store result when possible
+               if (storage) {
+                       if (bad) {
+                               storage.setItem("qunit-test-" + moduleName + "-" + testName, bad);
+                       } else {
+                               storage.removeItem("qunit-test-" + moduleName + "-" + testName);
+                       }
+               }
+
+               // After emitting the js-reporters event we cleanup the assertion data to
+               // avoid leaking it. It is not used by the legacy testDone callbacks.
+               emit("testEnd", this.testReport.end(true));
+               this.testReport.slimAssertions();
+
+               runLoggingCallbacks("testDone", {
+                       name: testName,
+                       module: moduleName,
+                       skipped: skipped,
+                       todo: todo,
+                       failed: bad,
+                       passed: this.assertions.length - bad,
+                       total: this.assertions.length,
+                       runtime: skipped ? 0 : this.runtime,
+
+                       // HTML Reporter use
+                       assertions: this.assertions,
+                       testId: this.testId,
+
+                       // Source of Test
+                       source: this.stack
+               });
+
+               if (module.testsRun === numberOfTests(module)) {
+                       logSuiteEnd(module);
+
+                       // Check if the parent modules, iteratively, are done. If that the case,
+                       // we emit the `suiteEnd` event and trigger `moduleDone` callback.
+                       var parent = module.parentModule;
+                       while (parent && parent.testsRun === numberOfTests(parent)) {
+                               logSuiteEnd(parent);
+                               parent = parent.parentModule;
+                       }
+               }
+
+               config.current = undefined;
+
+               function logSuiteEnd(module) {
+                       emit("suiteEnd", module.suiteReport.end(true));
+                       runLoggingCallbacks("moduleDone", {
+                               name: module.name,
+                               tests: module.tests,
+                               failed: module.stats.bad,
+                               passed: module.stats.all - module.stats.bad,
+                               total: module.stats.all,
+                               runtime: now() - module.stats.started
+                       });
+               }
+       },
+
+       preserveTestEnvironment: function preserveTestEnvironment() {
+               if (this.preserveEnvironment) {
+                       this.module.testEnvironment = this.testEnvironment;
+                       this.testEnvironment = extend({}, this.module.testEnvironment);
+               }
+       },
+
+       queue: function queue() {
+               var test = this;
+
+               if (!this.valid()) {
+                       return;
+               }
+
+               function runTest() {
+
+                       // Each of these can by async
+                       ProcessingQueue.addImmediate([function () {
+                               test.before();
+                       }, test.hooks("before"), function () {
+                               test.preserveTestEnvironment();
+                       }, test.hooks("beforeEach"), function () {
+                               test.run();
+                       }, test.hooks("afterEach").reverse(), test.hooks("after").reverse(), function () {
+                               test.after();
+                       }, function () {
+                               test.finish();
+                       }]);
+               }
+
+               var previousFailCount = config.storage && +config.storage.getItem("qunit-test-" + this.module.name + "-" + this.testName);
+
+               // Prioritize previously failed tests, detected from storage
+               var prioritize = config.reorder && !!previousFailCount;
+
+               this.previousFailure = !!previousFailCount;
+
+               ProcessingQueue.add(runTest, prioritize, config.seed);
+
+               // If the queue has already finished, we manually process the new test
+               if (ProcessingQueue.finished) {
+                       ProcessingQueue.advance();
+               }
+       },
+
+
+       pushResult: function pushResult(resultInfo) {
+               if (this !== config.current) {
+                       throw new Error("Assertion occured after test had finished.");
+               }
+
+               // Destructure of resultInfo = { result, actual, expected, message, negative }
+               var source,
+                   details = {
+                       module: this.module.name,
+                       name: this.testName,
+                       result: resultInfo.result,
+                       message: resultInfo.message,
+                       actual: resultInfo.actual,
+                       expected: resultInfo.expected,
+                       testId: this.testId,
+                       negative: resultInfo.negative || false,
+                       runtime: now() - this.started,
+                       todo: !!this.todo
+               };
+
+               if (!resultInfo.result) {
+                       source = resultInfo.source || sourceFromStacktrace();
+
+                       if (source) {
+                               details.source = source;
+                       }
+               }
+
+               this.logAssertion(details);
+
+               this.assertions.push({
+                       result: !!resultInfo.result,
+                       message: resultInfo.message
+               });
+       },
+
+       pushFailure: function pushFailure(message, source, actual) {
+               if (!(this instanceof Test)) {
+                       throw new Error("pushFailure() assertion outside test context, was " + sourceFromStacktrace(2));
+               }
+
+               this.pushResult({
+                       result: false,
+                       message: message || "error",
+                       actual: actual || null,
+                       expected: null,
+                       source: source
+               });
+       },
+
+       /**
+    * Log assertion details using both the old QUnit.log interface and
+    * QUnit.on( "assertion" ) interface.
+    *
+    * @private
+    */
+       logAssertion: function logAssertion(details) {
+               runLoggingCallbacks("log", details);
+
+               var assertion = {
+                       passed: details.result,
+                       actual: details.actual,
+                       expected: details.expected,
+                       message: details.message,
+                       stack: details.source,
+                       todo: details.todo
+               };
+               this.testReport.pushAssertion(assertion);
+               emit("assertion", assertion);
+       },
+
+
+       resolvePromise: function resolvePromise(promise, phase) {
+               var then,
+                   resume,
+                   message,
+                   test = this;
+               if (promise != null) {
+                       then = promise.then;
+                       if (objectType(then) === "function") {
+                               resume = internalStop(test);
+                               then.call(promise, function () {
+                                       resume();
+                               }, function (error) {
+                                       message = "Promise rejected " + (!phase ? "during" : phase.replace(/Each$/, "")) + " \"" + test.testName + "\": " + (error && error.message || error);
+                                       test.pushFailure(message, extractStacktrace(error, 0));
+
+                                       // Else next test will carry the responsibility
+                                       saveGlobal();
+
+                                       // Unblock
+                                       resume();
+                               });
+                       }
+               }
+       },
+
+       valid: function valid() {
+               var filter = config.filter,
+                   regexFilter = /^(!?)\/([\w\W]*)\/(i?$)/.exec(filter),
+                   module = config.module && config.module.toLowerCase(),
+                   fullName = this.module.name + ": " + this.testName;
+
+               function moduleChainNameMatch(testModule) {
+                       var testModuleName = testModule.name ? testModule.name.toLowerCase() : null;
+                       if (testModuleName === module) {
+                               return true;
+                       } else if (testModule.parentModule) {
+                               return moduleChainNameMatch(testModule.parentModule);
+                       } else {
+                               return false;
+                       }
+               }
+
+               function moduleChainIdMatch(testModule) {
+                       return inArray(testModule.moduleId, config.moduleId) || testModule.parentModule && moduleChainIdMatch(testModule.parentModule);
+               }
+
+               // Internally-generated tests are always valid
+               if (this.callback && this.callback.validTest) {
+                       return true;
+               }
+
+               if (config.moduleId && config.moduleId.length > 0 && !moduleChainIdMatch(this.module)) {
+
+                       return false;
+               }
+
+               if (config.testId && config.testId.length > 0 && !inArray(this.testId, config.testId)) {
+
+                       return false;
+               }
+
+               if (module && !moduleChainNameMatch(this.module)) {
+                       return false;
+               }
+
+               if (!filter) {
+                       return true;
+               }
+
+               return regexFilter ? this.regexFilter(!!regexFilter[1], regexFilter[2], regexFilter[3], fullName) : this.stringFilter(filter, fullName);
+       },
+
+       regexFilter: function regexFilter(exclude, pattern, flags, fullName) {
+               var regex = new RegExp(pattern, flags);
+               var match = regex.test(fullName);
+
+               return match !== exclude;
+       },
+
+       stringFilter: function stringFilter(filter, fullName) {
+               filter = filter.toLowerCase();
+               fullName = fullName.toLowerCase();
+
+               var include = filter.charAt(0) !== "!";
+               if (!include) {
+                       filter = filter.slice(1);
+               }
+
+               // If the filter matches, we need to honour include
+               if (fullName.indexOf(filter) !== -1) {
+                       return include;
+               }
+
+               // Otherwise, do the opposite
+               return !include;
+       }
+  };
+
+  function pushFailure() {
+       if (!config.current) {
+               throw new Error("pushFailure() assertion outside test context, in " + sourceFromStacktrace(2));
+       }
+
+       // Gets current test obj
+       var currentTest = config.current;
+
+       return currentTest.pushFailure.apply(currentTest, arguments);
+  }
+
+  function saveGlobal() {
+       config.pollution = [];
+
+       if (config.noglobals) {
+               for (var key in global$1) {
+                       if (hasOwn.call(global$1, key)) {
+
+                               // In Opera sometimes DOM element ids show up here, ignore them
+                               if (/^qunit-test-output/.test(key)) {
+                                       continue;
+                               }
+                               config.pollution.push(key);
+                       }
+               }
+       }
+  }
+
+  function checkPollution() {
+       var newGlobals,
+           deletedGlobals,
+           old = config.pollution;
+
+       saveGlobal();
+
+       newGlobals = diff(config.pollution, old);
+       if (newGlobals.length > 0) {
+               pushFailure("Introduced global variable(s): " + newGlobals.join(", "));
+       }
+
+       deletedGlobals = diff(old, config.pollution);
+       if (deletedGlobals.length > 0) {
+               pushFailure("Deleted global variable(s): " + deletedGlobals.join(", "));
+       }
+  }
+
+  // Will be exposed as QUnit.test
+  function test(testName, callback) {
+       if (focused$1) {
+               return;
+       }
+
+       var newTest = new Test({
+               testName: testName,
+               callback: callback
+       });
+
+       newTest.queue();
+  }
+
+  function todo(testName, callback) {
+       if (focused$1) {
+               return;
+       }
+
+       var newTest = new Test({
+               testName: testName,
+               callback: callback,
+               todo: true
+       });
+
+       newTest.queue();
+  }
+
+  // Will be exposed as QUnit.skip
+  function skip(testName) {
+       if (focused$1) {
+               return;
+       }
+
+       var test = new Test({
+               testName: testName,
+               skip: true
+       });
+
+       test.queue();
+  }
+
+  // Will be exposed as QUnit.only
+  function only(testName, callback) {
+       if (focused$1) {
+               return;
+       }
+
+       config.queue.length = 0;
+       focused$1 = true;
+
+       var newTest = new Test({
+               testName: testName,
+               callback: callback
+       });
+
+       newTest.queue();
+  }
+
+  // Put a hold on processing and return a function that will release it.
+  function internalStop(test) {
+       test.semaphore += 1;
+       config.blocking = true;
+
+       // Set a recovery timeout, if so configured.
+       if (defined.setTimeout) {
+               var timeoutDuration = void 0;
+
+               if (typeof test.timeout === "number") {
+                       timeoutDuration = test.timeout;
+               } else if (typeof config.testTimeout === "number") {
+                       timeoutDuration = config.testTimeout;
+               }
+
+               if (typeof timeoutDuration === "number" && timeoutDuration > 0) {
+                       clearTimeout(config.timeout);
+                       config.timeout = setTimeout(function () {
+                               pushFailure("Test took longer than " + timeoutDuration + "ms; test timed out.", sourceFromStacktrace(2));
+                               internalRecover(test);
+                       }, timeoutDuration);
+               }
+       }
+
+       var released = false;
+       return function resume() {
+               if (released) {
+                       return;
+               }
+
+               released = true;
+               test.semaphore -= 1;
+               internalStart(test);
+       };
+  }
+
+  // Forcefully release all processing holds.
+  function internalRecover(test) {
+       test.semaphore = 0;
+       internalStart(test);
+  }
+
+  // Release a processing hold, scheduling a resumption attempt if no holds remain.
+  function internalStart(test) {
+
+       // If semaphore is non-numeric, throw error
+       if (isNaN(test.semaphore)) {
+               test.semaphore = 0;
+
+               pushFailure("Invalid value on test.semaphore", sourceFromStacktrace(2));
+               return;
+       }
+
+       // Don't start until equal number of stop-calls
+       if (test.semaphore > 0) {
+               return;
+       }
+
+       // Throw an Error if start is called more often than stop
+       if (test.semaphore < 0) {
+               test.semaphore = 0;
+
+               pushFailure("Tried to restart test while already started (test's semaphore was 0 already)", sourceFromStacktrace(2));
+               return;
+       }
+
+       // Add a slight delay to allow more assertions etc.
+       if (defined.setTimeout) {
+               if (config.timeout) {
+                       clearTimeout(config.timeout);
+               }
+               config.timeout = setTimeout(function () {
+                       if (test.semaphore > 0) {
+                               return;
+                       }
+
+                       if (config.timeout) {
+                               clearTimeout(config.timeout);
+                       }
+
+                       begin();
+               }, 13);
+       } else {
+               begin();
+       }
+  }
+
+  function collectTests(module) {
+       var tests = [].concat(module.tests);
+       var modules = [].concat(toConsumableArray(module.childModules));
+
+       // Do a breadth-first traversal of the child modules
+       while (modules.length) {
+               var nextModule = modules.shift();
+               tests.push.apply(tests, nextModule.tests);
+               modules.push.apply(modules, toConsumableArray(nextModule.childModules));
+       }
+
+       return tests;
+  }
+
+  function numberOfTests(module) {
+       return collectTests(module).length;
+  }
+
+  function numberOfUnskippedTests(module) {
+       return collectTests(module).filter(function (test) {
+               return !test.skip;
+       }).length;
+  }
+
+  function notifyTestsRan(module, skipped) {
+       module.testsRun++;
+       if (!skipped) {
+               module.unskippedTestsRun++;
+       }
+       while (module = module.parentModule) {
+               module.testsRun++;
+               if (!skipped) {
+                       module.unskippedTestsRun++;
+               }
+       }
+  }
+
+  /**
+   * Returns a function that proxies to the given method name on the globals
+   * console object. The proxy will also detect if the console doesn't exist and
+   * will appropriately no-op. This allows support for IE9, which doesn't have a
+   * console if the developer tools are not open.
+   */
+  function consoleProxy(method) {
+       return function () {
+               if (console) {
+                       console[method].apply(console, arguments);
+               }
+       };
+  }
+
+  var Logger = {
+       warn: consoleProxy("warn")
+  };
+
+  var Assert = function () {
+       function Assert(testContext) {
+               classCallCheck(this, Assert);
+
+               this.test = testContext;
+       }
+
+       // Assert helpers
+
+       createClass(Assert, [{
+               key: "timeout",
+               value: function timeout(duration) {
+                       if (typeof duration !== "number") {
+                               throw new Error("You must pass a number as the duration to assert.timeout");
+                       }
+
+                       this.test.timeout = duration;
+               }
+
+               // Documents a "step", which is a string value, in a test as a passing assertion
+
+       }, {
+               key: "step",
+               value: function step(message) {
+                       var result = !!message;
+
+                       this.test.steps.push(message);
+
+                       return this.pushResult({
+                               result: result,
+                               message: message || "You must provide a message to assert.step"
+                       });
+               }
+
+               // Verifies the steps in a test match a given array of string values
+
+       }, {
+               key: "verifySteps",
+               value: function verifySteps(steps, message) {
+                       this.deepEqual(this.test.steps, steps, message);
+               }
+
+               // Specify the number of expected assertions to guarantee that failed test
+               // (no assertions are run at all) don't slip through.
+
+       }, {
+               key: "expect",
+               value: function expect(asserts) {
+                       if (arguments.length === 1) {
+                               this.test.expected = asserts;
+                       } else {
+                               return this.test.expected;
+                       }
+               }
+
+               // Put a hold on processing and return a function that will release it a maximum of once.
+
+       }, {
+               key: "async",
+               value: function async(count) {
+                       var test$$1 = this.test;
+
+                       var popped = false,
+                           acceptCallCount = count;
+
+                       if (typeof acceptCallCount === "undefined") {
+                               acceptCallCount = 1;
+                       }
+
+                       var resume = internalStop(test$$1);
+
+                       return function done() {
+                               if (config.current !== test$$1) {
+                                       throw Error("assert.async callback called after test finished.");
+                               }
+
+                               if (popped) {
+                                       test$$1.pushFailure("Too many calls to the `assert.async` callback", sourceFromStacktrace(2));
+                                       return;
+                               }
+
+                               acceptCallCount -= 1;
+                               if (acceptCallCount > 0) {
+                                       return;
+                               }
+
+                               popped = true;
+                               resume();
+                       };
+               }
+
+               // Exports test.push() to the user API
+               // Alias of pushResult.
+
+       }, {
+               key: "push",
+               value: function push(result, actual, expected, message, negative) {
+                       Logger.warn("assert.push is deprecated and will be removed in QUnit 3.0." + " Please use assert.pushResult instead (https://api.qunitjs.com/assert/pushResult).");
+
+                       var currentAssert = this instanceof Assert ? this : config.current.assert;
+                       return currentAssert.pushResult({
+                               result: result,
+                               actual: actual,
+                               expected: expected,
+                               message: message,
+                               negative: negative
+                       });
+               }
+       }, {
+               key: "pushResult",
+               value: function pushResult(resultInfo) {
+
+                       // Destructure of resultInfo = { result, actual, expected, message, negative }
+                       var assert = this;
+                       var currentTest = assert instanceof Assert && assert.test || config.current;
+
+                       // Backwards compatibility fix.
+                       // Allows the direct use of global exported assertions and QUnit.assert.*
+                       // Although, it's use is not recommended as it can leak assertions
+                       // to other tests from async tests, because we only get a reference to the current test,
+                       // not exactly the test where assertion were intended to be called.
+                       if (!currentTest) {
+                               throw new Error("assertion outside test context, in " + sourceFromStacktrace(2));
+                       }
+
+                       if (!(assert instanceof Assert)) {
+                               assert = currentTest.assert;
+                       }
+
+                       return assert.test.pushResult(resultInfo);
+               }
+       }, {
+               key: "ok",
+               value: function ok(result, message) {
+                       if (!message) {
+                               message = result ? "okay" : "failed, expected argument to be truthy, was: " + dump.parse(result);
+                       }
+
+                       this.pushResult({
+                               result: !!result,
+                               actual: result,
+                               expected: true,
+                               message: message
+                       });
+               }
+       }, {
+               key: "notOk",
+               value: function notOk(result, message) {
+                       if (!message) {
+                               message = !result ? "okay" : "failed, expected argument to be falsy, was: " + dump.parse(result);
+                       }
+
+                       this.pushResult({
+                               result: !result,
+                               actual: result,
+                               expected: false,
+                               message: message
+                       });
+               }
+       }, {
+               key: "equal",
+               value: function equal(actual, expected, message) {
+
+                       // eslint-disable-next-line eqeqeq
+                       var result = expected == actual;
+
+                       this.pushResult({
+                               result: result,
+                               actual: actual,
+                               expected: expected,
+                               message: message
+                       });
+               }
+       }, {
+               key: "notEqual",
+               value: function notEqual(actual, expected, message) {
+
+                       // eslint-disable-next-line eqeqeq
+                       var result = expected != actual;
+
+                       this.pushResult({
+                               result: result,
+                               actual: actual,
+                               expected: expected,
+                               message: message,
+                               negative: true
+                       });
+               }
+       }, {
+               key: "propEqual",
+               value: function propEqual(actual, expected, message) {
+                       actual = objectValues(actual);
+                       expected = objectValues(expected);
+
+                       this.pushResult({
+                               result: equiv(actual, expected),
+                               actual: actual,
+                               expected: expected,
+                               message: message
+                       });
+               }
+       }, {
+               key: "notPropEqual",
+               value: function notPropEqual(actual, expected, message) {
+                       actual = objectValues(actual);
+                       expected = objectValues(expected);
+
+                       this.pushResult({
+                               result: !equiv(actual, expected),
+                               actual: actual,
+                               expected: expected,
+                               message: message,
+                               negative: true
+                       });
+               }
+       }, {
+               key: "deepEqual",
+               value: function deepEqual(actual, expected, message) {
+                       this.pushResult({
+                               result: equiv(actual, expected),
+                               actual: actual,
+                               expected: expected,
+                               message: message
+                       });
+               }
+       }, {
+               key: "notDeepEqual",
+               value: function notDeepEqual(actual, expected, message) {
+                       this.pushResult({
+                               result: !equiv(actual, expected),
+                               actual: actual,
+                               expected: expected,
+                               message: message,
+                               negative: true
+                       });
+               }
+       }, {
+               key: "strictEqual",
+               value: function strictEqual(actual, expected, message) {
+                       this.pushResult({
+                               result: expected === actual,
+                               actual: actual,
+                               expected: expected,
+                               message: message
+                       });
+               }
+       }, {
+               key: "notStrictEqual",
+               value: function notStrictEqual(actual, expected, message) {
+                       this.pushResult({
+                               result: expected !== actual,
+                               actual: actual,
+                               expected: expected,
+                               message: message,
+                               negative: true
+                       });
+               }
+       }, {
+               key: "throws",
+               value: function throws(block, expected, message) {
+                       var actual = void 0,
+                           result = false;
+
+                       var currentTest = this instanceof Assert && this.test || config.current;
+
+                       // 'expected' is optional unless doing string comparison
+                       if (objectType(expected) === "string") {
+                               if (message == null) {
+                                       message = expected;
+                                       expected = null;
+                               } else {
+                                       throw new Error("throws/raises does not accept a string value for the expected argument.\n" + "Use a non-string object value (e.g. regExp) instead if it's necessary.");
+                               }
+                       }
+
+                       currentTest.ignoreGlobalErrors = true;
+                       try {
+                               block.call(currentTest.testEnvironment);
+                       } catch (e) {
+                               actual = e;
+                       }
+                       currentTest.ignoreGlobalErrors = false;
+
+                       if (actual) {
+                               var expectedType = objectType(expected);
+
+                               // We don't want to validate thrown error
+                               if (!expected) {
+                                       result = true;
+                                       expected = null;
+
+                                       // Expected is a regexp
+                               } else if (expectedType === "regexp") {
+                                       result = expected.test(errorString(actual));
+
+                                       // Expected is a constructor, maybe an Error constructor
+                               } else if (expectedType === "function" && actual instanceof expected) {
+                                       result = true;
+
+                                       // Expected is an Error object
+                               } else if (expectedType === "object") {
+                                       result = actual instanceof expected.constructor && actual.name === expected.name && actual.message === expected.message;
+
+                                       // Expected is a validation function which returns true if validation passed
+                               } else if (expectedType === "function" && expected.call({}, actual) === true) {
+                                       expected = null;
+                                       result = true;
+                               }
+                       }
+
+                       currentTest.assert.pushResult({
+                               result: result,
+                               actual: actual,
+                               expected: expected,
+                               message: message
+                       });
+               }
+       }]);
+       return Assert;
+  }();
+
+  // Provide an alternative to assert.throws(), for environments that consider throws a reserved word
+  // Known to us are: Closure Compiler, Narwhal
+  // eslint-disable-next-line dot-notation
+
+
+  Assert.prototype.raises = Assert.prototype["throws"];
+
+  /**
+   * Converts an error into a simple string for comparisons.
+   *
+   * @param {Error} error
+   * @return {String}
+   */
+  function errorString(error) {
+       var resultErrorString = error.toString();
+
+       if (resultErrorString.substring(0, 7) === "[object") {
+               var name = error.name ? error.name.toString() : "Error";
+               var message = error.message ? error.message.toString() : "";
+
+               if (name && message) {
+                       return name + ": " + message;
+               } else if (name) {
+                       return name;
+               } else if (message) {
+                       return message;
+               } else {
+                       return "Error";
+               }
+       } else {
+               return resultErrorString;
+       }
+  }
+
+  /* global module, exports, define */
+  function exportQUnit(QUnit) {
+
+       if (defined.document) {
+
+               // QUnit may be defined when it is preconfigured but then only QUnit and QUnit.config may be defined.
+               if (window.QUnit && window.QUnit.version) {
+                       throw new Error("QUnit has already been defined.");
+               }
+
+               window.QUnit = QUnit;
+       }
+
+       // For nodejs
+       if (typeof module !== "undefined" && module && module.exports) {
+               module.exports = QUnit;
+
+               // For consistency with CommonJS environments' exports
+               module.exports.QUnit = QUnit;
+       }
+
+       // For CommonJS with exports, but without module.exports, like Rhino
+       if (typeof exports !== "undefined" && exports) {
+               exports.QUnit = QUnit;
+       }
+
+       if (typeof define === "function" && define.amd) {
+               define(function () {
+                       return QUnit;
+               });
+               QUnit.config.autostart = false;
+       }
+
+       // For Web/Service Workers
+       if (self$1 && self$1.WorkerGlobalScope && self$1 instanceof self$1.WorkerGlobalScope) {
+               self$1.QUnit = QUnit;
+       }
+  }
+
+  var SuiteReport = function () {
+       function SuiteReport(name, parentSuite) {
+               classCallCheck(this, SuiteReport);
+
+               this.name = name;
+               this.fullName = parentSuite ? parentSuite.fullName.concat(name) : [];
+
+               this.tests = [];
+               this.childSuites = [];
+
+               if (parentSuite) {
+                       parentSuite.pushChildSuite(this);
+               }
+       }
+
+       createClass(SuiteReport, [{
+               key: "start",
+               value: function start(recordTime) {
+                       if (recordTime) {
+                               this._startTime = Date.now();
+                       }
+
+                       return {
+                               name: this.name,
+                               fullName: this.fullName.slice(),
+                               tests: this.tests.map(function (test) {
+                                       return test.start();
+                               }),
+                               childSuites: this.childSuites.map(function (suite) {
+                                       return suite.start();
+                               }),
+                               testCounts: {
+                                       total: this.getTestCounts().total
+                               }
+                       };
+               }
+       }, {
+               key: "end",
+               value: function end(recordTime) {
+                       if (recordTime) {
+                               this._endTime = Date.now();
+                       }
+
+                       return {
+                               name: this.name,
+                               fullName: this.fullName.slice(),
+                               tests: this.tests.map(function (test) {
+                                       return test.end();
+                               }),
+                               childSuites: this.childSuites.map(function (suite) {
+                                       return suite.end();
+                               }),
+                               testCounts: this.getTestCounts(),
+                               runtime: this.getRuntime(),
+                               status: this.getStatus()
+                       };
+               }
+       }, {
+               key: "pushChildSuite",
+               value: function pushChildSuite(suite) {
+                       this.childSuites.push(suite);
+               }
+       }, {
+               key: "pushTest",
+               value: function pushTest(test) {
+                       this.tests.push(test);
+               }
+       }, {
+               key: "getRuntime",
+               value: function getRuntime() {
+                       return this._endTime - this._startTime;
+               }
+       }, {
+               key: "getTestCounts",
+               value: function getTestCounts() {
+                       var counts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : { passed: 0, failed: 0, skipped: 0, todo: 0, total: 0 };
+
+                       counts = this.tests.reduce(function (counts, test) {
+                               if (test.valid) {
+                                       counts[test.getStatus()]++;
+                                       counts.total++;
+                               }
+
+                               return counts;
+                       }, counts);
+
+                       return this.childSuites.reduce(function (counts, suite) {
+                               return suite.getTestCounts(counts);
+                       }, counts);
+               }
+       }, {
+               key: "getStatus",
+               value: function getStatus() {
+                       var _getTestCounts = this.getTestCounts(),
+                           total = _getTestCounts.total,
+                           failed = _getTestCounts.failed,
+                           skipped = _getTestCounts.skipped,
+                           todo = _getTestCounts.todo;
+
+                       if (failed) {
+                               return "failed";
+                       } else {
+                               if (skipped === total) {
+                                       return "skipped";
+                               } else if (todo === total) {
+                                       return "todo";
+                               } else {
+                                       return "passed";
+                               }
+                       }
+               }
+       }]);
+       return SuiteReport;
+  }();
+
+  // Handle an unhandled exception. By convention, returns true if further
+  // error handling should be suppressed and false otherwise.
+  // In this case, we will only suppress further error handling if the
+  // "ignoreGlobalErrors" configuration option is enabled.
+  function onError(error) {
+       for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
+               args[_key - 1] = arguments[_key];
+       }
+
+       if (config.current) {
+               if (config.current.ignoreGlobalErrors) {
+                       return true;
+               }
+               pushFailure.apply(undefined, [error.message, error.fileName + ":" + error.lineNumber].concat(args));
+       } else {
+               test("global failure", extend(function () {
+                       pushFailure.apply(undefined, [error.message, error.fileName + ":" + error.lineNumber].concat(args));
+               }, { validTest: true }));
+       }
+
+       return false;
+  }
+
+  var focused = false;
+  var QUnit = {};
+  var globalSuite = new SuiteReport();
+
+  // The initial "currentModule" represents the global (or top-level) module that
+  // is not explicitly defined by the user, therefore we add the "globalSuite" to
+  // it since each module has a suiteReport associated with it.
+  config.currentModule.suiteReport = globalSuite;
+
+  var moduleStack = [];
+  var globalStartCalled = false;
+  var runStarted = false;
+
+  // Figure out if we're running the tests from a server or not
+  QUnit.isLocal = !(defined.document && window.location.protocol !== "file:");
+
+  // Expose the current QUnit version
+  QUnit.version = "2.4.0";
+
+  function createModule(name, testEnvironment, modifiers) {
+       var parentModule = moduleStack.length ? moduleStack.slice(-1)[0] : null;
+       var moduleName = parentModule !== null ? [parentModule.name, name].join(" > ") : name;
+       var parentSuite = parentModule ? parentModule.suiteReport : globalSuite;
+
+       var skip$$1 = parentModule !== null && parentModule.skip || modifiers.skip;
+       var todo$$1 = parentModule !== null && parentModule.todo || modifiers.todo;
+
+       var module = {
+               name: moduleName,
+               parentModule: parentModule,
+               tests: [],
+               moduleId: generateHash(moduleName),
+               testsRun: 0,
+               unskippedTestsRun: 0,
+               childModules: [],
+               suiteReport: new SuiteReport(name, parentSuite),
+
+               // Pass along `skip` and `todo` properties from parent module, in case
+               // there is one, to childs. And use own otherwise.
+               // This property will be used to mark own tests and tests of child suites
+               // as either `skipped` or `todo`.
+               skip: skip$$1,
+               todo: skip$$1 ? false : todo$$1
+       };
+
+       var env = {};
+       if (parentModule) {
+               parentModule.childModules.push(module);
+               extend(env, parentModule.testEnvironment);
+       }
+       extend(env, testEnvironment);
+       module.testEnvironment = env;
+
+       config.modules.push(module);
+       return module;
+  }
+
+  function processModule(name, options, executeNow) {
+       var modifiers = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
+
+       var module = createModule(name, options, modifiers);
+
+       // Move any hooks to a 'hooks' object
+       var testEnvironment = module.testEnvironment;
+       var hooks = module.hooks = {};
+
+       setHookFromEnvironment(hooks, testEnvironment, "before");
+       setHookFromEnvironment(hooks, testEnvironment, "beforeEach");
+       setHookFromEnvironment(hooks, testEnvironment, "afterEach");
+       setHookFromEnvironment(hooks, testEnvironment, "after");
+
+       function setHookFromEnvironment(hooks, environment, name) {
+               var potentialHook = environment[name];
+               hooks[name] = typeof potentialHook === "function" ? [potentialHook] : [];
+               delete environment[name];
+       }
+
+       var moduleFns = {
+               before: setHookFunction(module, "before"),
+               beforeEach: setHookFunction(module, "beforeEach"),
+               afterEach: setHookFunction(module, "afterEach"),
+               after: setHookFunction(module, "after")
+       };
+
+       var currentModule = config.currentModule;
+       if (objectType(executeNow) === "function") {
+               moduleStack.push(module);
+               config.currentModule = module;
+               executeNow.call(module.testEnvironment, moduleFns);
+               moduleStack.pop();
+               module = module.parentModule || currentModule;
+       }
+
+       config.currentModule = module;
+  }
+
+  // TODO: extract this to a new file alongside its related functions
+  function module$1(name, options, executeNow) {
+       if (focused) {
+               return;
+       }
+
+       if (arguments.length === 2) {
+               if (objectType(options) === "function") {
+                       executeNow = options;
+                       options = undefined;
+               }
+       }
+
+       processModule(name, options, executeNow);
+  }
+
+  module$1.only = function () {
+       if (focused) {
+               return;
+       }
+
+       config.modules.length = 0;
+       config.queue.length = 0;
+
+       module$1.apply(undefined, arguments);
+
+       focused = true;
+  };
+
+  module$1.skip = function (name, options, executeNow) {
+       if (focused) {
+               return;
+       }
+
+       if (arguments.length === 2) {
+               if (objectType(options) === "function") {
+                       executeNow = options;
+                       options = undefined;
+               }
+       }
+
+       processModule(name, options, executeNow, { skip: true });
+  };
+
+  module$1.todo = function (name, options, executeNow) {
+       if (focused) {
+               return;
+       }
+
+       if (arguments.length === 2) {
+               if (objectType(options) === "function") {
+                       executeNow = options;
+                       options = undefined;
+               }
+       }
+
+       processModule(name, options, executeNow, { todo: true });
+  };
+
+  extend(QUnit, {
+       on: on,
+
+       module: module$1,
+
+       test: test,
+
+       todo: todo,
+
+       skip: skip,
+
+       only: only,
+
+       start: function start(count) {
+               var globalStartAlreadyCalled = globalStartCalled;
+
+               if (!config.current) {
+                       globalStartCalled = true;
+
+                       if (runStarted) {
+                               throw new Error("Called start() while test already started running");
+                       } else if (globalStartAlreadyCalled || count > 1) {
+                               throw new Error("Called start() outside of a test context too many times");
+                       } else if (config.autostart) {
+                               throw new Error("Called start() outside of a test context when " + "QUnit.config.autostart was true");
+                       } else if (!config.pageLoaded) {
+
+                               // The page isn't completely loaded yet, so we set autostart and then
+                               // load if we're in Node or wait for the browser's load event.
+                               config.autostart = true;
+
+                               // Starts from Node even if .load was not previously called. We still return
+                               // early otherwise we'll wind up "beginning" twice.
+                               if (!defined.document) {
+                                       QUnit.load();
+                               }
+
+                               return;
+                       }
+               } else {
+                       throw new Error("QUnit.start cannot be called inside a test context.");
+               }
+
+               scheduleBegin();
+       },
+
+       config: config,
+
+       is: is,
+
+       objectType: objectType,
+
+       extend: extend,
+
+       load: function load() {
+               config.pageLoaded = true;
+
+               // Initialize the configuration options
+               extend(config, {
+                       stats: { all: 0, bad: 0 },
+                       started: 0,
+                       updateRate: 1000,
+                       autostart: true,
+                       filter: ""
+               }, true);
+
+               if (!runStarted) {
+                       config.blocking = false;
+
+                       if (config.autostart) {
+                               scheduleBegin();
+                       }
+               }
+       },
+
+       stack: function stack(offset) {
+               offset = (offset || 0) + 2;
+               return sourceFromStacktrace(offset);
+       },
+
+       onError: onError
+  });
+
+  QUnit.pushFailure = pushFailure;
+  QUnit.assert = Assert.prototype;
+  QUnit.equiv = equiv;
+  QUnit.dump = dump;
+
+  registerLoggingCallbacks(QUnit);
+
+  function scheduleBegin() {
+
+       runStarted = true;
+
+       // Add a slight delay to allow definition of more modules and tests.
+       if (defined.setTimeout) {
+               setTimeout(function () {
+                       begin();
+               }, 13);
+       } else {
+               begin();
+       }
+  }
+
+  function begin() {
+       var i,
+           l,
+           modulesLog = [];
+
+       // If the test run hasn't officially begun yet
+       if (!config.started) {
+
+               // Record the time of the test run's beginning
+               config.started = now();
+
+               // Delete the loose unnamed module if unused.
+               if (config.modules[0].name === "" && config.modules[0].tests.length === 0) {
+                       config.modules.shift();
+               }
+
+               // Avoid unnecessary information by not logging modules' test environments
+               for (i = 0, l = config.modules.length; i < l; i++) {
+                       modulesLog.push({
+                               name: config.modules[i].name,
+                               tests: config.modules[i].tests
+                       });
+               }
+
+               // The test run is officially beginning now
+               emit("runStart", globalSuite.start(true));
+               runLoggingCallbacks("begin", {
+                       totalTests: Test.count,
+                       modules: modulesLog
+               });
+       }
+
+       config.blocking = false;
+       ProcessingQueue.advance();
+  }
+
+  function setHookFunction(module, hookName) {
+       return function setHook(callback) {
+               module.hooks[hookName].push(callback);
+       };
+  }
+
+  exportQUnit(QUnit);
+
+  (function () {
+
+       if (typeof window === "undefined" || typeof document === "undefined") {
+               return;
+       }
+
+       var config = QUnit.config,
+           hasOwn = Object.prototype.hasOwnProperty;
+
+       // Stores fixture HTML for resetting later
+       function storeFixture() {
+
+               // Avoid overwriting user-defined values
+               if (hasOwn.call(config, "fixture")) {
+                       return;
+               }
+
+               var fixture = document.getElementById("qunit-fixture");
+               if (fixture) {
+                       config.fixture = fixture.innerHTML;
+               }
+       }
+
+       QUnit.begin(storeFixture);
+
+       // Resets the fixture DOM element if available.
+       function resetFixture() {
+               if (config.fixture == null) {
+                       return;
+               }
+
+               var fixture = document.getElementById("qunit-fixture");
+               if (fixture) {
+                       fixture.innerHTML = config.fixture;
+               }
+       }
+
+       QUnit.testStart(resetFixture);
+  })();
+
+  (function () {
+
+       // Only interact with URLs via window.location
+       var location = typeof window !== "undefined" && window.location;
+       if (!location) {
+               return;
+       }
+
+       var urlParams = getUrlParams();
+
+       QUnit.urlParams = urlParams;
+
+       // Match module/test by inclusion in an array
+       QUnit.config.moduleId = [].concat(urlParams.moduleId || []);
+       QUnit.config.testId = [].concat(urlParams.testId || []);
+
+       // Exact case-insensitive match of the module name
+       QUnit.config.module = urlParams.module;
+
+       // Regular expression or case-insenstive substring match against "moduleName: testName"
+       QUnit.config.filter = urlParams.filter;
+
+       // Test order randomization
+       if (urlParams.seed === true) {
+
+               // Generate a random seed if the option is specified without a value
+               QUnit.config.seed = Math.random().toString(36).slice(2);
+       } else if (urlParams.seed) {
+               QUnit.config.seed = urlParams.seed;
+       }
+
+       // Add URL-parameter-mapped config values with UI form rendering data
+       QUnit.config.urlConfig.push({
+               id: "hidepassed",
+               label: "Hide passed tests",
+               tooltip: "Only show tests and assertions that fail. Stored as query-strings."
+       }, {
+               id: "noglobals",
+               label: "Check for Globals",
+               tooltip: "Enabling this will test if any test introduces new properties on the " + "global object (`window` in Browsers). Stored as query-strings."
+       }, {
+               id: "notrycatch",
+               label: "No try-catch",
+               tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging " + "exceptions in IE reasonable. Stored as query-strings."
+       });
+
+       QUnit.begin(function () {
+               var i,
+                   option,
+                   urlConfig = QUnit.config.urlConfig;
+
+               for (i = 0; i < urlConfig.length; i++) {
+
+                       // Options can be either strings or objects with nonempty "id" properties
+                       option = QUnit.config.urlConfig[i];
+                       if (typeof option !== "string") {
+                               option = option.id;
+                       }
+
+                       if (QUnit.config[option] === undefined) {
+                               QUnit.config[option] = urlParams[option];
+                       }
+               }
+       });
+
+       function getUrlParams() {
+               var i, param, name, value;
+               var urlParams = Object.create(null);
+               var params = location.search.slice(1).split("&");
+               var length = params.length;
+
+               for (i = 0; i < length; i++) {
+                       if (params[i]) {
+                               param = params[i].split("=");
+                               name = decodeQueryParam(param[0]);
+
+                               // Allow just a key to turn on a flag, e.g., test.html?noglobals
+                               value = param.length === 1 || decodeQueryParam(param.slice(1).join("="));
+                               if (name in urlParams) {
+                                       urlParams[name] = [].concat(urlParams[name], value);
+                               } else {
+                                       urlParams[name] = value;
+                               }
+                       }
+               }
+
+               return urlParams;
+       }
+
+       function decodeQueryParam(param) {
+               return decodeURIComponent(param.replace(/\+/g, "%20"));
+       }
+  })();
+
+  var stats = {
+       passedTests: 0,
+       failedTests: 0,
+       skippedTests: 0,
+       todoTests: 0
+  };
+
+  // Escape text for attribute or text content.
+  function escapeText(s) {
+       if (!s) {
+               return "";
+       }
+       s = s + "";
+
+       // Both single quotes and double quotes (for attributes)
+       return s.replace(/['"<>&]/g, function (s) {
+               switch (s) {
+                       case "'":
+                               return "&#039;";
+                       case "\"":
+                               return "&quot;";
+                       case "<":
+                               return "&lt;";
+                       case ">":
+                               return "&gt;";
+                       case "&":
+                               return "&amp;";
+               }
+       });
+  }
+
+  (function () {
+
+       // Don't load the HTML Reporter on non-browser environments
+       if (typeof window === "undefined" || !window.document) {
+               return;
+       }
+
+       var config = QUnit.config,
+           document$$1 = window.document,
+           collapseNext = false,
+           hasOwn = Object.prototype.hasOwnProperty,
+           unfilteredUrl = setUrl({ filter: undefined, module: undefined,
+               moduleId: undefined, testId: undefined }),
+           modulesList = [];
+
+       function addEvent(elem, type, fn) {
+               elem.addEventListener(type, fn, false);
+       }
+
+       function removeEvent(elem, type, fn) {
+               elem.removeEventListener(type, fn, false);
+       }
+
+       function addEvents(elems, type, fn) {
+               var i = elems.length;
+               while (i--) {
+                       addEvent(elems[i], type, fn);
+               }
+       }
+
+       function hasClass(elem, name) {
+               return (" " + elem.className + " ").indexOf(" " + name + " ") >= 0;
+       }
+
+       function addClass(elem, name) {
+               if (!hasClass(elem, name)) {
+                       elem.className += (elem.className ? " " : "") + name;
+               }
+       }
+
+       function toggleClass(elem, name, force) {
+               if (force || typeof force === "undefined" && !hasClass(elem, name)) {
+                       addClass(elem, name);
+               } else {
+                       removeClass(elem, name);
+               }
+       }
+
+       function removeClass(elem, name) {
+               var set = " " + elem.className + " ";
+
+               // Class name may appear multiple times
+               while (set.indexOf(" " + name + " ") >= 0) {
+                       set = set.replace(" " + name + " ", " ");
+               }
+
+               // Trim for prettiness
+               elem.className = typeof set.trim === "function" ? set.trim() : set.replace(/^\s+|\s+$/g, "");
+       }
+
+       function id(name) {
+               return document$$1.getElementById && document$$1.getElementById(name);
+       }
+
+       function abortTests() {
+               var abortButton = id("qunit-abort-tests-button");
+               if (abortButton) {
+                       abortButton.disabled = true;
+                       abortButton.innerHTML = "Aborting...";
+               }
+               QUnit.config.queue.length = 0;
+               return false;
+       }
+
+       function interceptNavigation(ev) {
+               applyUrlParams();
+
+               if (ev && ev.preventDefault) {
+                       ev.preventDefault();
+               }
+
+               return false;
+       }
+
+       function getUrlConfigHtml() {
+               var i,
+                   j,
+                   val,
+                   escaped,
+                   escapedTooltip,
+                   selection = false,
+                   urlConfig = config.urlConfig,
+                   urlConfigHtml = "";
+
+               for (i = 0; i < urlConfig.length; i++) {
+
+                       // Options can be either strings or objects with nonempty "id" properties
+                       val = config.urlConfig[i];
+                       if (typeof val === "string") {
+                               val = {
+                                       id: val,
+                                       label: val
+                               };
+                       }
+
+                       escaped = escapeText(val.id);
+                       escapedTooltip = escapeText(val.tooltip);
+
+                       if (!val.value || typeof val.value === "string") {
+                               urlConfigHtml += "<label for='qunit-urlconfig-" + escaped + "' title='" + escapedTooltip + "'><input id='qunit-urlconfig-" + escaped + "' name='" + escaped + "' type='checkbox'" + (val.value ? " value='" + escapeText(val.value) + "'" : "") + (config[val.id] ? " checked='checked'" : "") + " title='" + escapedTooltip + "' />" + escapeText(val.label) + "</label>";
+                       } else {
+                               urlConfigHtml += "<label for='qunit-urlconfig-" + escaped + "' title='" + escapedTooltip + "'>" + val.label + ": </label><select id='qunit-urlconfig-" + escaped + "' name='" + escaped + "' title='" + escapedTooltip + "'><option></option>";
+
+                               if (QUnit.is("array", val.value)) {
+                                       for (j = 0; j < val.value.length; j++) {
+                                               escaped = escapeText(val.value[j]);
+                                               urlConfigHtml += "<option value='" + escaped + "'" + (config[val.id] === val.value[j] ? (selection = true) && " selected='selected'" : "") + ">" + escaped + "</option>";
+                                       }
+                               } else {
+                                       for (j in val.value) {
+                                               if (hasOwn.call(val.value, j)) {
+                                                       urlConfigHtml += "<option value='" + escapeText(j) + "'" + (config[val.id] === j ? (selection = true) && " selected='selected'" : "") + ">" + escapeText(val.value[j]) + "</option>";
+                                               }
+                                       }
+                               }
+                               if (config[val.id] && !selection) {
+                                       escaped = escapeText(config[val.id]);
+                                       urlConfigHtml += "<option value='" + escaped + "' selected='selected' disabled='disabled'>" + escaped + "</option>";
+                               }
+                               urlConfigHtml += "</select>";
+                       }
+               }
+
+               return urlConfigHtml;
+       }
+
+       // Handle "click" events on toolbar checkboxes and "change" for select menus.
+       // Updates the URL with the new state of `config.urlConfig` values.
+       function toolbarChanged() {
+               var updatedUrl,
+                   value,
+                   tests,
+                   field = this,
+                   params = {};
+
+               // Detect if field is a select menu or a checkbox
+               if ("selectedIndex" in field) {
+                       value = field.options[field.selectedIndex].value || undefined;
+               } else {
+                       value = field.checked ? field.defaultValue || true : undefined;
+               }
+
+               params[field.name] = value;
+               updatedUrl = setUrl(params);
+
+               // Check if we can apply the change without a page refresh
+               if ("hidepassed" === field.name && "replaceState" in window.history) {
+                       QUnit.urlParams[field.name] = value;
+                       config[field.name] = value || false;
+                       tests = id("qunit-tests");
+                       if (tests) {
+                               toggleClass(tests, "hidepass", value || false);
+                       }
+                       window.history.replaceState(null, "", updatedUrl);
+               } else {
+                       window.location = updatedUrl;
+               }
+       }
+
+       function setUrl(params) {
+               var key,
+                   arrValue,
+                   i,
+                   querystring = "?",
+                   location = window.location;
+
+               params = QUnit.extend(QUnit.extend({}, QUnit.urlParams), params);
+
+               for (key in params) {
+
+                       // Skip inherited or undefined properties
+                       if (hasOwn.call(params, key) && params[key] !== undefined) {
+
+                               // Output a parameter for each value of this key (but usually just one)
+                               arrValue = [].concat(params[key]);
+                               for (i = 0; i < arrValue.length; i++) {
+                                       querystring += encodeURIComponent(key);
+                                       if (arrValue[i] !== true) {
+                                               querystring += "=" + encodeURIComponent(arrValue[i]);
+                                       }
+                                       querystring += "&";
+                               }
+                       }
+               }
+               return location.protocol + "//" + location.host + location.pathname + querystring.slice(0, -1);
+       }
+
+       function applyUrlParams() {
+               var i,
+                   selectedModules = [],
+                   modulesList = id("qunit-modulefilter-dropdown-list").getElementsByTagName("input"),
+                   filter = id("qunit-filter-input").value;
+
+               for (i = 0; i < modulesList.length; i++) {
+                       if (modulesList[i].checked) {
+                               selectedModules.push(modulesList[i].value);
+                       }
+               }
+
+               window.location = setUrl({
+                       filter: filter === "" ? undefined : filter,
+                       moduleId: selectedModules.length === 0 ? undefined : selectedModules,
+
+                       // Remove module and testId filter
+                       module: undefined,
+                       testId: undefined
+               });
+       }
+
+       function toolbarUrlConfigContainer() {
+               var urlConfigContainer = document$$1.createElement("span");
+
+               urlConfigContainer.innerHTML = getUrlConfigHtml();
+               addClass(urlConfigContainer, "qunit-url-config");
+
+               addEvents(urlConfigContainer.getElementsByTagName("input"), "change", toolbarChanged);
+               addEvents(urlConfigContainer.getElementsByTagName("select"), "change", toolbarChanged);
+
+               return urlConfigContainer;
+       }
+
+       function abortTestsButton() {
+               var button = document$$1.createElement("button");
+               button.id = "qunit-abort-tests-button";
+               button.innerHTML = "Abort";
+               addEvent(button, "click", abortTests);
+               return button;
+       }
+
+       function toolbarLooseFilter() {
+               var filter = document$$1.createElement("form"),
+                   label = document$$1.createElement("label"),
+                   input = document$$1.createElement("input"),
+                   button = document$$1.createElement("button");
+
+               addClass(filter, "qunit-filter");
+
+               label.innerHTML = "Filter: ";
+
+               input.type = "text";
+               input.value = config.filter || "";
+               input.name = "filter";
+               input.id = "qunit-filter-input";
+
+               button.innerHTML = "Go";
+
+               label.appendChild(input);
+
+               filter.appendChild(label);
+               filter.appendChild(document$$1.createTextNode(" "));
+               filter.appendChild(button);
+               addEvent(filter, "submit", interceptNavigation);
+
+               return filter;
+       }
+
+       function moduleListHtml() {
+               var i,
+                   checked,
+                   html = "";
+
+               for (i = 0; i < config.modules.length; i++) {
+                       if (config.modules[i].name !== "") {
+                               checked = config.moduleId.indexOf(config.modules[i].moduleId) > -1;
+                               html += "<li><label class='clickable" + (checked ? " checked" : "") + "'><input type='checkbox' " + "value='" + config.modules[i].moduleId + "'" + (checked ? " checked='checked'" : "") + " />" + escapeText(config.modules[i].name) + "</label></li>";
+                       }
+               }
+
+               return html;
+       }
+
+       function toolbarModuleFilter() {
+               var allCheckbox,
+                   commit,
+                   reset,
+                   moduleFilter = document$$1.createElement("form"),
+                   label = document$$1.createElement("label"),
+                   moduleSearch = document$$1.createElement("input"),
+                   dropDown = document$$1.createElement("div"),
+                   actions = document$$1.createElement("span"),
+                   dropDownList = document$$1.createElement("ul"),
+                   dirty = false;
+
+               moduleSearch.id = "qunit-modulefilter-search";
+               addEvent(moduleSearch, "input", searchInput);
+               addEvent(moduleSearch, "input", searchFocus);
+               addEvent(moduleSearch, "focus", searchFocus);
+               addEvent(moduleSearch, "click", searchFocus);
+
+               label.id = "qunit-modulefilter-search-container";
+               label.innerHTML = "Module: ";
+               label.appendChild(moduleSearch);
+
+               actions.id = "qunit-modulefilter-actions";
+               actions.innerHTML = "<button style='display:none'>Apply</button>" + "<button type='reset' style='display:none'>Reset</button>" + "<label class='clickable" + (config.moduleId.length ? "" : " checked") + "'><input type='checkbox'" + (config.moduleId.length ? "" : " checked='checked'") + ">All modules</label>";
+               allCheckbox = actions.lastChild.firstChild;
+               commit = actions.firstChild;
+               reset = commit.nextSibling;
+               addEvent(commit, "click", applyUrlParams);
+
+               dropDownList.id = "qunit-modulefilter-dropdown-list";
+               dropDownList.innerHTML = moduleListHtml();
+
+               dropDown.id = "qunit-modulefilter-dropdown";
+               dropDown.style.display = "none";
+               dropDown.appendChild(actions);
+               dropDown.appendChild(dropDownList);
+               addEvent(dropDown, "change", selectionChange);
+               selectionChange();
+
+               moduleFilter.id = "qunit-modulefilter";
+               moduleFilter.appendChild(label);
+               moduleFilter.appendChild(dropDown);
+               addEvent(moduleFilter, "submit", interceptNavigation);
+               addEvent(moduleFilter, "reset", function () {
+
+                       // Let the reset happen, then update styles
+                       window.setTimeout(selectionChange);
+               });
+
+               // Enables show/hide for the dropdown
+               function searchFocus() {
+                       if (dropDown.style.display !== "none") {
+                               return;
+                       }
+
+                       dropDown.style.display = "block";
+                       addEvent(document$$1, "click", hideHandler);
+                       addEvent(document$$1, "keydown", hideHandler);
+
+                       // Hide on Escape keydown or outside-container click
+                       function hideHandler(e) {
+                               var inContainer = moduleFilter.contains(e.target);
+
+                               if (e.keyCode === 27 || !inContainer) {
+                                       if (e.keyCode === 27 && inContainer) {
+                                               moduleSearch.focus();
+                                       }
+                                       dropDown.style.display = "none";
+                                       removeEvent(document$$1, "click", hideHandler);
+                                       removeEvent(document$$1, "keydown", hideHandler);
+                                       moduleSearch.value = "";
+                                       searchInput();
+                               }
+                       }
+               }
+
+               // Processes module search box input
+               function searchInput() {
+                       var i,
+                           item,
+                           searchText = moduleSearch.value.toLowerCase(),
+                           listItems = dropDownList.children;
+
+                       for (i = 0; i < listItems.length; i++) {
+                               item = listItems[i];
+                               if (!searchText || item.textContent.toLowerCase().indexOf(searchText) > -1) {
+                                       item.style.display = "";
+                               } else {
+                                       item.style.display = "none";
+                               }
+                       }
+               }
+
+               // Processes selection changes
+               function selectionChange(evt) {
+                       var i,
+                           item,
+                           checkbox = evt && evt.target || allCheckbox,
+                           modulesList = dropDownList.getElementsByTagName("input"),
+                           selectedNames = [];
+
+                       toggleClass(checkbox.parentNode, "checked", checkbox.checked);
+
+                       dirty = false;
+                       if (checkbox.checked && checkbox !== allCheckbox) {
+                               allCheckbox.checked = false;
+                               removeClass(allCheckbox.parentNode, "checked");
+                       }
+                       for (i = 0; i < modulesList.length; i++) {
+                               item = modulesList[i];
+                               if (!evt) {
+                                       toggleClass(item.parentNode, "checked", item.checked);
+                               } else if (checkbox === allCheckbox && checkbox.checked) {
+                                       item.checked = false;
+                                       removeClass(item.parentNode, "checked");
+                               }
+                               dirty = dirty || item.checked !== item.defaultChecked;
+                               if (item.checked) {
+                                       selectedNames.push(item.parentNode.textContent);
+                               }
+                       }
+
+                       commit.style.display = reset.style.display = dirty ? "" : "none";
+                       moduleSearch.placeholder = selectedNames.join(", ") || allCheckbox.parentNode.textContent;
+                       moduleSearch.title = "Type to filter list. Current selection:\n" + (selectedNames.join("\n") || allCheckbox.parentNode.textContent);
+               }
+
+               return moduleFilter;
+       }
+
+       function appendToolbar() {
+               var toolbar = id("qunit-testrunner-toolbar");
+
+               if (toolbar) {
+                       toolbar.appendChild(toolbarUrlConfigContainer());
+                       toolbar.appendChild(toolbarModuleFilter());
+                       toolbar.appendChild(toolbarLooseFilter());
+                       toolbar.appendChild(document$$1.createElement("div")).className = "clearfix";
+               }
+       }
+
+       function appendHeader() {
+               var header = id("qunit-header");
+
+               if (header) {
+                       header.innerHTML = "<a href='" + escapeText(unfilteredUrl) + "'>" + header.innerHTML + "</a> ";
+               }
+       }
+
+       function appendBanner() {
+               var banner = id("qunit-banner");
+
+               if (banner) {
+                       banner.className = "";
+               }
+       }
+
+       function appendTestResults() {
+               var tests = id("qunit-tests"),
+                   result = id("qunit-testresult"),
+                   controls;
+
+               if (result) {
+                       result.parentNode.removeChild(result);
+               }
+
+               if (tests) {
+                       tests.innerHTML = "";
+                       result = document$$1.createElement("p");
+                       result.id = "qunit-testresult";
+                       result.className = "result";
+                       tests.parentNode.insertBefore(result, tests);
+                       result.innerHTML = "<div id=\"qunit-testresult-display\">Running...<br />&#160;</div>" + "<div id=\"qunit-testresult-controls\"></div>" + "<div class=\"clearfix\"></div>";
+                       controls = id("qunit-testresult-controls");
+               }
+
+               if (controls) {
+                       controls.appendChild(abortTestsButton());
+               }
+       }
+
+       function appendFilteredTest() {
+               var testId = QUnit.config.testId;
+               if (!testId || testId.length <= 0) {
+                       return "";
+               }
+               return "<div id='qunit-filteredTest'>Rerunning selected tests: " + escapeText(testId.join(", ")) + " <a id='qunit-clearFilter' href='" + escapeText(unfilteredUrl) + "'>Run all tests</a></div>";
+       }
+
+       function appendUserAgent() {
+               var userAgent = id("qunit-userAgent");
+
+               if (userAgent) {
+                       userAgent.innerHTML = "";
+                       userAgent.appendChild(document$$1.createTextNode("QUnit " + QUnit.version + "; " + navigator.userAgent));
+               }
+       }
+
+       function appendInterface() {
+               var qunit = id("qunit");
+
+               if (qunit) {
+                       qunit.innerHTML = "<h1 id='qunit-header'>" + escapeText(document$$1.title) + "</h1>" + "<h2 id='qunit-banner'></h2>" + "<div id='qunit-testrunner-toolbar'></div>" + appendFilteredTest() + "<h2 id='qunit-userAgent'></h2>" + "<ol id='qunit-tests'></ol>";
+               }
+
+               appendHeader();
+               appendBanner();
+               appendTestResults();
+               appendUserAgent();
+               appendToolbar();
+       }
+
+       function appendTestsList(modules) {
+               var i, l, x, z, test, moduleObj;
+
+               for (i = 0, l = modules.length; i < l; i++) {
+                       moduleObj = modules[i];
+
+                       for (x = 0, z = moduleObj.tests.length; x < z; x++) {
+                               test = moduleObj.tests[x];
+
+                               appendTest(test.name, test.testId, moduleObj.name);
+                       }
+               }
+       }
+
+       function appendTest(name, testId, moduleName) {
+               var title,
+                   rerunTrigger,
+                   testBlock,
+                   assertList,
+                   tests = id("qunit-tests");
+
+               if (!tests) {
+                       return;
+               }
+
+               title = document$$1.createElement("strong");
+               title.innerHTML = getNameHtml(name, moduleName);
+
+               rerunTrigger = document$$1.createElement("a");
+               rerunTrigger.innerHTML = "Rerun";
+               rerunTrigger.href = setUrl({ testId: testId });
+
+               testBlock = document$$1.createElement("li");
+               testBlock.appendChild(title);
+               testBlock.appendChild(rerunTrigger);
+               testBlock.id = "qunit-test-output-" + testId;
+
+               assertList = document$$1.createElement("ol");
+               assertList.className = "qunit-assert-list";
+
+               testBlock.appendChild(assertList);
+
+               tests.appendChild(testBlock);
+       }
+
+       // HTML Reporter initialization and load
+       QUnit.begin(function (details) {
+               var i, moduleObj, tests;
+
+               // Sort modules by name for the picker
+               for (i = 0; i < details.modules.length; i++) {
+                       moduleObj = details.modules[i];
+                       if (moduleObj.name) {
+                               modulesList.push(moduleObj.name);
+                       }
+               }
+               modulesList.sort(function (a, b) {
+                       return a.localeCompare(b);
+               });
+
+               // Initialize QUnit elements
+               appendInterface();
+               appendTestsList(details.modules);
+               tests = id("qunit-tests");
+               if (tests && config.hidepassed) {
+                       addClass(tests, "hidepass");
+               }
+       });
+
+       QUnit.done(function (details) {
+               var banner = id("qunit-banner"),
+                   tests = id("qunit-tests"),
+                   abortButton = id("qunit-abort-tests-button"),
+                   totalTests = stats.passedTests + stats.skippedTests + stats.todoTests + stats.failedTests,
+                   html = [totalTests, " tests completed in ", details.runtime, " milliseconds, with ", stats.failedTests, " failed, ", stats.skippedTests, " skipped, and ", stats.todoTests, " todo.<br />", "<span class='passed'>", details.passed, "</span> assertions of <span class='total'>", details.total, "</span> passed, <span class='failed'>", details.failed, "</span> failed."].join(""),
+                   test,
+                   assertLi,
+                   assertList;
+
+               // Update remaing tests to aborted
+               if (abortButton && abortButton.disabled) {
+                       html = "Tests aborted after " + details.runtime + " milliseconds.";
+
+                       for (var i = 0; i < tests.children.length; i++) {
+                               test = tests.children[i];
+                               if (test.className === "" || test.className === "running") {
+                                       test.className = "aborted";
+                                       assertList = test.getElementsByTagName("ol")[0];
+                                       assertLi = document$$1.createElement("li");
+                                       assertLi.className = "fail";
+                                       assertLi.innerHTML = "Test aborted.";
+                                       assertList.appendChild(assertLi);
+                               }
+                       }
+               }
+
+               if (banner && (!abortButton || abortButton.disabled === false)) {
+                       banner.className = stats.failedTests ? "qunit-fail" : "qunit-pass";
+               }
+
+               if (abortButton) {
+                       abortButton.parentNode.removeChild(abortButton);
+               }
+
+               if (tests) {
+                       id("qunit-testresult-display").innerHTML = html;
+               }
+
+               if (config.altertitle && document$$1.title) {
+
+                       // Show ✖ for good, ✔ for bad suite result in title
+                       // use escape sequences in case file gets loaded with non-utf-8-charset
+                       document$$1.title = [stats.failedTests ? "\u2716" : "\u2714", document$$1.title.replace(/^[\u2714\u2716] /i, "")].join(" ");
+               }
+
+               // Scroll back to top to show results
+               if (config.scrolltop && window.scrollTo) {
+                       window.scrollTo(0, 0);
+               }
+       });
+
+       function getNameHtml(name, module) {
+               var nameHtml = "";
+
+               if (module) {
+                       nameHtml = "<span class='module-name'>" + escapeText(module) + "</span>: ";
+               }
+
+               nameHtml += "<span class='test-name'>" + escapeText(name) + "</span>";
+
+               return nameHtml;
+       }
+
+       QUnit.testStart(function (details) {
+               var running, testBlock, bad;
+
+               testBlock = id("qunit-test-output-" + details.testId);
+               if (testBlock) {
+                       testBlock.className = "running";
+               } else {
+
+                       // Report later registered tests
+                       appendTest(details.name, details.testId, details.module);
+               }
+
+               running = id("qunit-testresult-display");
+               if (running) {
+                       bad = QUnit.config.reorder && details.previousFailure;
+
+                       running.innerHTML = (bad ? "Rerunning previously failed test: <br />" : "Running: <br />") + getNameHtml(details.name, details.module);
+               }
+       });
+
+       function stripHtml(string) {
+
+               // Strip tags, html entity and whitespaces
+               return string.replace(/<\/?[^>]+(>|$)/g, "").replace(/\&quot;/g, "").replace(/\s+/g, "");
+       }
+
+       QUnit.log(function (details) {
+               var assertList,
+                   assertLi,
+                   message,
+                   expected,
+                   actual,
+                   diff,
+                   showDiff = false,
+                   testItem = id("qunit-test-output-" + details.testId);
+
+               if (!testItem) {
+                       return;
+               }
+
+               message = escapeText(details.message) || (details.result ? "okay" : "failed");
+               message = "<span class='test-message'>" + message + "</span>";
+               message += "<span class='runtime'>@ " + details.runtime + " ms</span>";
+
+               // The pushFailure doesn't provide details.expected
+               // when it calls, it's implicit to also not show expected and diff stuff
+               // Also, we need to check details.expected existence, as it can exist and be undefined
+               if (!details.result && hasOwn.call(details, "expected")) {
+                       if (details.negative) {
+                               expected = "NOT " + QUnit.dump.parse(details.expected);
+                       } else {
+                               expected = QUnit.dump.parse(details.expected);
+                       }
+
+                       actual = QUnit.dump.parse(details.actual);
+                       message += "<table><tr class='test-expected'><th>Expected: </th><td><pre>" + escapeText(expected) + "</pre></td></tr>";
+
+                       if (actual !== expected) {
+
+                               message += "<tr class='test-actual'><th>Result: </th><td><pre>" + escapeText(actual) + "</pre></td></tr>";
+
+                               if (typeof details.actual === "number" && typeof details.expected === "number") {
+                                       if (!isNaN(details.actual) && !isNaN(details.expected)) {
+                                               showDiff = true;
+                                               diff = details.actual - details.expected;
+                                               diff = (diff > 0 ? "+" : "") + diff;
+                                       }
+                               } else if (typeof details.actual !== "boolean" && typeof details.expected !== "boolean") {
+                                       diff = QUnit.diff(expected, actual);
+
+                                       // don't show diff if there is zero overlap
+                                       showDiff = stripHtml(diff).length !== stripHtml(expected).length + stripHtml(actual).length;
+                               }
+
+                               if (showDiff) {
+                                       message += "<tr class='test-diff'><th>Diff: </th><td><pre>" + diff + "</pre></td></tr>";
+                               }
+                       } else if (expected.indexOf("[object Array]") !== -1 || expected.indexOf("[object Object]") !== -1) {
+                               message += "<tr class='test-message'><th>Message: </th><td>" + "Diff suppressed as the depth of object is more than current max depth (" + QUnit.config.maxDepth + ").<p>Hint: Use <code>QUnit.dump.maxDepth</code> to " + " run with a higher max depth or <a href='" + escapeText(setUrl({ maxDepth: -1 })) + "'>" + "Rerun</a> without max depth.</p></td></tr>";
+                       } else {
+                               message += "<tr class='test-message'><th>Message: </th><td>" + "Diff suppressed as the expected and actual results have an equivalent" + " serialization</td></tr>";
+                       }
+
+                       if (details.source) {
+                               message += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText(details.source) + "</pre></td></tr>";
+                       }
+
+                       message += "</table>";
+
+                       // This occurs when pushFailure is set and we have an extracted stack trace
+               } else if (!details.result && details.source) {
+                       message += "<table>" + "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText(details.source) + "</pre></td></tr>" + "</table>";
+               }
+
+               assertList = testItem.getElementsByTagName("ol")[0];
+
+               assertLi = document$$1.createElement("li");
+               assertLi.className = details.result ? "pass" : "fail";
+               assertLi.innerHTML = message;
+               assertList.appendChild(assertLi);
+       });
+
+       QUnit.testDone(function (details) {
+               var testTitle,
+                   time,
+                   testItem,
+                   assertList,
+                   good,
+                   bad,
+                   testCounts,
+                   skipped,
+                   sourceName,
+                   tests = id("qunit-tests");
+
+               if (!tests) {
+                       return;
+               }
+
+               testItem = id("qunit-test-output-" + details.testId);
+
+               assertList = testItem.getElementsByTagName("ol")[0];
+
+               good = details.passed;
+               bad = details.failed;
+
+               // This test passed if it has no unexpected failed assertions
+               var testPassed = details.failed > 0 ? details.todo : !details.todo;
+
+               if (testPassed) {
+
+                       // Collapse the passing tests
+                       addClass(assertList, "qunit-collapsed");
+               } else if (config.collapse) {
+                       if (!collapseNext) {
+
+                               // Skip collapsing the first failing test
+                               collapseNext = true;
+                       } else {
+
+                               // Collapse remaining tests
+                               addClass(assertList, "qunit-collapsed");
+                       }
+               }
+
+               // The testItem.firstChild is the test name
+               testTitle = testItem.firstChild;
+
+               testCounts = bad ? "<b class='failed'>" + bad + "</b>, " + "<b class='passed'>" + good + "</b>, " : "";
+
+               testTitle.innerHTML += " <b class='counts'>(" + testCounts + details.assertions.length + ")</b>";
+
+               if (details.skipped) {
+                       stats.skippedTests++;
+
+                       testItem.className = "skipped";
+                       skipped = document$$1.createElement("em");
+                       skipped.className = "qunit-skipped-label";
+                       skipped.innerHTML = "skipped";
+                       testItem.insertBefore(skipped, testTitle);
+               } else {
+                       addEvent(testTitle, "click", function () {
+                               toggleClass(assertList, "qunit-collapsed");
+                       });
+
+                       testItem.className = testPassed ? "pass" : "fail";
+
+                       if (details.todo) {
+                               var todoLabel = document$$1.createElement("em");
+                               todoLabel.className = "qunit-todo-label";
+                               todoLabel.innerHTML = "todo";
+                               testItem.className += " todo";
+                               testItem.insertBefore(todoLabel, testTitle);
+                       }
+
+                       time = document$$1.createElement("span");
+                       time.className = "runtime";
+                       time.innerHTML = details.runtime + " ms";
+                       testItem.insertBefore(time, assertList);
+
+                       if (!testPassed) {
+                               stats.failedTests++;
+                       } else if (details.todo) {
+                               stats.todoTests++;
+                       } else {
+                               stats.passedTests++;
+                       }
+               }
+
+               // Show the source of the test when showing assertions
+               if (details.source) {
+                       sourceName = document$$1.createElement("p");
+                       sourceName.innerHTML = "<strong>Source: </strong>" + details.source;
+                       addClass(sourceName, "qunit-source");
+                       if (testPassed) {
+                               addClass(sourceName, "qunit-collapsed");
+                       }
+                       addEvent(testTitle, "click", function () {
+                               toggleClass(sourceName, "qunit-collapsed");
+                       });
+                       testItem.appendChild(sourceName);
+               }
+       });
+
+       // Avoid readyState issue with phantomjs
+       // Ref: #818
+       var notPhantom = function (p) {
+               return !(p && p.version && p.version.major > 0);
+       }(window.phantom);
+
+       if (notPhantom && document$$1.readyState === "complete") {
+               QUnit.load();
+       } else {
+               addEvent(window, "load", QUnit.load);
+       }
+
+       // Wrap window.onerror. We will call the original window.onerror to see if
+       // the existing handler fully handles the error; if not, we will call the
+       // QUnit.onError function.
+       var originalWindowOnError = window.onerror;
+
+       // Cover uncaught exceptions
+       // Returning true will suppress the default browser handler,
+       // returning false will let it run.
+       window.onerror = function (message, fileName, lineNumber) {
+               var ret = false;
+               if (originalWindowOnError) {
+                       for (var _len = arguments.length, args = Array(_len > 3 ? _len - 3 : 0), _key = 3; _key < _len; _key++) {
+                               args[_key - 3] = arguments[_key];
+                       }
+
+                       ret = originalWindowOnError.call.apply(originalWindowOnError, [this, message, fileName, lineNumber].concat(args));
+               }
+
+               // Treat return value as window.onerror itself does,
+               // Only do our handling if not suppressed.
+               if (ret !== true) {
+                       var error = {
+                               message: message,
+                               fileName: fileName,
+                               lineNumber: lineNumber
+                       };
+
+                       ret = QUnit.onError(error);
+               }
+
+               return ret;
+       };
+  })();
+
+  /*
+   * This file is a modified version of google-diff-match-patch's JavaScript implementation
+   * (https://code.google.com/p/google-diff-match-patch/source/browse/trunk/javascript/diff_match_patch_uncompressed.js),
+   * modifications are licensed as more fully set forth in LICENSE.txt.
+   *
+   * The original source of google-diff-match-patch is attributable and licensed as follows:
+   *
+   * Copyright 2006 Google Inc.
+   * https://code.google.com/p/google-diff-match-patch/
+   *
+   * Licensed under the Apache License, Version 2.0 (the "License");
+   * you may not use this file except in compliance with the License.
+   * You may obtain a copy of the License at
+   *
+   * https://www.apache.org/licenses/LICENSE-2.0
+   *
+   * Unless required by applicable law or agreed to in writing, software
+   * distributed under the License is distributed on an "AS IS" BASIS,
+   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   * See the License for the specific language governing permissions and
+   * limitations under the License.
+   *
+   * More Info:
+   *  https://code.google.com/p/google-diff-match-patch/
+   *
+   * Usage: QUnit.diff(expected, actual)
+   *
+   */
+  QUnit.diff = function () {
+       function DiffMatchPatch() {}
+
+       //  DIFF FUNCTIONS
+
+       /**
+    * The data structure representing a diff is an array of tuples:
+    * [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']]
+    * which means: delete 'Hello', add 'Goodbye' and keep ' world.'
+    */
+       var DIFF_DELETE = -1,
+           DIFF_INSERT = 1,
+           DIFF_EQUAL = 0;
+
+       /**
+    * Find the differences between two texts.  Simplifies the problem by stripping
+    * any common prefix or suffix off the texts before diffing.
+    * @param {string} text1 Old string to be diffed.
+    * @param {string} text2 New string to be diffed.
+    * @param {boolean=} optChecklines Optional speedup flag. If present and false,
+    *     then don't run a line-level diff first to identify the changed areas.
+    *     Defaults to true, which does a faster, slightly less optimal diff.
+    * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
+    */
+       DiffMatchPatch.prototype.DiffMain = function (text1, text2, optChecklines) {
+               var deadline, checklines, commonlength, commonprefix, commonsuffix, diffs;
+
+               // The diff must be complete in up to 1 second.
+               deadline = new Date().getTime() + 1000;
+
+               // Check for null inputs.
+               if (text1 === null || text2 === null) {
+                       throw new Error("Null input. (DiffMain)");
+               }
+
+               // Check for equality (speedup).
+               if (text1 === text2) {
+                       if (text1) {
+                               return [[DIFF_EQUAL, text1]];
+                       }
+                       return [];
+               }
+
+               if (typeof optChecklines === "undefined") {
+                       optChecklines = true;
+               }
+
+               checklines = optChecklines;
+
+               // Trim off common prefix (speedup).
+               commonlength = this.diffCommonPrefix(text1, text2);
+               commonprefix = text1.substring(0, commonlength);
+               text1 = text1.substring(commonlength);
+               text2 = text2.substring(commonlength);
+
+               // Trim off common suffix (speedup).
+               commonlength = this.diffCommonSuffix(text1, text2);
+               commonsuffix = text1.substring(text1.length - commonlength);
+               text1 = text1.substring(0, text1.length - commonlength);
+               text2 = text2.substring(0, text2.length - commonlength);
+
+               // Compute the diff on the middle block.
+               diffs = this.diffCompute(text1, text2, checklines, deadline);
+
+               // Restore the prefix and suffix.
+               if (commonprefix) {
+                       diffs.unshift([DIFF_EQUAL, commonprefix]);
+               }
+               if (commonsuffix) {
+                       diffs.push([DIFF_EQUAL, commonsuffix]);
+               }
+               this.diffCleanupMerge(diffs);
+               return diffs;
+       };
+
+       /**
+    * Reduce the number of edits by eliminating operationally trivial equalities.
+    * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
+    */
+       DiffMatchPatch.prototype.diffCleanupEfficiency = function (diffs) {
+               var changes, equalities, equalitiesLength, lastequality, pointer, preIns, preDel, postIns, postDel;
+               changes = false;
+               equalities = []; // Stack of indices where equalities are found.
+               equalitiesLength = 0; // Keeping our own length var is faster in JS.
+               /** @type {?string} */
+               lastequality = null;
+
+               // Always equal to diffs[equalities[equalitiesLength - 1]][1]
+               pointer = 0; // Index of current position.
+
+               // Is there an insertion operation before the last equality.
+               preIns = false;
+
+               // Is there a deletion operation before the last equality.
+               preDel = false;
+
+               // Is there an insertion operation after the last equality.
+               postIns = false;
+
+               // Is there a deletion operation after the last equality.
+               postDel = false;
+               while (pointer < diffs.length) {
+
+                       // Equality found.
+                       if (diffs[pointer][0] === DIFF_EQUAL) {
+                               if (diffs[pointer][1].length < 4 && (postIns || postDel)) {
+
+                                       // Candidate found.
+                                       equalities[equalitiesLength++] = pointer;
+                                       preIns = postIns;
+                                       preDel = postDel;
+                                       lastequality = diffs[pointer][1];
+                               } else {
+
+                                       // Not a candidate, and can never become one.
+                                       equalitiesLength = 0;
+                                       lastequality = null;
+                               }
+                               postIns = postDel = false;
+
+                               // An insertion or deletion.
+                       } else {
+
+                               if (diffs[pointer][0] === DIFF_DELETE) {
+                                       postDel = true;
+                               } else {
+                                       postIns = true;
+                               }
+
+                               /*
+       * Five types to be split:
+       * <ins>A</ins><del>B</del>XY<ins>C</ins><del>D</del>
+       * <ins>A</ins>X<ins>C</ins><del>D</del>
+       * <ins>A</ins><del>B</del>X<ins>C</ins>
+       * <ins>A</del>X<ins>C</ins><del>D</del>
+       * <ins>A</ins><del>B</del>X<del>C</del>
+       */
+                               if (lastequality && (preIns && preDel && postIns && postDel || lastequality.length < 2 && preIns + preDel + postIns + postDel === 3)) {
+
+                                       // Duplicate record.
+                                       diffs.splice(equalities[equalitiesLength - 1], 0, [DIFF_DELETE, lastequality]);
+
+                                       // Change second copy to insert.
+                                       diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT;
+                                       equalitiesLength--; // Throw away the equality we just deleted;
+                                       lastequality = null;
+                                       if (preIns && preDel) {
+
+                                               // No changes made which could affect previous entry, keep going.
+                                               postIns = postDel = true;
+                                               equalitiesLength = 0;
+                                       } else {
+                                               equalitiesLength--; // Throw away the previous equality.
+                                               pointer = equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1;
+                                               postIns = postDel = false;
+                                       }
+                                       changes = true;
+                               }
+                       }
+                       pointer++;
+               }
+
+               if (changes) {
+                       this.diffCleanupMerge(diffs);
+               }
+       };
+
+       /**
+    * Convert a diff array into a pretty HTML report.
+    * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
+    * @param {integer} string to be beautified.
+    * @return {string} HTML representation.
+    */
+       DiffMatchPatch.prototype.diffPrettyHtml = function (diffs) {
+               var op,
+                   data,
+                   x,
+                   html = [];
+               for (x = 0; x < diffs.length; x++) {
+                       op = diffs[x][0]; // Operation (insert, delete, equal)
+                       data = diffs[x][1]; // Text of change.
+                       switch (op) {
+                               case DIFF_INSERT:
+                                       html[x] = "<ins>" + escapeText(data) + "</ins>";
+                                       break;
+                               case DIFF_DELETE:
+                                       html[x] = "<del>" + escapeText(data) + "</del>";
+                                       break;
+                               case DIFF_EQUAL:
+                                       html[x] = "<span>" + escapeText(data) + "</span>";
+                                       break;
+                       }
+               }
+               return html.join("");
+       };
+
+       /**
+    * Determine the common prefix of two strings.
+    * @param {string} text1 First string.
+    * @param {string} text2 Second string.
+    * @return {number} The number of characters common to the start of each
+    *     string.
+    */
+       DiffMatchPatch.prototype.diffCommonPrefix = function (text1, text2) {
+               var pointermid, pointermax, pointermin, pointerstart;
+
+               // Quick check for common null cases.
+               if (!text1 || !text2 || text1.charAt(0) !== text2.charAt(0)) {
+                       return 0;
+               }
+
+               // Binary search.
+               // Performance analysis: https://neil.fraser.name/news/2007/10/09/
+               pointermin = 0;
+               pointermax = Math.min(text1.length, text2.length);
+               pointermid = pointermax;
+               pointerstart = 0;
+               while (pointermin < pointermid) {
+                       if (text1.substring(pointerstart, pointermid) === text2.substring(pointerstart, pointermid)) {
+                               pointermin = pointermid;
+                               pointerstart = pointermin;
+                       } else {
+                               pointermax = pointermid;
+                       }
+                       pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin);
+               }
+               return pointermid;
+       };
+
+       /**
+    * Determine the common suffix of two strings.
+    * @param {string} text1 First string.
+    * @param {string} text2 Second string.
+    * @return {number} The number of characters common to the end of each string.
+    */
+       DiffMatchPatch.prototype.diffCommonSuffix = function (text1, text2) {
+               var pointermid, pointermax, pointermin, pointerend;
+
+               // Quick check for common null cases.
+               if (!text1 || !text2 || text1.charAt(text1.length - 1) !== text2.charAt(text2.length - 1)) {
+                       return 0;
+               }
+
+               // Binary search.
+               // Performance analysis: https://neil.fraser.name/news/2007/10/09/
+               pointermin = 0;
+               pointermax = Math.min(text1.length, text2.length);
+               pointermid = pointermax;
+               pointerend = 0;
+               while (pointermin < pointermid) {
+                       if (text1.substring(text1.length - pointermid, text1.length - pointerend) === text2.substring(text2.length - pointermid, text2.length - pointerend)) {
+                               pointermin = pointermid;
+                               pointerend = pointermin;
+                       } else {
+                               pointermax = pointermid;
+                       }
+                       pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin);
+               }
+               return pointermid;
+       };
+
+       /**
+    * Find the differences between two texts.  Assumes that the texts do not
+    * have any common prefix or suffix.
+    * @param {string} text1 Old string to be diffed.
+    * @param {string} text2 New string to be diffed.
+    * @param {boolean} checklines Speedup flag.  If false, then don't run a
+    *     line-level diff first to identify the changed areas.
+    *     If true, then run a faster, slightly less optimal diff.
+    * @param {number} deadline Time when the diff should be complete by.
+    * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
+    * @private
+    */
+       DiffMatchPatch.prototype.diffCompute = function (text1, text2, checklines, deadline) {
+               var diffs, longtext, shorttext, i, hm, text1A, text2A, text1B, text2B, midCommon, diffsA, diffsB;
+
+               if (!text1) {
+
+                       // Just add some text (speedup).
+                       return [[DIFF_INSERT, text2]];
+               }
+
+               if (!text2) {
+
+                       // Just delete some text (speedup).
+                       return [[DIFF_DELETE, text1]];
+               }
+
+               longtext = text1.length > text2.length ? text1 : text2;
+               shorttext = text1.length > text2.length ? text2 : text1;
+               i = longtext.indexOf(shorttext);
+               if (i !== -1) {
+
+                       // Shorter text is inside the longer text (speedup).
+                       diffs = [[DIFF_INSERT, longtext.substring(0, i)], [DIFF_EQUAL, shorttext], [DIFF_INSERT, longtext.substring(i + shorttext.length)]];
+
+                       // Swap insertions for deletions if diff is reversed.
+                       if (text1.length > text2.length) {
+                               diffs[0][0] = diffs[2][0] = DIFF_DELETE;
+                       }
+                       return diffs;
+               }
+
+               if (shorttext.length === 1) {
+
+                       // Single character string.
+                       // After the previous speedup, the character can't be an equality.
+                       return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]];
+               }
+
+               // Check to see if the problem can be split in two.
+               hm = this.diffHalfMatch(text1, text2);
+               if (hm) {
+
+                       // A half-match was found, sort out the return data.
+                       text1A = hm[0];
+                       text1B = hm[1];
+                       text2A = hm[2];
+                       text2B = hm[3];
+                       midCommon = hm[4];
+
+                       // Send both pairs off for separate processing.
+                       diffsA = this.DiffMain(text1A, text2A, checklines, deadline);
+                       diffsB = this.DiffMain(text1B, text2B, checklines, deadline);
+
+                       // Merge the results.
+                       return diffsA.concat([[DIFF_EQUAL, midCommon]], diffsB);
+               }
+
+               if (checklines && text1.length > 100 && text2.length > 100) {
+                       return this.diffLineMode(text1, text2, deadline);
+               }
+
+               return this.diffBisect(text1, text2, deadline);
+       };
+
+       /**
+    * Do the two texts share a substring which is at least half the length of the
+    * longer text?
+    * This speedup can produce non-minimal diffs.
+    * @param {string} text1 First string.
+    * @param {string} text2 Second string.
+    * @return {Array.<string>} Five element Array, containing the prefix of
+    *     text1, the suffix of text1, the prefix of text2, the suffix of
+    *     text2 and the common middle.  Or null if there was no match.
+    * @private
+    */
+       DiffMatchPatch.prototype.diffHalfMatch = function (text1, text2) {
+               var longtext, shorttext, dmp, text1A, text2B, text2A, text1B, midCommon, hm1, hm2, hm;
+
+               longtext = text1.length > text2.length ? text1 : text2;
+               shorttext = text1.length > text2.length ? text2 : text1;
+               if (longtext.length < 4 || shorttext.length * 2 < longtext.length) {
+                       return null; // Pointless.
+               }
+               dmp = this; // 'this' becomes 'window' in a closure.
+
+               /**
+     * Does a substring of shorttext exist within longtext such that the substring
+     * is at least half the length of longtext?
+     * Closure, but does not reference any external variables.
+     * @param {string} longtext Longer string.
+     * @param {string} shorttext Shorter string.
+     * @param {number} i Start index of quarter length substring within longtext.
+     * @return {Array.<string>} Five element Array, containing the prefix of
+     *     longtext, the suffix of longtext, the prefix of shorttext, the suffix
+     *     of shorttext and the common middle.  Or null if there was no match.
+     * @private
+     */
+               function diffHalfMatchI(longtext, shorttext, i) {
+                       var seed, j, bestCommon, prefixLength, suffixLength, bestLongtextA, bestLongtextB, bestShorttextA, bestShorttextB;
+
+                       // Start with a 1/4 length substring at position i as a seed.
+                       seed = longtext.substring(i, i + Math.floor(longtext.length / 4));
+                       j = -1;
+                       bestCommon = "";
+                       while ((j = shorttext.indexOf(seed, j + 1)) !== -1) {
+                               prefixLength = dmp.diffCommonPrefix(longtext.substring(i), shorttext.substring(j));
+                               suffixLength = dmp.diffCommonSuffix(longtext.substring(0, i), shorttext.substring(0, j));
+                               if (bestCommon.length < suffixLength + prefixLength) {
+                                       bestCommon = shorttext.substring(j - suffixLength, j) + shorttext.substring(j, j + prefixLength);
+                                       bestLongtextA = longtext.substring(0, i - suffixLength);
+                                       bestLongtextB = longtext.substring(i + prefixLength);
+                                       bestShorttextA = shorttext.substring(0, j - suffixLength);
+                                       bestShorttextB = shorttext.substring(j + prefixLength);
+                               }
+                       }
+                       if (bestCommon.length * 2 >= longtext.length) {
+                               return [bestLongtextA, bestLongtextB, bestShorttextA, bestShorttextB, bestCommon];
+                       } else {
+                               return null;
+                       }
+               }
+
+               // First check if the second quarter is the seed for a half-match.
+               hm1 = diffHalfMatchI(longtext, shorttext, Math.ceil(longtext.length / 4));
+
+               // Check again based on the third quarter.
+               hm2 = diffHalfMatchI(longtext, shorttext, Math.ceil(longtext.length / 2));
+               if (!hm1 && !hm2) {
+                       return null;
+               } else if (!hm2) {
+                       hm = hm1;
+               } else if (!hm1) {
+                       hm = hm2;
+               } else {
+
+                       // Both matched.  Select the longest.
+                       hm = hm1[4].length > hm2[4].length ? hm1 : hm2;
+               }
+
+               // A half-match was found, sort out the return data.
+               if (text1.length > text2.length) {
+                       text1A = hm[0];
+                       text1B = hm[1];
+                       text2A = hm[2];
+                       text2B = hm[3];
+               } else {
+                       text2A = hm[0];
+                       text2B = hm[1];
+                       text1A = hm[2];
+                       text1B = hm[3];
+               }
+               midCommon = hm[4];
+               return [text1A, text1B, text2A, text2B, midCommon];
+       };
+
+       /**
+    * Do a quick line-level diff on both strings, then rediff the parts for
+    * greater accuracy.
+    * This speedup can produce non-minimal diffs.
+    * @param {string} text1 Old string to be diffed.
+    * @param {string} text2 New string to be diffed.
+    * @param {number} deadline Time when the diff should be complete by.
+    * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
+    * @private
+    */
+       DiffMatchPatch.prototype.diffLineMode = function (text1, text2, deadline) {
+               var a, diffs, linearray, pointer, countInsert, countDelete, textInsert, textDelete, j;
+
+               // Scan the text on a line-by-line basis first.
+               a = this.diffLinesToChars(text1, text2);
+               text1 = a.chars1;
+               text2 = a.chars2;
+               linearray = a.lineArray;
+
+               diffs = this.DiffMain(text1, text2, false, deadline);
+
+               // Convert the diff back to original text.
+               this.diffCharsToLines(diffs, linearray);
+
+               // Eliminate freak matches (e.g. blank lines)
+               this.diffCleanupSemantic(diffs);
+
+               // Rediff any replacement blocks, this time character-by-character.
+               // Add a dummy entry at the end.
+               diffs.push([DIFF_EQUAL, ""]);
+               pointer = 0;
+               countDelete = 0;
+               countInsert = 0;
+               textDelete = "";
+               textInsert = "";
+               while (pointer < diffs.length) {
+                       switch (diffs[pointer][0]) {
+                               case DIFF_INSERT:
+                                       countInsert++;
+                                       textInsert += diffs[pointer][1];
+                                       break;
+                               case DIFF_DELETE:
+                                       countDelete++;
+                                       textDelete += diffs[pointer][1];
+                                       break;
+                               case DIFF_EQUAL:
+
+                                       // Upon reaching an equality, check for prior redundancies.
+                                       if (countDelete >= 1 && countInsert >= 1) {
+
+                                               // Delete the offending records and add the merged ones.
+                                               diffs.splice(pointer - countDelete - countInsert, countDelete + countInsert);
+                                               pointer = pointer - countDelete - countInsert;
+                                               a = this.DiffMain(textDelete, textInsert, false, deadline);
+                                               for (j = a.length - 1; j >= 0; j--) {
+                                                       diffs.splice(pointer, 0, a[j]);
+                                               }
+                                               pointer = pointer + a.length;
+                                       }
+                                       countInsert = 0;
+                                       countDelete = 0;
+                                       textDelete = "";
+                                       textInsert = "";
+                                       break;
+                       }
+                       pointer++;
+               }
+               diffs.pop(); // Remove the dummy entry at the end.
+
+               return diffs;
+       };
+
+       /**
+    * Find the 'middle snake' of a diff, split the problem in two
+    * and return the recursively constructed diff.
+    * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations.
+    * @param {string} text1 Old string to be diffed.
+    * @param {string} text2 New string to be diffed.
+    * @param {number} deadline Time at which to bail if not yet complete.
+    * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
+    * @private
+    */
+       DiffMatchPatch.prototype.diffBisect = function (text1, text2, deadline) {
+               var text1Length, text2Length, maxD, vOffset, vLength, v1, v2, x, delta, front, k1start, k1end, k2start, k2end, k2Offset, k1Offset, x1, x2, y1, y2, d, k1, k2;
+
+               // Cache the text lengths to prevent multiple calls.
+               text1Length = text1.length;
+               text2Length = text2.length;
+               maxD = Math.ceil((text1Length + text2Length) / 2);
+               vOffset = maxD;
+               vLength = 2 * maxD;
+               v1 = new Array(vLength);
+               v2 = new Array(vLength);
+
+               // Setting all elements to -1 is faster in Chrome & Firefox than mixing
+               // integers and undefined.
+               for (x = 0; x < vLength; x++) {
+                       v1[x] = -1;
+                       v2[x] = -1;
+               }
+               v1[vOffset + 1] = 0;
+               v2[vOffset + 1] = 0;
+               delta = text1Length - text2Length;
+
+               // If the total number of characters is odd, then the front path will collide
+               // with the reverse path.
+               front = delta % 2 !== 0;
+
+               // Offsets for start and end of k loop.
+               // Prevents mapping of space beyond the grid.
+               k1start = 0;
+               k1end = 0;
+               k2start = 0;
+               k2end = 0;
+               for (d = 0; d < maxD; d++) {
+
+                       // Bail out if deadline is reached.
+                       if (new Date().getTime() > deadline) {
+                               break;
+                       }
+
+                       // Walk the front path one step.
+                       for (k1 = -d + k1start; k1 <= d - k1end; k1 += 2) {
+                               k1Offset = vOffset + k1;
+                               if (k1 === -d || k1 !== d && v1[k1Offset - 1] < v1[k1Offset + 1]) {
+                                       x1 = v1[k1Offset + 1];
+                               } else {
+                                       x1 = v1[k1Offset - 1] + 1;
+                               }
+                               y1 = x1 - k1;
+                               while (x1 < text1Length && y1 < text2Length && text1.charAt(x1) === text2.charAt(y1)) {
+                                       x1++;
+                                       y1++;
+                               }
+                               v1[k1Offset] = x1;
+                               if (x1 > text1Length) {
+
+                                       // Ran off the right of the graph.
+                                       k1end += 2;
+                               } else if (y1 > text2Length) {
+
+                                       // Ran off the bottom of the graph.
+                                       k1start += 2;
+                               } else if (front) {
+                                       k2Offset = vOffset + delta - k1;
+                                       if (k2Offset >= 0 && k2Offset < vLength && v2[k2Offset] !== -1) {
+
+                                               // Mirror x2 onto top-left coordinate system.
+                                               x2 = text1Length - v2[k2Offset];
+                                               if (x1 >= x2) {
+
+                                                       // Overlap detected.
+                                                       return this.diffBisectSplit(text1, text2, x1, y1, deadline);
+                                               }
+                                       }
+                               }
+                       }
+
+                       // Walk the reverse path one step.
+                       for (k2 = -d + k2start; k2 <= d - k2end; k2 += 2) {
+                               k2Offset = vOffset + k2;
+                               if (k2 === -d || k2 !== d && v2[k2Offset - 1] < v2[k2Offset + 1]) {
+                                       x2 = v2[k2Offset + 1];
+                               } else {
+                                       x2 = v2[k2Offset - 1] + 1;
+                               }
+                               y2 = x2 - k2;
+                               while (x2 < text1Length && y2 < text2Length && text1.charAt(text1Length - x2 - 1) === text2.charAt(text2Length - y2 - 1)) {
+                                       x2++;
+                                       y2++;
+                               }
+                               v2[k2Offset] = x2;
+                               if (x2 > text1Length) {
+
+                                       // Ran off the left of the graph.
+                                       k2end += 2;
+                               } else if (y2 > text2Length) {
+
+                                       // Ran off the top of the graph.
+                                       k2start += 2;
+                               } else if (!front) {
+                                       k1Offset = vOffset + delta - k2;
+                                       if (k1Offset >= 0 && k1Offset < vLength && v1[k1Offset] !== -1) {
+                                               x1 = v1[k1Offset];
+                                               y1 = vOffset + x1 - k1Offset;
+
+                                               // Mirror x2 onto top-left coordinate system.
+                                               x2 = text1Length - x2;
+                                               if (x1 >= x2) {
+
+                                                       // Overlap detected.
+                                                       return this.diffBisectSplit(text1, text2, x1, y1, deadline);
+                                               }
+                                       }
+                               }
+                       }
+               }
+
+               // Diff took too long and hit the deadline or
+               // number of diffs equals number of characters, no commonality at all.
+               return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]];
+       };
+
+       /**
+    * Given the location of the 'middle snake', split the diff in two parts
+    * and recurse.
+    * @param {string} text1 Old string to be diffed.
+    * @param {string} text2 New string to be diffed.
+    * @param {number} x Index of split point in text1.
+    * @param {number} y Index of split point in text2.
+    * @param {number} deadline Time at which to bail if not yet complete.
+    * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
+    * @private
+    */
+       DiffMatchPatch.prototype.diffBisectSplit = function (text1, text2, x, y, deadline) {
+               var text1a, text1b, text2a, text2b, diffs, diffsb;
+               text1a = text1.substring(0, x);
+               text2a = text2.substring(0, y);
+               text1b = text1.substring(x);
+               text2b = text2.substring(y);
+
+               // Compute both diffs serially.
+               diffs = this.DiffMain(text1a, text2a, false, deadline);
+               diffsb = this.DiffMain(text1b, text2b, false, deadline);
+
+               return diffs.concat(diffsb);
+       };
+
+       /**
+    * Reduce the number of edits by eliminating semantically trivial equalities.
+    * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
+    */
+       DiffMatchPatch.prototype.diffCleanupSemantic = function (diffs) {
+               var changes, equalities, equalitiesLength, lastequality, pointer, lengthInsertions2, lengthDeletions2, lengthInsertions1, lengthDeletions1, deletion, insertion, overlapLength1, overlapLength2;
+               changes = false;
+               equalities = []; // Stack of indices where equalities are found.
+               equalitiesLength = 0; // Keeping our own length var is faster in JS.
+               /** @type {?string} */
+               lastequality = null;
+
+               // Always equal to diffs[equalities[equalitiesLength - 1]][1]
+               pointer = 0; // Index of current position.
+
+               // Number of characters that changed prior to the equality.
+               lengthInsertions1 = 0;
+               lengthDeletions1 = 0;
+
+               // Number of characters that changed after the equality.
+               lengthInsertions2 = 0;
+               lengthDeletions2 = 0;
+               while (pointer < diffs.length) {
+                       if (diffs[pointer][0] === DIFF_EQUAL) {
+                               // Equality found.
+                               equalities[equalitiesLength++] = pointer;
+                               lengthInsertions1 = lengthInsertions2;
+                               lengthDeletions1 = lengthDeletions2;
+                               lengthInsertions2 = 0;
+                               lengthDeletions2 = 0;
+                               lastequality = diffs[pointer][1];
+                       } else {
+                               // An insertion or deletion.
+                               if (diffs[pointer][0] === DIFF_INSERT) {
+                                       lengthInsertions2 += diffs[pointer][1].length;
+                               } else {
+                                       lengthDeletions2 += diffs[pointer][1].length;
+                               }
+
+                               // Eliminate an equality that is smaller or equal to the edits on both
+                               // sides of it.
+                               if (lastequality && lastequality.length <= Math.max(lengthInsertions1, lengthDeletions1) && lastequality.length <= Math.max(lengthInsertions2, lengthDeletions2)) {
+
+                                       // Duplicate record.
+                                       diffs.splice(equalities[equalitiesLength - 1], 0, [DIFF_DELETE, lastequality]);
+
+                                       // Change second copy to insert.
+                                       diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT;
+
+                                       // Throw away the equality we just deleted.
+                                       equalitiesLength--;
+
+                                       // Throw away the previous equality (it needs to be reevaluated).
+                                       equalitiesLength--;
+                                       pointer = equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1;
+
+                                       // Reset the counters.
+                                       lengthInsertions1 = 0;
+                                       lengthDeletions1 = 0;
+                                       lengthInsertions2 = 0;
+                                       lengthDeletions2 = 0;
+                                       lastequality = null;
+                                       changes = true;
+                               }
+                       }
+                       pointer++;
+               }
+
+               // Normalize the diff.
+               if (changes) {
+                       this.diffCleanupMerge(diffs);
+               }
+
+               // Find any overlaps between deletions and insertions.
+               // e.g: <del>abcxxx</del><ins>xxxdef</ins>
+               //   -> <del>abc</del>xxx<ins>def</ins>
+               // e.g: <del>xxxabc</del><ins>defxxx</ins>
+               //   -> <ins>def</ins>xxx<del>abc</del>
+               // Only extract an overlap if it is as big as the edit ahead or behind it.
+               pointer = 1;
+               while (pointer < diffs.length) {
+                       if (diffs[pointer - 1][0] === DIFF_DELETE && diffs[pointer][0] === DIFF_INSERT) {
+                               deletion = diffs[pointer - 1][1];
+                               insertion = diffs[pointer][1];
+                               overlapLength1 = this.diffCommonOverlap(deletion, insertion);
+                               overlapLength2 = this.diffCommonOverlap(insertion, deletion);
+                               if (overlapLength1 >= overlapLength2) {
+                                       if (overlapLength1 >= deletion.length / 2 || overlapLength1 >= insertion.length / 2) {
+
+                                               // Overlap found.  Insert an equality and trim the surrounding edits.
+                                               diffs.splice(pointer, 0, [DIFF_EQUAL, insertion.substring(0, overlapLength1)]);
+                                               diffs[pointer - 1][1] = deletion.substring(0, deletion.length - overlapLength1);
+                                               diffs[pointer + 1][1] = insertion.substring(overlapLength1);
+                                               pointer++;
+                                       }
+                               } else {
+                                       if (overlapLength2 >= deletion.length / 2 || overlapLength2 >= insertion.length / 2) {
+
+                                               // Reverse overlap found.
+                                               // Insert an equality and swap and trim the surrounding edits.
+                                               diffs.splice(pointer, 0, [DIFF_EQUAL, deletion.substring(0, overlapLength2)]);
+
+                                               diffs[pointer - 1][0] = DIFF_INSERT;
+                                               diffs[pointer - 1][1] = insertion.substring(0, insertion.length - overlapLength2);
+                                               diffs[pointer + 1][0] = DIFF_DELETE;
+                                               diffs[pointer + 1][1] = deletion.substring(overlapLength2);
+                                               pointer++;
+                                       }
+                               }
+                               pointer++;
+                       }
+                       pointer++;
+               }
+       };
+
+       /**
+    * Determine if the suffix of one string is the prefix of another.
+    * @param {string} text1 First string.
+    * @param {string} text2 Second string.
+    * @return {number} The number of characters common to the end of the first
+    *     string and the start of the second string.
+    * @private
+    */
+       DiffMatchPatch.prototype.diffCommonOverlap = function (text1, text2) {
+               var text1Length, text2Length, textLength, best, length, pattern, found;
+
+               // Cache the text lengths to prevent multiple calls.
+               text1Length = text1.length;
+               text2Length = text2.length;
+
+               // Eliminate the null case.
+               if (text1Length === 0 || text2Length === 0) {
+                       return 0;
+               }
+
+               // Truncate the longer string.
+               if (text1Length > text2Length) {
+                       text1 = text1.substring(text1Length - text2Length);
+               } else if (text1Length < text2Length) {
+                       text2 = text2.substring(0, text1Length);
+               }
+               textLength = Math.min(text1Length, text2Length);
+
+               // Quick check for the worst case.
+               if (text1 === text2) {
+                       return textLength;
+               }
+
+               // Start by looking for a single character match
+               // and increase length until no match is found.
+               // Performance analysis: https://neil.fraser.name/news/2010/11/04/
+               best = 0;
+               length = 1;
+               while (true) {
+                       pattern = text1.substring(textLength - length);
+                       found = text2.indexOf(pattern);
+                       if (found === -1) {
+                               return best;
+                       }
+                       length += found;
+                       if (found === 0 || text1.substring(textLength - length) === text2.substring(0, length)) {
+                               best = length;
+                               length++;
+                       }
+               }
+       };
+
+       /**
+    * Split two texts into an array of strings.  Reduce the texts to a string of
+    * hashes where each Unicode character represents one line.
+    * @param {string} text1 First string.
+    * @param {string} text2 Second string.
+    * @return {{chars1: string, chars2: string, lineArray: !Array.<string>}}
+    *     An object containing the encoded text1, the encoded text2 and
+    *     the array of unique strings.
+    *     The zeroth element of the array of unique strings is intentionally blank.
+    * @private
+    */
+       DiffMatchPatch.prototype.diffLinesToChars = function (text1, text2) {
+               var lineArray, lineHash, chars1, chars2;
+               lineArray = []; // E.g. lineArray[4] === 'Hello\n'
+               lineHash = {}; // E.g. lineHash['Hello\n'] === 4
+
+               // '\x00' is a valid character, but various debuggers don't like it.
+               // So we'll insert a junk entry to avoid generating a null character.
+               lineArray[0] = "";
+
+               /**
+     * Split a text into an array of strings.  Reduce the texts to a string of
+     * hashes where each Unicode character represents one line.
+     * Modifies linearray and linehash through being a closure.
+     * @param {string} text String to encode.
+     * @return {string} Encoded string.
+     * @private
+     */
+               function diffLinesToCharsMunge(text) {
+                       var chars, lineStart, lineEnd, lineArrayLength, line;
+                       chars = "";
+
+                       // Walk the text, pulling out a substring for each line.
+                       // text.split('\n') would would temporarily double our memory footprint.
+                       // Modifying text would create many large strings to garbage collect.
+                       lineStart = 0;
+                       lineEnd = -1;
+
+                       // Keeping our own length variable is faster than looking it up.
+                       lineArrayLength = lineArray.length;
+                       while (lineEnd < text.length - 1) {
+                               lineEnd = text.indexOf("\n", lineStart);
+                               if (lineEnd === -1) {
+                                       lineEnd = text.length - 1;
+                               }
+                               line = text.substring(lineStart, lineEnd + 1);
+                               lineStart = lineEnd + 1;
+
+                               if (lineHash.hasOwnProperty ? lineHash.hasOwnProperty(line) : lineHash[line] !== undefined) {
+                                       chars += String.fromCharCode(lineHash[line]);
+                               } else {
+                                       chars += String.fromCharCode(lineArrayLength);
+                                       lineHash[line] = lineArrayLength;
+                                       lineArray[lineArrayLength++] = line;
+                               }
+                       }
+                       return chars;
+               }
+
+               chars1 = diffLinesToCharsMunge(text1);
+               chars2 = diffLinesToCharsMunge(text2);
+               return {
+                       chars1: chars1,
+                       chars2: chars2,
+                       lineArray: lineArray
+               };
+       };
+
+       /**
+    * Rehydrate the text in a diff from a string of line hashes to real lines of
+    * text.
+    * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
+    * @param {!Array.<string>} lineArray Array of unique strings.
+    * @private
+    */
+       DiffMatchPatch.prototype.diffCharsToLines = function (diffs, lineArray) {
+               var x, chars, text, y;
+               for (x = 0; x < diffs.length; x++) {
+                       chars = diffs[x][1];
+                       text = [];
+                       for (y = 0; y < chars.length; y++) {
+                               text[y] = lineArray[chars.charCodeAt(y)];
+                       }
+                       diffs[x][1] = text.join("");
+               }
+       };
+
+       /**
+    * Reorder and merge like edit sections.  Merge equalities.
+    * Any edit section can move as long as it doesn't cross an equality.
+    * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
+    */
+       DiffMatchPatch.prototype.diffCleanupMerge = function (diffs) {
+               var pointer, countDelete, countInsert, textInsert, textDelete, commonlength, changes, diffPointer, position;
+               diffs.push([DIFF_EQUAL, ""]); // Add a dummy entry at the end.
+               pointer = 0;
+               countDelete = 0;
+               countInsert = 0;
+               textDelete = "";
+               textInsert = "";
+
+               while (pointer < diffs.length) {
+                       switch (diffs[pointer][0]) {
+                               case DIFF_INSERT:
+                                       countInsert++;
+                                       textInsert += diffs[pointer][1];
+                                       pointer++;
+                                       break;
+                               case DIFF_DELETE:
+                                       countDelete++;
+                                       textDelete += diffs[pointer][1];
+                                       pointer++;
+                                       break;
+                               case DIFF_EQUAL:
+
+                                       // Upon reaching an equality, check for prior redundancies.
+                                       if (countDelete + countInsert > 1) {
+                                               if (countDelete !== 0 && countInsert !== 0) {
+
+                                                       // Factor out any common prefixes.
+                                                       commonlength = this.diffCommonPrefix(textInsert, textDelete);
+                                                       if (commonlength !== 0) {
+                                                               if (pointer - countDelete - countInsert > 0 && diffs[pointer - countDelete - countInsert - 1][0] === DIFF_EQUAL) {
+                                                                       diffs[pointer - countDelete - countInsert - 1][1] += textInsert.substring(0, commonlength);
+                                                               } else {
+                                                                       diffs.splice(0, 0, [DIFF_EQUAL, textInsert.substring(0, commonlength)]);
+                                                                       pointer++;
+                                                               }
+                                                               textInsert = textInsert.substring(commonlength);
+                                                               textDelete = textDelete.substring(commonlength);
+                                                       }
+
+                                                       // Factor out any common suffixies.
+                                                       commonlength = this.diffCommonSuffix(textInsert, textDelete);
+                                                       if (commonlength !== 0) {
+                                                               diffs[pointer][1] = textInsert.substring(textInsert.length - commonlength) + diffs[pointer][1];
+                                                               textInsert = textInsert.substring(0, textInsert.length - commonlength);
+                                                               textDelete = textDelete.substring(0, textDelete.length - commonlength);
+                                                       }
+                                               }
+
+                                               // Delete the offending records and add the merged ones.
+                                               if (countDelete === 0) {
+                                                       diffs.splice(pointer - countInsert, countDelete + countInsert, [DIFF_INSERT, textInsert]);
+                                               } else if (countInsert === 0) {
+                                                       diffs.splice(pointer - countDelete, countDelete + countInsert, [DIFF_DELETE, textDelete]);
+                                               } else {
+                                                       diffs.splice(pointer - countDelete - countInsert, countDelete + countInsert, [DIFF_DELETE, textDelete], [DIFF_INSERT, textInsert]);
+                                               }
+                                               pointer = pointer - countDelete - countInsert + (countDelete ? 1 : 0) + (countInsert ? 1 : 0) + 1;
+                                       } else if (pointer !== 0 && diffs[pointer - 1][0] === DIFF_EQUAL) {
+
+                                               // Merge this equality with the previous one.
+                                               diffs[pointer - 1][1] += diffs[pointer][1];
+                                               diffs.splice(pointer, 1);
+                                       } else {
+                                               pointer++;
+                                       }
+                                       countInsert = 0;
+                                       countDelete = 0;
+                                       textDelete = "";
+                                       textInsert = "";
+                                       break;
+                       }
+               }
+               if (diffs[diffs.length - 1][1] === "") {
+                       diffs.pop(); // Remove the dummy entry at the end.
+               }
+
+               // Second pass: look for single edits surrounded on both sides by equalities
+               // which can be shifted sideways to eliminate an equality.
+               // e.g: A<ins>BA</ins>C -> <ins>AB</ins>AC
+               changes = false;
+               pointer = 1;
+
+               // Intentionally ignore the first and last element (don't need checking).
+               while (pointer < diffs.length - 1) {
+                       if (diffs[pointer - 1][0] === DIFF_EQUAL && diffs[pointer + 1][0] === DIFF_EQUAL) {
+
+                               diffPointer = diffs[pointer][1];
+                               position = diffPointer.substring(diffPointer.length - diffs[pointer - 1][1].length);
+
+                               // This is a single edit surrounded by equalities.
+                               if (position === diffs[pointer - 1][1]) {
+
+                                       // Shift the edit over the previous equality.
+                                       diffs[pointer][1] = diffs[pointer - 1][1] + diffs[pointer][1].substring(0, diffs[pointer][1].length - diffs[pointer - 1][1].length);
+                                       diffs[pointer + 1][1] = diffs[pointer - 1][1] + diffs[pointer + 1][1];
+                                       diffs.splice(pointer - 1, 1);
+                                       changes = true;
+                               } else if (diffPointer.substring(0, diffs[pointer + 1][1].length) === diffs[pointer + 1][1]) {
+
+                                       // Shift the edit over the next equality.
+                                       diffs[pointer - 1][1] += diffs[pointer + 1][1];
+                                       diffs[pointer][1] = diffs[pointer][1].substring(diffs[pointer + 1][1].length) + diffs[pointer + 1][1];
+                                       diffs.splice(pointer + 1, 1);
+                                       changes = true;
+                               }
+                       }
+                       pointer++;
+               }
+
+               // If shifts were made, the diff needs reordering and another shift sweep.
+               if (changes) {
+                       this.diffCleanupMerge(diffs);
+               }
+       };
+
+       return function (o, n) {
+               var diff, output, text;
+               diff = new DiffMatchPatch();
+               output = diff.DiffMain(o, n);
+               diff.diffCleanupEfficiency(output);
+               text = diff.diffPrettyHtml(output);
+
+               return text;
+       };
+  }();
+
+}((function() { return this; }())));
index a602c32..3281735 100644 (file)
                } );
 
                // Collect views
-               allViews = {
+               allViews = $.extend( true, {
                        'default': {
                                title: mw.msg( 'rcfilters-filterlist-title' ),
                                groups: filterGroups
                        }
-               };
-
-               if ( views && mw.config.get( 'wgStructuredChangeFiltersEnableExperimentalViews' ) ) {
-                       // If we have extended views, add them in
-                       $.extend( true, allViews, views );
-               }
+               }, views );
 
                // Go over all views
                $.each( allViews, function ( viewName, viewData ) {
index cced3d5..3b8ebbd 100644 (file)
@@ -35,6 +35,7 @@
                        items = [],
                        uri = new mw.Uri(),
                        $changesList = $( '.mw-changeslist' ).first().contents(),
+                       experimentalViews = mw.config.get( 'wgStructuredChangeFiltersEnableExperimentalViews' ),
                        createFilterDataFromNumber = function ( num, convertedNumForLabel ) {
                                return {
                                        name: String( num ),
@@ -43,7 +44,7 @@
                        };
 
                // Prepare views
-               if ( namespaceStructure ) {
+               if ( namespaceStructure && experimentalViews ) {
                        items = [];
                        $.each( namespaceStructure, function ( namespaceID, label ) {
                                // Build and clean up the individual namespace items definition
@@ -74,7 +75,7 @@
                                } ]
                        };
                }
-               if ( tagList ) {
+               if ( tagList && experimentalViews ) {
                        views.tags = {
                                title: mw.msg( 'rcfilters-view-tags' ),
                                trigger: '#',
                                        'default': '7',
                                        filters: [
                                                // Hours (1, 2, 6, 12)
-                                               // TEMPORARY: Hide hours temporarily
-                                               // 0.04166, 0.0833, 0.25, 0.5,
+                                               0.04166, 0.0833, 0.25, 0.5,
                                                // Days
                                                1, 3, 7, 14, 30
                                        ].map( function ( num ) {
                                                        uriValue,
                                                        // In this case we don't want to round because it can be arbitrary
                                                        // weird numbers but we want to round to 2 decimal digits
-
-                                                       // HACK: Temporarily remove hours from UI
-                                                       // Number( uriValue ) < 1 ?
-                                                       //      ( Number( uriValue ) * 24 ).toFixed( 2 ) :
-                                                       //      Number( uriValue )
-                                                       Number( uriValue )
+                                                       Number( uriValue ) < 1 ?
+                                                               ( Number( uriValue ) * 24 ).toFixed( 2 ) :
+                                                               Number( uriValue )
                                                ) );
                                        } else {
                                                groupData.filters.push( createFilterDataFromNumber( uriValue, uriValue ) );
index 9da3f8c..e758f26 100644 (file)
 
                        $( '.rcfilters-container' ).append( filtersWidget.$element );
                        $( 'body' ).append( $overlay );
-
-                       // Set as ready
                        $( '.rcfilters-head' ).addClass( 'mw-rcfilters-ui-ready' );
-                       $( '.rcfilters-spinner' ).detach();
 
                        $( 'a.mw-helplink' ).attr(
                                'href',
index 305f3f9..9f3b809 100644 (file)
                &:not( .mw-rcfilters-ui-ready ) {
                        opacity: 0.5;
                        pointer-events: none;
+
+                       .rcoptions {
+                               display: none;
+                       }
                }
        }
 
                margin: 0;
        }
 
-       .mw-changeslist-empty {
-               // Hide the 'empty' message when we load rcfilters
-               // since we replace it anyways with a specific
-               // message of our own
-               display: none;
+       .mw-changeslist {
+               &-empty {
+                       // Hide the 'empty' message when we load rcfilters
+                       // since we replace it anyways with a specific
+                       // message of our own
+                       display: none;
+               }
+
+               &:not( .mw-rcfilters-ui-ready ) {
+                       opacity: 0.5;
+               }
        }
 
        .rcfilters-spinner {
                margin: -2em auto 0;
                width: 70px;
                opacity: 0.8;
-               display: block;
+               display: none;
                white-space: nowrap;
 
+               &:not( .mw-rcfilters-ui-ready ) {
+                       display: block;
+               }
+
                & .rcfilters-spinner-bounce,
                &:before,
                &:after {
index 0e9e843..c2533df 100644 (file)
@@ -3,7 +3,6 @@
         * List of changes
         *
         * @extends OO.ui.Widget
-        * @mixins OO.ui.mixin.PendingElement
         *
         * @constructor
         * @param {mw.rcfilters.dm.FiltersViewModel} filtersViewModel View model
@@ -23,8 +22,6 @@
 
                // Parent
                mw.rcfilters.ui.ChangesListWrapperWidget.parent.call( this, config );
-               // Mixin constructors
-               OO.ui.mixin.PendingElement.call( this, config );
 
                this.filtersViewModel = filtersViewModel;
                this.changesListViewModel = changesListViewModel;
@@ -51,7 +48,6 @@
        /* Initialization */
 
        OO.inheritClass( mw.rcfilters.ui.ChangesListWrapperWidget, OO.ui.Widget );
-       OO.mixinClass( mw.rcfilters.ui.ChangesListWrapperWidget, OO.ui.mixin.PendingElement );
 
        /**
         * Respond to the highlight feature being toggled on and off
@@ -80,7 +76,8 @@
         * Respond to changes list model invalidate
         */
        mw.rcfilters.ui.ChangesListWrapperWidget.prototype.onModelInvalidate = function () {
-               this.pushPending();
+               $( '.rcfilters-spinner' ).removeClass( 'mw-rcfilters-ui-ready' );
+               this.$element.removeClass( 'mw-rcfilters-ui-ready' );
        };
 
        /**
                                mw.hook( 'wikipage.content' ).fire( this.$element );
                        }
                }
-               this.popPending();
+
+               $( '.rcfilters-spinner' ).addClass( 'mw-rcfilters-ui-ready' );
+               this.$element.addClass( 'mw-rcfilters-ui-ready' );
        };
 
        /**
index 681b350..1569f38 100644 (file)
                if ( item ) {
                        this.button.setLabel(
                                mw.msg(
-                                       // Number( item.getParamName() ) < 1 ?
-                                       //      'rcfilters-days-show-hours' : 'rcfilters-days-show-days',
-
-                                       // Temporarily hide the functionality for hours, use days only
-                                       'rcfilters-days-show-days',
+                                       Number( item.getParamName() ) < 1 ?
+                                               'rcfilters-days-show-hours' : 'rcfilters-days-show-days',
                                        item.getLabel()
                                )
                        );
index ac841c0..6971df5 100644 (file)
@@ -16,8 +16,6 @@
 
                this.model = model;
 
-               /*
-               // HACK: Temporarily remove hours from UI
                this.hoursValuePicker = new mw.rcfilters.ui.ValuePickerWidget(
                        this.model,
                        {
                                label: mw.msg( 'rcfilters-hours-title' ),
                                itemFilter: function ( itemModel ) { return Number( itemModel.getParamName() ) < 1; }
                        }
-               );*/
+               );
                this.daysValuePicker = new mw.rcfilters.ui.ValuePickerWidget(
                        this.model,
                        {
                                classes: [ 'mw-rcfilters-ui-datePopupWidget-days' ],
-                               label: mw.msg( 'rcfilters-days-title' )
-                               // HACK: Temporarily remove hours from UI
-                               // itemFilter: function ( itemModel ) { return Number( itemModel.getParamName() ) >= 1; }
+                               label: mw.msg( 'rcfilters-days-title' ),
+                               itemFilter: function ( itemModel ) { return Number( itemModel.getParamName() ) >= 1; }
                        }
                );
 
                // Events
-               // HACK: Temporarily remove hours from UI
-               // this.hoursValuePicker.connect( this, { choose: [ 'emit', 'days' ] } );
+               this.hoursValuePicker.connect( this, { choose: [ 'emit', 'days' ] } );
                this.daysValuePicker.connect( this, { choose: [ 'emit', 'days' ] } );
 
                // Initialize
                this.$element
                        .addClass( 'mw-rcfilters-ui-datePopupWidget' )
                        .append(
-                               // HACK: Temporarily remove hours from UI
-                               // this.hoursValuePicker.$element,
+                               this.hoursValuePicker.$element,
                                this.daysValuePicker.$element
                        );
        };
index 0abaff2..ee8e0bc 100644 (file)
@@ -19,8 +19,6 @@
                mw.rcfilters.ui.FormWrapperWidget.parent.call( this, $.extend( {}, config, {
                        $element: $formRoot
                } ) );
-               // Mixin constructors
-               OO.ui.mixin.PendingElement.call( this, config );
 
                this.changeListModel = changeListModel;
                this.filtersModel = filtersModel;
@@ -48,7 +46,6 @@
        /* Initialization */
 
        OO.inheritClass( mw.rcfilters.ui.FormWrapperWidget, OO.ui.Widget );
-       OO.mixinClass( mw.rcfilters.ui.FormWrapperWidget, OO.ui.mixin.PendingElement );
 
        /**
         * Respond to link click
@@ -89,8 +86,8 @@
         * Respond to model invalidate
         */
        mw.rcfilters.ui.FormWrapperWidget.prototype.onChangesModelInvalidate = function () {
-               this.pushPending();
                this.$submitButton.prop( 'disabled', true );
+               this.$element.removeClass( 'mw-rcfilters-ui-ready' );
        };
 
        /**
         */
        mw.rcfilters.ui.FormWrapperWidget.prototype.onChangesModelUpdate = function ( $changesList, $fieldset, isInitialDOM ) {
                this.$submitButton.prop( 'disabled', false );
+               this.$element.removeClass( 'mw-rcfilters-ui-ready' );
 
                // Replace the entire fieldset
                this.$element.empty().append( $fieldset.contents() );
                }
 
                this.cleanUpFieldset();
-
-               this.popPending();
        };
 
        /**
index 8615edc..feed77f 100644 (file)
@@ -1036,6 +1036,9 @@ class ParserTestRunner {
                $linkHolderBatchSize =
                        self::getOptionValue( 'wgLinkHolderBatchSize', $opts, 1000 );
 
+               // Default to fallback skin, but allow it to be overridden
+               $skin = self::getOptionValue( 'skin', $opts, 'fallback' );
+
                $setup = [
                        'wgEnableUploads' => self::getOptionValue( 'wgEnableUploads', $opts, true ),
                        'wgLanguageCode' => $langCode,
@@ -1105,7 +1108,13 @@ class ParserTestRunner {
                $context = RequestContext::getMain();
                $context->setUser( $user );
                $context->setLanguage( $lang );
-               $teardown[] = function () use ( $context ) {
+               // And the skin!
+               $oldSkin = $context->getSkin();
+               $skinFactory = MediaWikiServices::getInstance()->getSkinFactory();
+               $context->setSkin( $skinFactory->makeSkin( $skin ) );
+               $context->setOutput( new OutputPage( $context ) );
+               $setup['wgOut'] = $context->getOutput();
+               $teardown[] = function () use ( $context, $oldSkin ) {
                        // Clear language conversion tables
                        $wrapper = TestingAccessWrapper::newFromObject(
                                $context->getLanguage()->getConverter()
@@ -1114,6 +1123,8 @@ class ParserTestRunner {
                        // Reset context to the restored globals
                        $context->setUser( $GLOBALS['wgUser'] );
                        $context->setLanguage( $GLOBALS['wgContLang'] );
+                       $context->setSkin( $oldSkin );
+                       $context->setOutput( $GLOBALS['wgOut'] );
                };
 
                $teardown[] = $this->executeSetupSnippets( $setup );
index e8ccd9d..f8ba742 100644 (file)
@@ -28364,3 +28364,25 @@ wgRawHtml=1
 <style data-mw-foobar="baz">.foo::after { content: "<bar>"; }</style>
 </div>
 !! end
+
+!! test
+Decoding of HTML entities in headings and links for IDs and link fragments (T103714)
+!! wikitext
+== A&B&amp;C&amp;amp;D&amp;amp;amp;E ==
+[[#A&B&amp;C&amp;amp;D&amp;amp;amp;E]]
+!! html/php
+<h2><span class="mw-headline" id="A.26B.26C.26amp.3BD.26amp.3Bamp.3BE">A&amp;B&amp;C&amp;amp;D&amp;amp;amp;E</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&amp;action=edit&amp;section=1" title="Edit section: A&amp;B&amp;C&amp;amp;D&amp;amp;amp;E">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
+<p><a href="#A.26B.26C.26D.26amp.3BE">#A&amp;B&amp;C&amp;amp;D&amp;amp;amp;E</a>
+</p>
+!! end
+
+!! test
+Decoding of HTML entities in indicator names for IDs (T104196)
+!! options
+showindicators
+!! wikitext
+<indicator name="1&2&amp;3&amp;amp;4&amp;amp;amp;5">Indicator</indicator>
+!! html/php
+1&2&3&amp;4&amp;amp;5=Indicator
+
+!! end
index 31cfa70..215d292 100644 (file)
@@ -1087,10 +1087,15 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
                        $page->doEditContent(
                                new WikitextContent( 'UTContent' ),
                                'UTPageSummary',
-                               EDIT_NEW,
+                               EDIT_NEW | EDIT_SUPPRESS_RC,
                                false,
                                $user
                        );
+                       // an edit always attempt to purge backlink links such as history
+                       // pages. That is unneccessary.
+                       JobQueueGroup::singleton()->get( 'htmlCacheUpdate' )->delete();
+                       // WikiPages::doEditUpdates randomly adds RC purges
+                       JobQueueGroup::singleton()->get( 'recentChangesUpdate' )->delete();
 
                        // doEditContent() probably started the session via
                        // User::loadFromSession(). Close it now.
index abcf1d4..6d093b0 100644 (file)
@@ -343,6 +343,41 @@ class SanitizerTest extends MediaWikiTestCase {
                ];
        }
 
+       /**
+        * Test Sanitizer::escapeId
+        *
+        * @dataProvider provideEscapeId
+        * @covers Sanitizer::escapeId
+        */
+       public function testEscapeId( $input, $output ) {
+               $this->assertEquals(
+                       $output,
+                       Sanitizer::escapeId( $input, [ 'noninitial', 'legacy' ] )
+               );
+       }
+
+       public static function provideEscapeId() {
+               return [
+                       [ '+', '.2B' ],
+                       [ '&', '.26' ],
+                       [ '=', '.3D' ],
+                       [ ':', ':' ],
+                       [ ';', '.3B' ],
+                       [ '@', '.40' ],
+                       [ '$', '.24' ],
+                       [ '-_.', '-_.' ],
+                       [ '!', '.21' ],
+                       [ '*', '.2A' ],
+                       [ '/', '.2F' ],
+                       [ '[]', '.5B.5D' ],
+                       [ '<>', '.3C.3E' ],
+                       [ '\'', '.27' ],
+                       [ '§', '.C2.A7' ],
+                       [ 'Test:A & B/Here', 'Test:A_.26_B.2FHere' ],
+                       [ 'A&B&amp;C&amp;amp;D&amp;amp;amp;E', 'A.26B.26C.26amp.3BD.26amp.3Bamp.3BE' ],
+               ];
+       }
+
        /**
         * Test escapeIdReferenceList for consistency with escapeId
         *
diff --git a/tests/phpunit/includes/SiteStatsTest.php b/tests/phpunit/includes/SiteStatsTest.php
new file mode 100644 (file)
index 0000000..ea476a7
--- /dev/null
@@ -0,0 +1,36 @@
+<?php
+
+class SiteStatsTest extends MediaWikiTestCase {
+
+       /**
+        * @covers SiteStats::jobs
+        */
+       function testJobsCountGetCached() {
+               $this->setService( 'MainWANObjectCache',
+                       new WANObjectCache( [ 'cache' => new HashBagOStuff() ] ) );
+               $cache = \MediaWiki\MediaWikiServices::getInstance()->getMainWANObjectCache();
+               $jobq = JobQueueGroup::singleton();
+
+               $jobq->push( new NullJob( Title::newMainPage(), [] ) );
+               $this->assertEquals( 1, SiteStats::jobs(),
+                        'A single job enqueued bumps jobscount stat to 1' );
+
+               $jobq->push( new NullJob( Title::newMainPage(), [] ) );
+               $this->assertEquals( 1, SiteStats::jobs(),
+                       'SiteStats::jobs() count does not reflect addition ' .
+                       'of a second job (cached)'
+               );
+
+               $jobq->get( 'null' )->delete();  // clear jobqueue
+               $this->assertEquals( 0, $jobq->get( 'null' )->getSize(),
+                       'Job queue for NullJob has been cleaned' );
+
+               $cache->delete( $cache->makeKey( 'SiteStats', 'jobscount' ) );
+               $this->assertEquals( 1, SiteStats::jobs(),
+                       'jobs count is kept in process cache' );
+
+               $cache->clearProcessCache();
+               $this->assertEquals( 0, SiteStats::jobs() );
+       }
+
+}
diff --git a/tests/phpunit/includes/changetags/ChangeTagsTest.php b/tests/phpunit/includes/changetags/ChangeTagsTest.php
new file mode 100644 (file)
index 0000000..723d685
--- /dev/null
@@ -0,0 +1,247 @@
+<?php
+
+/**
+ * @covers ChangeTags
+ */
+class ChangeTagsTest extends MediaWikiTestCase {
+
+       // TODO only modifyDisplayQuery is tested, nothing else is
+
+       /** @dataProvider provideModifyDisplayQuery */
+       public function testModifyDisplayQuery( $origQuery, $filter_tag, $useTags, $modifiedQuery ) {
+               $this->setMwGlobals( 'wgUseTagFilter', $useTags );
+               // HACK resolve deferred group concats (see comment in provideModifyDisplayQuery)
+               if ( isset( $modifiedQuery['fields']['ts_tags'] ) ) {
+                       $modifiedQuery['fields']['ts_tags'] = call_user_func_array(
+                               [ wfGetDB( DB_REPLICA ), 'buildGroupConcatField' ],
+                               $modifiedQuery['fields']['ts_tags']
+                       );
+               }
+               if ( isset( $modifiedQuery['exception'] ) ) {
+                       $this->setExpectedException( $modifiedQuery['exception'] );
+               }
+               ChangeTags::modifyDisplayQuery(
+                       $origQuery['tables'],
+                       $origQuery['fields'],
+                       $origQuery['conds'],
+                       $origQuery['join_conds'],
+                       $origQuery['options'],
+                       $filter_tag
+               );
+               if ( !isset( $modifiedQuery['exception'] ) ) {
+                       $this->assertArrayEquals(
+                               $modifiedQuery,
+                               $origQuery,
+                               /* ordered = */ false,
+                               /* named = */ true
+                       );
+               }
+       }
+
+       public function provideModifyDisplayQuery() {
+               // HACK if we call $dbr->buildGroupConcatField() now, it will return the wrong table names
+               // We have to have the test runner call it instead
+               $groupConcats = [
+                       'recentchanges' => [ ',', 'change_tag', 'ct_tag', 'ct_rc_id=rc_id' ],
+                       'logging' => [ ',', 'change_tag', 'ct_tag', 'ct_log_id=log_id' ],
+                       'revision' => [ ',', 'change_tag', 'ct_tag', 'ct_rev_id=rev_id' ],
+                       'archive' => [ ',', 'change_tag', 'ct_tag', 'ct_rev_id=ar_rev_id' ],
+               ];
+
+               return [
+                       'simple recentchanges query' => [
+                               [
+                                       'tables' => [ 'recentchanges' ],
+                                       'fields' => [ 'rc_id', 'rc_timestamp' ],
+                                       'conds' => [ "rc_timestamp > '20170714183203'" ],
+                                       'join_conds' => [],
+                                       'options' => [ 'ORDER BY' => 'rc_timestamp DESC' ],
+                               ],
+                               '', // no tag filter
+                               true, // tag filtering enabled
+                               [
+                                       'tables' => [ 'recentchanges' ],
+                                       'fields' => [ 'rc_id', 'rc_timestamp', 'ts_tags' => $groupConcats['recentchanges'] ],
+                                       'conds' => [ "rc_timestamp > '20170714183203'" ],
+                                       'join_conds' => [],
+                                       'options' => [ 'ORDER BY' => 'rc_timestamp DESC' ],
+                               ]
+                       ],
+                       'simple query with strings' => [
+                               [
+                                       'tables' => 'recentchanges',
+                                       'fields' => 'rc_id',
+                                       'conds' => "rc_timestamp > '20170714183203'",
+                                       'join_conds' => [],
+                                       'options' => 'ORDER BY rc_timestamp DESC',
+                               ],
+                               '', // no tag filter
+                               true, // tag filtering enabled
+                               [
+                                       'tables' => [ 'recentchanges' ],
+                                       'fields' => [ 'rc_id', 'ts_tags' => $groupConcats['recentchanges'] ],
+                                       'conds' => [ "rc_timestamp > '20170714183203'" ],
+                                       'join_conds' => [],
+                                       'options' => [ 'ORDER BY rc_timestamp DESC' ],
+                               ]
+                       ],
+                       'recentchanges query with single tag filter' => [
+                               [
+                                       'tables' => [ 'recentchanges' ],
+                                       'fields' => [ 'rc_id', 'rc_timestamp' ],
+                                       'conds' => [ "rc_timestamp > '20170714183203'" ],
+                                       'join_conds' => [],
+                                       'options' => [ 'ORDER BY' => 'rc_timestamp DESC' ],
+                               ],
+                               'foo',
+                               true, // tag filtering enabled
+                               [
+                                       'tables' => [ 'recentchanges', 'change_tag' ],
+                                       'fields' => [ 'rc_id', 'rc_timestamp', 'ts_tags' => $groupConcats['recentchanges'] ],
+                                       'conds' => [ "rc_timestamp > '20170714183203'", 'ct_tag' => 'foo' ],
+                                       'join_conds' => [ 'change_tag' => [ 'INNER JOIN', 'ct_rc_id=rc_id' ] ],
+                                       'options' => [ 'ORDER BY' => 'rc_timestamp DESC' ],
+                               ]
+                       ],
+                       'logging query with single tag filter and strings' => [
+                               [
+                                       'tables' => 'logging',
+                                       'fields' => 'log_id',
+                                       'conds' => "log_timestamp > '20170714183203'",
+                                       'join_conds' => [],
+                                       'options' => 'ORDER BY log_timestamp DESC',
+                               ],
+                               'foo',
+                               true, // tag filtering enabled
+                               [
+                                       'tables' => [ 'logging', 'change_tag' ],
+                                       'fields' => [ 'log_id', 'ts_tags' => $groupConcats['logging'] ],
+                                       'conds' => [ "log_timestamp > '20170714183203'", 'ct_tag' => 'foo' ],
+                                       'join_conds' => [ 'change_tag' => [ 'INNER JOIN', 'ct_log_id=log_id' ] ],
+                                       'options' => [ 'ORDER BY log_timestamp DESC' ],
+                               ]
+                       ],
+                       'revision query with single tag filter' => [
+                               [
+                                       'tables' => [ 'revision' ],
+                                       'fields' => [ 'rev_id', 'rev_timestamp' ],
+                                       'conds' => [ "rev_timestamp > '20170714183203'" ],
+                                       'join_conds' => [],
+                                       'options' => [ 'ORDER BY' => 'rev_timestamp DESC' ],
+                               ],
+                               'foo',
+                               true, // tag filtering enabled
+                               [
+                                       'tables' => [ 'revision', 'change_tag' ],
+                                       'fields' => [ 'rev_id', 'rev_timestamp', 'ts_tags' => $groupConcats['revision'] ],
+                                       'conds' => [ "rev_timestamp > '20170714183203'", 'ct_tag' => 'foo' ],
+                                       'join_conds' => [ 'change_tag' => [ 'INNER JOIN', 'ct_rev_id=rev_id' ] ],
+                                       'options' => [ 'ORDER BY' => 'rev_timestamp DESC' ],
+                               ]
+                       ],
+                       'archive query with single tag filter' => [
+                               [
+                                       'tables' => [ 'archive' ],
+                                       'fields' => [ 'ar_id', 'ar_timestamp' ],
+                                       'conds' => [ "ar_timestamp > '20170714183203'" ],
+                                       'join_conds' => [],
+                                       'options' => [ 'ORDER BY' => 'ar_timestamp DESC' ],
+                               ],
+                               'foo',
+                               true, // tag filtering enabled
+                               [
+                                       'tables' => [ 'archive', 'change_tag' ],
+                                       'fields' => [ 'ar_id', 'ar_timestamp', 'ts_tags' => $groupConcats['archive'] ],
+                                       'conds' => [ "ar_timestamp > '20170714183203'", 'ct_tag' => 'foo' ],
+                                       'join_conds' => [ 'change_tag' => [ 'INNER JOIN', 'ct_rev_id=ar_rev_id' ] ],
+                                       'options' => [ 'ORDER BY' => 'ar_timestamp DESC' ],
+                               ]
+                       ],
+                       'unsupported table name throws exception (even without tag filter)' => [
+                               [
+                                       'tables' => [ 'foobar' ],
+                                       'fields' => [ 'fb_id', 'fb_timestamp' ],
+                                       'conds' => [ "fb_timestamp > '20170714183203'" ],
+                                       'join_conds' => [],
+                                       'options' => [ 'ORDER BY' => 'fb_timestamp DESC' ],
+                               ],
+                               '',
+                               true, // tag filtering enabled
+                               [ 'exception' => MWException::class ]
+                       ],
+                       'tag filter ignored when tag filtering is disabled' => [
+                               [
+                                       'tables' => [ 'archive' ],
+                                       'fields' => [ 'ar_id', 'ar_timestamp' ],
+                                       'conds' => [ "ar_timestamp > '20170714183203'" ],
+                                       'join_conds' => [],
+                                       'options' => [ 'ORDER BY' => 'ar_timestamp DESC' ],
+                               ],
+                               'foo',
+                               false, // tag filtering disabled
+                               [
+                                       'tables' => [ 'archive' ],
+                                       'fields' => [ 'ar_id', 'ar_timestamp', 'ts_tags' => $groupConcats['archive'] ],
+                                       'conds' => [ "ar_timestamp > '20170714183203'" ],
+                                       'join_conds' => [],
+                                       'options' => [ 'ORDER BY' => 'ar_timestamp DESC' ],
+                               ]
+                       ],
+                       'recentchanges query with multiple tag filter' => [
+                               [
+                                       'tables' => [ 'recentchanges' ],
+                                       'fields' => [ 'rc_id', 'rc_timestamp' ],
+                                       'conds' => [ "rc_timestamp > '20170714183203'" ],
+                                       'join_conds' => [],
+                                       'options' => [ 'ORDER BY' => 'rc_timestamp DESC' ],
+                               ],
+                               [ 'foo', 'bar' ],
+                               true, // tag filtering enabled
+                               [
+                                       'tables' => [ 'recentchanges', 'change_tag' ],
+                                       'fields' => [ 'rc_id', 'rc_timestamp', 'ts_tags' => $groupConcats['recentchanges'] ],
+                                       'conds' => [ "rc_timestamp > '20170714183203'", 'ct_tag' => [ 'foo', 'bar' ] ],
+                                       'join_conds' => [ 'change_tag' => [ 'INNER JOIN', 'ct_rc_id=rc_id' ] ],
+                                       'options' => [ 'ORDER BY' => 'rc_timestamp DESC', 'DISTINCT' ],
+                               ]
+                       ],
+                       'recentchanges query with multiple tag filter that already has DISTINCT' => [
+                               [
+                                       'tables' => [ 'recentchanges' ],
+                                       'fields' => [ 'rc_id', 'rc_timestamp' ],
+                                       'conds' => [ "rc_timestamp > '20170714183203'" ],
+                                       'join_conds' => [],
+                                       'options' => [ 'DISTINCT', 'ORDER BY' => 'rc_timestamp DESC' ],
+                               ],
+                               [ 'foo', 'bar' ],
+                               true, // tag filtering enabled
+                               [
+                                       'tables' => [ 'recentchanges', 'change_tag' ],
+                                       'fields' => [ 'rc_id', 'rc_timestamp', 'ts_tags' => $groupConcats['recentchanges'] ],
+                                       'conds' => [ "rc_timestamp > '20170714183203'", 'ct_tag' => [ 'foo', 'bar' ] ],
+                                       'join_conds' => [ 'change_tag' => [ 'INNER JOIN', 'ct_rc_id=rc_id' ] ],
+                                       'options' => [ 'DISTINCT', 'ORDER BY' => 'rc_timestamp DESC' ],
+                               ]
+                       ],
+                       'recentchanges query with multiple tag filter with strings' => [
+                               [
+                                       'tables' => 'recentchanges',
+                                       'fields' => 'rc_id',
+                                       'conds' => "rc_timestamp > '20170714183203'",
+                                       'join_conds' => [],
+                                       'options' => 'ORDER BY rc_timestamp DESC',
+                               ],
+                               [ 'foo', 'bar' ],
+                               true, // tag filtering enabled
+                               [
+                                       'tables' => [ 'recentchanges', 'change_tag' ],
+                                       'fields' => [ 'rc_id', 'ts_tags' => $groupConcats['recentchanges'] ],
+                                       'conds' => [ "rc_timestamp > '20170714183203'", 'ct_tag' => [ 'foo', 'bar' ] ],
+                                       'join_conds' => [ 'change_tag' => [ 'INNER JOIN', 'ct_rc_id=rc_id' ] ],
+                                       'options' => [ 'ORDER BY rc_timestamp DESC', 'DISTINCT' ],
+                               ]
+                       ],
+               ];
+       }
+
+}
index 8e57a01..19cffa2 100644 (file)
@@ -364,22 +364,125 @@ class EtcConfigTest extends PHPUnit_Framework_TestCase {
 
        public static function provideFetchFromServer() {
                return [
-                       [
+                       '200 OK - Success' => [
+                               'http' => [
+                                       'code' => 200,
+                                       'reason' => 'OK',
+                                       'headers' => [],
+                                       'body' => json_encode( [ 'node' => [ 'nodes' => [
+                                               [
+                                                       'key' => '/example/foo',
+                                                       'value' => json_encode( [ 'val' => true ] )
+                                               ],
+                                       ] ] ] ),
+                                       'error' => '',
+                               ],
+                               'expect' => [
+                                       [ 'foo' => true ], // data
+                                       null,
+                                       false // retry
+                               ],
+                       ],
+                       '200 OK - Skip dir' => [
                                'http' => [
                                        'code' => 200,
                                        'reason' => 'OK',
-                                       'headers' => [
-                                               'content-length' => 0,
-                                       ],
+                                       'headers' => [],
+                                       'body' => json_encode( [ 'node' => [ 'nodes' => [
+                                               [
+                                                       'key' => '/example/foo',
+                                                       'value' => json_encode( [ 'val' => true ] )
+                                               ],
+                                               [
+                                                       'key' => '/example/sub',
+                                                       'dir' => true
+                                               ],
+                                               [
+                                                       'key' => '/example/bar',
+                                                       'value' => json_encode( [ 'val' => false ] )
+                                               ],
+                                       ] ] ] ),
+                                       'error' => '',
+                               ],
+                               'expect' => [
+                                       [ 'foo' => true, 'bar' => false ], // data
+                                       null,
+                                       false // retry
+                               ],
+                       ],
+                       '200 OK - Bad value' => [
+                               'http' => [
+                                       'code' => 200,
+                                       'reason' => 'OK',
+                                       'headers' => [],
+                                       'body' => json_encode( [ 'node' => [ 'nodes' => [
+                                               [
+                                                       'key' => '/example/foo',
+                                                       'value' => ';"broken{value'
+                                               ]
+                                       ] ] ] ),
+                                       'error' => '',
+                               ],
+                               'expect' => [
+                                       null, // data
+                                       "Failed to parse value for 'foo'.",
+                                       false // retry
+                               ],
+                       ],
+                       '200 OK - Empty node list' => [
+                               'http' => [
+                                       'code' => 200,
+                                       'reason' => 'OK',
+                                       'headers' => [],
+                                       'body' => '{"node":{"nodes":[]}}',
+                                       'error' => '',
+                               ],
+                               'expect' => [
+                                       [], // data
+                                       null,
+                                       false // retry
+                               ],
+                       ],
+                       '200 OK - Invalid JSON' => [
+                               'http' => [
+                                       'code' => 200,
+                                       'reason' => 'OK',
+                                       'headers' => [ 'content-length' => 0 ],
                                        'body' => '',
                                        'error' => '(curl error: no status set)',
                                ],
                                'expect' => [
-                                       // FIXME: Returning 4 values instead of 3
-                                       null,
-                                       200,
+                                       null, // data
                                        "Unexpected JSON response; missing 'nodes' list.",
-                                       false
+                                       false // retry
+                               ],
+                       ],
+                       '404 Not Found' => [
+                               'http' => [
+                                       'code' => 404,
+                                       'reason' => 'Not Found',
+                                       'headers' => [ 'content-length' => 0 ],
+                                       'body' => '',
+                                       'error' => '',
+                               ],
+                               'expect' => [
+                                       null, // data
+                                       'HTTP 404 (Not Found)',
+                                       false // retry
+                               ],
+                       ],
+                       '400 Bad Request - custom error' => [
+                               'http' => [
+                                       'code' => 400,
+                                       'reason' => 'Bad Request',
+                                       'headers' => [ 'content-length' => 0 ],
+                                       'body' => '',
+                                       'error' => 'No good reason',
+                               ],
+                               'expect' => [
+                                       null, // data
+                                       'No good reason',
+                                       true // retry
                                ],
                        ],
                ];
@@ -387,6 +490,7 @@ class EtcConfigTest extends PHPUnit_Framework_TestCase {
 
        /**
         * @covers EtcdConfig::fetchAllFromEtcdServer
+        * @covers EtcdConfig::unserialize
         * @dataProvider provideFetchFromServer
         */
        public function testFetchFromServer( array $httpResponse, array $expected ) {
index 775709f..4a9f6cc 100644 (file)
@@ -81,22 +81,26 @@ class MultiWriteBagOStuffTest extends MediaWikiTestCase {
         */
        public function testSetDelayed() {
                $key = wfRandomString();
-               $value = wfRandomString();
+               $value = (object)[ 'v' => wfRandomString() ];
+               $expectValue = clone $value;
 
                // XXX: DeferredUpdates bound to transactions in CLI mode
                $dbw = wfGetDB( DB_MASTER );
                $dbw->begin();
                $this->cache->set( $key, $value );
 
+               // Test that later changes to $value don't affect the saved value (e.g. T168040)
+               $value->v = 'bogus';
+
                // Set in tier 1
-               $this->assertEquals( $value, $this->cache1->get( $key ), 'Written to tier 1' );
+               $this->assertEquals( $expectValue, $this->cache1->get( $key ), 'Written to tier 1' );
                // Not yet set in tier 2
                $this->assertEquals( false, $this->cache2->get( $key ), 'Not written to tier 2' );
 
                $dbw->commit();
 
                // Set in tier 2
-               $this->assertEquals( $value, $this->cache2->get( $key ), 'Written to tier 2' );
+               $this->assertEquals( $expectValue, $this->cache2->get( $key ), 'Written to tier 2' );
        }
 
        /**
index 57666bd..f519772 100644 (file)
@@ -22,13 +22,18 @@ class DatabaseSQLTest extends PHPUnit_Framework_TestCase {
                );
        }
 
-       protected function assertLastSqlDb( $sqlText, $db ) {
+       protected function assertLastSqlDb( $sqlText, DatabaseTestHelper $db ) {
                $this->assertEquals( $sqlText, $db->getLastSqls() );
        }
 
        /**
         * @dataProvider provideSelect
         * @covers Wikimedia\Rdbms\Database::select
+        * @covers Wikimedia\Rdbms\Database::selectSQLText
+        * @covers Wikimedia\Rdbms\Database::tableNamesWithIndexClauseOrJOIN
+        * @covers Wikimedia\Rdbms\Database::makeSelectOptions
+        * @covers Wikimedia\Rdbms\Database::makeOrderBy
+        * @covers Wikimedia\Rdbms\Database::makeGroupByWithHaving
         */
        public function testSelect( $sql, $sqlText ) {
                $this->database->select(
@@ -54,6 +59,23 @@ class DatabaseSQLTest extends PHPUnit_Framework_TestCase {
                                        "FROM table " .
                                        "WHERE alias = 'text'"
                        ],
+                       [
+                               [
+                                       // 'tables' with space prepended indicates pre-escaped table name
+                                       'tables' => ' table LEFT JOIN table2',
+                                       'fields' => [ 'field' ],
+                                       'conds' => [ 'field' => 'text' ],
+                               ],
+                               "SELECT field FROM  table LEFT JOIN table2 WHERE field = 'text'"
+                       ],
+                       [
+                               [
+                                       // Empty 'tables' is allowed
+                                       'tables' => '',
+                                       'fields' => [ 'SPECIAL_QUERY()' ],
+                               ],
+                               "SELECT SPECIAL_QUERY()"
+                       ],
                        [
                                [
                                        'tables' => 'table',
@@ -129,12 +151,38 @@ class DatabaseSQLTest extends PHPUnit_Framework_TestCase {
                                        "FROM table " .
                                        "WHERE alias IN ('1','2','3','4')"
                        ],
+                       [
+                               [
+                                       'tables' => 'table',
+                                       'fields' => [ 'field' ],
+                                       'options' => [ 'DISTINCT', 'LOCK IN SHARE MODE' ],
+                               ],
+                               "SELECT DISTINCT field FROM table      LOCK IN SHARE MODE"
+                       ],
+                       [
+                               [
+                                       'tables' => 'table',
+                                       'fields' => [ 'field' ],
+                                       'options' => [ 'EXPLAIN' => true ],
+                               ],
+                               'EXPLAIN SELECT field FROM table'
+                       ],
+                       [
+                               [
+                                       'tables' => 'table',
+                                       'fields' => [ 'field' ],
+                                       'options' => [ 'FOR UPDATE' ],
+                               ],
+                               "SELECT field FROM table      FOR UPDATE"
+                       ],
                ];
        }
 
        /**
         * @dataProvider provideUpdate
         * @covers Wikimedia\Rdbms\Database::update
+        * @covers Wikimedia\Rdbms\Database::makeUpdateOptions
+        * @covers Wikimedia\Rdbms\Database::makeUpdateOptionsArray
         */
        public function testUpdate( $sql, $sqlText ) {
                $this->database->update(
@@ -303,6 +351,7 @@ class DatabaseSQLTest extends PHPUnit_Framework_TestCase {
        /**
         * @dataProvider provideInsert
         * @covers Wikimedia\Rdbms\Database::insert
+        * @covers Wikimedia\Rdbms\Database::makeInsertOptions
         */
        public function testInsert( $sql, $sqlText ) {
                $this->database->insert(
@@ -356,6 +405,7 @@ class DatabaseSQLTest extends PHPUnit_Framework_TestCase {
        /**
         * @dataProvider provideInsertSelect
         * @covers Wikimedia\Rdbms\Database::insertSelect
+        * @covers Wikimedia\Rdbms\Database::nativeInsertSelect
         */
        public function testInsertSelect( $sql, $sqlTextNative, $sqlSelect, $sqlInsert ) {
                $this->database->insertSelect(
@@ -673,6 +723,7 @@ class DatabaseSQLTest extends PHPUnit_Framework_TestCase {
        /**
         * @dataProvider provideBuildLike
         * @covers Wikimedia\Rdbms\Database::buildLike
+        * @covers Wikimedia\Rdbms\Database::escapeLikeInternal
         */
        public function testBuildLike( $array, $sqlText ) {
                $this->assertEquals( trim( $this->database->buildLike(
@@ -921,6 +972,7 @@ class DatabaseSQLTest extends PHPUnit_Framework_TestCase {
 
        /**
         * @covers Wikimedia\Rdbms\Database::commit
+        * @covers Wikimedia\Rdbms\Database::doCommit
         */
        public function testTransactionCommit() {
                $this->database->begin( __METHOD__ );
@@ -930,6 +982,7 @@ class DatabaseSQLTest extends PHPUnit_Framework_TestCase {
 
        /**
         * @covers Wikimedia\Rdbms\Database::rollback
+        * @covers Wikimedia\Rdbms\Database::doRollback
         */
        public function testTransactionRollback() {
                $this->database->begin( __METHOD__ );
@@ -1035,6 +1088,9 @@ class DatabaseSQLTest extends PHPUnit_Framework_TestCase {
                ];
        }
 
+       /**
+        * @covers Wikimedia\Rdbms\Database::registerTempTableOperation
+        */
        public function testSessionTempTables() {
                $temp1 = $this->database->tableName( 'tmp_table_1' );
                $temp2 = $this->database->tableName( 'tmp_table_2' );
index 9bea7ff..70b6c36 100644 (file)
@@ -1,6 +1,7 @@
 <?php
 
 use Wikimedia\Rdbms\IDatabase;
+use Wikimedia\Rdbms\LBFactorySingle;
 use Wikimedia\Rdbms\TransactionProfiler;
 use Wikimedia\TestingAccessWrapper;
 
@@ -135,6 +136,71 @@ class DatabaseTest extends PHPUnit_Framework_TestCase {
                $this->assertFalse( $db->getFlag( DBO_TRX ), 'DBO_TRX restored to default' );
        }
 
+       /**
+        * @covers Wikimedia\Rdbms\Database::onTransactionPreCommitOrIdle
+        * @covers Wikimedia\Rdbms\Database::runOnTransactionPreCommitCallbacks
+        */
+       public function testTransactionPreCommitOrIdle() {
+               $db = $this->getMockDB( [ 'isOpen' ] );
+               $db->method( 'isOpen' )->willReturn( true );
+               $db->clearFlag( DBO_TRX );
+
+               $this->assertFalse( $db->getFlag( DBO_TRX ), 'DBO_TRX is not set' );
+
+               $called = false;
+               $db->onTransactionPreCommitOrIdle(
+                       function () use ( &$called ) {
+                               $called = true;
+                       },
+                       __METHOD__
+               );
+               $this->assertTrue( $called, 'Called when idle' );
+
+               $db->begin( __METHOD__ );
+               $called = false;
+               $db->onTransactionPreCommitOrIdle(
+                       function () use ( &$called ) {
+                               $called = true;
+                       },
+                       __METHOD__
+               );
+               $this->assertFalse( $called, 'Not called when transaction is active' );
+               $db->commit( __METHOD__ );
+               $this->assertTrue( $called, 'Called when transaction is committed' );
+       }
+
+       /**
+        * @covers Wikimedia\Rdbms\Database::onTransactionPreCommitOrIdle
+        * @covers Wikimedia\Rdbms\Database::runOnTransactionPreCommitCallbacks
+        */
+       public function testTransactionPreCommitOrIdle_TRX() {
+               $db = $this->getMockDB( [ 'isOpen' ] );
+               $db->method( 'isOpen' )->willReturn( true );
+               $db->setFlag( DBO_TRX );
+
+               $lbFactory = LBFactorySingle::newFromConnection( $db );
+               // Ask for the connectin so that LB sets internal state
+               // about this connection being the master connection
+               $lb = $lbFactory->getMainLB();
+               $conn = $lb->openConnection( $lb->getWriterIndex() );
+               $this->assertSame( $db, $conn, 'Same DB instance' );
+               $this->assertTrue( $db->getFlag( DBO_TRX ), 'DBO_TRX is set' );
+
+               $called = false;
+               $db->onTransactionPreCommitOrIdle(
+                       function () use ( &$called ) {
+                               $called = true;
+                       }
+               );
+               $this->assertFalse( $called, 'Not called when idle if DBO_TRX is set' );
+
+               $lbFactory->beginMasterChanges( __METHOD__ );
+               $this->assertFalse( $called, 'Not called when lb-transaction is active' );
+
+               $lbFactory->commitMasterChanges( __METHOD__ );
+               $this->assertTrue( $called, 'Called when lb-transaction is committed' );
+       }
+
        /**
         * @covers Wikimedia\Rdbms\Database::onTransactionResolution
         * @covers Wikimedia\Rdbms\Database::runOnTransactionIdleCallbacks
index 85becff..a9a612d 100644 (file)
@@ -27,6 +27,8 @@ class SpecialRecentchangesTest extends AbstractChangesListSpecialPageTestCase {
 
                        [ 'days=3', [ 'days' => '3' ] ],
 
+                       [ 'days=0.25', [ 'days' => '0.25'] ],
+
                        [ 'namespace=5', [ 'namespace' => '5' ] ],
 
                        [ 'namespace=5|3', [ 'namespace' => '5|3' ] ],
index 929fa1f..e944ef0 100644 (file)
@@ -2,7 +2,7 @@
 ( function ( $, mw, QUnit ) {
        'use strict';
 
-       var addons;
+       var addons, nested;
 
        /**
         * Make a safe copy of localEnv:
@@ -77,8 +77,8 @@
        ( function () {
                var orgModule = QUnit.module;
                QUnit.module = function ( name, localEnv, executeNow ) {
-                       var orgBeforeEach, orgAfterEach;
-                       if ( QUnit.config.moduleStack.length ) {
+                       var orgBeforeEach, orgAfterEach, orgExecute;
+                       if ( nested ) {
                                // In a nested module, don't re-run our handlers.
                                return orgModule.apply( this, arguments );
                        }
                                executeNow = localEnv;
                                localEnv = undefined;
                        }
+                       if ( executeNow ) {
+                               // Wrap executeNow() so that we can detect nested modules
+                               orgExecute = executeNow;
+                               executeNow = function () {
+                                       var ret;
+                                       nested = true;
+                                       ret = orgExecute.apply( this, arguments );
+                                       nested = false;
+                                       return ret;
+                               };
+                       }
 
                        localEnv = localEnv || {};
                        orgBeforeEach = localEnv.beforeEach;
                var orgModule = QUnit.module;
                QUnit.module = function ( name, localEnv, executeNow ) {
                        var orgBeforeEach, orgAfterEach;
-                       if ( QUnit.config.moduleStack.length ) {
+                       if ( nested ) {
                                // In a nested module, don't re-run our handlers.
                                return orgModule.apply( this, arguments );
                        }
index 5b973f6..c88941e 100644 (file)
         * @param {function($table)} callback something to do with the table before we compare
         */
        function tableTest( msg, header, data, expected, callback ) {
-               QUnit.test( msg, 1, function ( assert ) {
+               QUnit.test( msg, function ( assert ) {
                        var extracted,
                                $table = tableCreate( header, data );
 
         * @param {function($table)} callback Something to do with the table before we compare
         */
        function tableTestHTML( msg, html, expected, callback ) {
-               QUnit.test( msg, 1, function ( assert ) {
+               QUnit.test( msg, function ( assert ) {
                        var extracted,
                                $table = $( html );