Merge "Special:EditWatchlist/clear: use destructive button"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Wed, 26 Nov 2014 05:13:28 +0000 (05:13 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Wed, 26 Nov 2014 05:13:28 +0000 (05:13 +0000)
212 files changed:
RELEASE-NOTES-1.25
StartProfiler.sample
autoload.php
composer.json
docs/hooks.txt
docs/kss/styleguide-template/public/less.js [deleted file]
docs/uidesign/mediawiki.action.history.diff.html
includes/CdbCompat.php [new file with mode: 0644]
includes/DefaultSettings.php
includes/GlobalFunctions.php
includes/Html.php
includes/Linker.php
includes/OutputHandler.php
includes/OutputPage.php
includes/Sanitizer.php
includes/Title.php
includes/User.php
includes/WebStart.php
includes/api/ApiBase.php
includes/api/ApiFormatJson.php
includes/api/ApiFormatPhp.php
includes/api/ApiMain.php
includes/api/ApiQueryAllDeletedRevisions.php
includes/api/ApiQueryInfo.php
includes/api/ApiUpload.php
includes/api/i18n/ar.json [new file with mode: 0644]
includes/api/i18n/be-tarask.json
includes/api/i18n/de.json
includes/api/i18n/el.json [new file with mode: 0644]
includes/api/i18n/es.json
includes/api/i18n/fr.json
includes/api/i18n/fy.json
includes/api/i18n/ko.json [new file with mode: 0644]
includes/api/i18n/mk.json
includes/api/i18n/pl.json
includes/api/i18n/sv.json
includes/api/i18n/zh-hans.json
includes/api/i18n/zh-hant.json
includes/cache/LocalisationCache.php
includes/cache/bloom/BloomCache.php
includes/db/Database.php
includes/db/DatabaseMysqlBase.php
includes/db/LBFactory.php
includes/db/LBFactoryMulti.php
includes/db/LBFactorySingle.php
includes/db/LoadBalancer.php
includes/debug/MWDebug.php
includes/debug/logger/legacy/Logger.php
includes/debug/logger/monolog/Handler.php
includes/debug/logger/monolog/LegacyFormatter.php [new file with mode: 0644]
includes/exception/MWExceptionHandler.php
includes/filebackend/FileBackendMultiWrite.php
includes/filebackend/SwiftFileBackend.php
includes/htmlform/HTMLIntField.php
includes/htmlform/HTMLSelectAndOtherField.php
includes/installer/WebInstallerOutput.php
includes/installer/WebInstallerPage.php
includes/installer/i18n/ar.json
includes/installer/i18n/ba.json
includes/installer/i18n/de.json
includes/installer/i18n/el.json
includes/installer/i18n/fy.json
includes/installer/i18n/ro.json
includes/installer/i18n/ru.json
includes/installer/i18n/zh-hant.json
includes/interwiki/Interwiki.php
includes/libs/cdb/CdbException.php [deleted file]
includes/libs/cdb/CdbFunctions.php [deleted file]
includes/libs/cdb/CdbReader.php [deleted file]
includes/libs/cdb/CdbReaderDBA.php [deleted file]
includes/libs/cdb/CdbReaderPHP.php [deleted file]
includes/libs/cdb/CdbWriter.php [deleted file]
includes/libs/cdb/CdbWriterDBA.php [deleted file]
includes/libs/cdb/CdbWriterPHP.php [deleted file]
includes/libs/lessc.inc.php [deleted file]
includes/logging/LogEventsList.php
includes/media/Bitmap.php
includes/media/FormatMetadata.php
includes/media/TransformationalImageHandler.php
includes/objectcache/BagOStuff.php
includes/objectcache/MemcachedPhpBagOStuff.php
includes/objectcache/MultiWriteBagOStuff.php
includes/parser/Parser.php
includes/poolcounter/PoolWorkArticleView.php
includes/profiler/Profiler.php
includes/profiler/ProfilerSimpleDB.php [deleted file]
includes/profiler/ProfilerSimpleText.php [deleted file]
includes/profiler/ProfilerSimpleUDP.php [deleted file]
includes/profiler/ProfilerStandard.php
includes/profiler/ProfilerStub.php
includes/profiler/ProfilerXhprof.php
includes/profiler/SectionProfiler.php
includes/profiler/TransactionProfiler.php
includes/profiler/output/ProfilerOutput.php [new file with mode: 0644]
includes/profiler/output/ProfilerOutputDb.php [new file with mode: 0644]
includes/profiler/output/ProfilerOutputText.php [new file with mode: 0644]
includes/profiler/output/ProfilerOutputUdp.php [new file with mode: 0644]
includes/site/SiteSQLStore.php
includes/specials/SpecialContributions.php
includes/specials/SpecialNewpages.php
includes/specials/SpecialRedirect.php
includes/utils/AutoloadGenerator.php
languages/i18n/aeb.json
languages/i18n/ar.json
languages/i18n/azb.json
languages/i18n/ba.json
languages/i18n/be-tarask.json
languages/i18n/bn.json
languages/i18n/ca.json
languages/i18n/ce.json
languages/i18n/crh-cyrl.json
languages/i18n/crh-latn.json
languages/i18n/de.json
languages/i18n/el.json
languages/i18n/eo.json
languages/i18n/es.json
languages/i18n/et.json
languages/i18n/eu.json
languages/i18n/frr.json
languages/i18n/fy.json
languages/i18n/he.json
languages/i18n/hr.json
languages/i18n/ia.json
languages/i18n/id.json
languages/i18n/ka.json
languages/i18n/kk-cyrl.json
languages/i18n/ksh.json
languages/i18n/lb.json
languages/i18n/lrc.json
languages/i18n/ml.json
languages/i18n/mt.json
languages/i18n/nb.json
languages/i18n/pa.json
languages/i18n/pl.json
languages/i18n/pms.json
languages/i18n/ps.json
languages/i18n/pt-br.json
languages/i18n/qqq.json
languages/i18n/ru.json
languages/i18n/sq.json
languages/i18n/sv.json
languages/i18n/th.json
languages/i18n/tt-cyrl.json
languages/i18n/tyv.json
languages/i18n/uk.json
languages/i18n/ur.json
languages/i18n/war.json
languages/i18n/yi.json
languages/i18n/zh-hans.json
languages/i18n/zh-hant.json
maintenance/Doxyfile
maintenance/Maintenance.php
maintenance/backupTextPass.inc
maintenance/cdb.php
maintenance/fetchText.php
maintenance/generateLocalAutoload.php
maintenance/jsduck/categories.json
maintenance/resources/update-oojs-ui.sh
maintenance/resources/update-oojs.sh
profileinfo.php
resources/Resources.php
resources/lib/oojs-ui/i18n/ce.json
resources/lib/oojs-ui/i18n/crh-cyrl.json [new file with mode: 0644]
resources/lib/oojs-ui/i18n/crh-latn.json [new file with mode: 0644]
resources/lib/oojs-ui/i18n/cs.json
resources/lib/oojs-ui/i18n/es.json
resources/lib/oojs-ui/i18n/fi.json
resources/lib/oojs-ui/i18n/fr.json
resources/lib/oojs-ui/i18n/hu.json
resources/lib/oojs-ui/i18n/hy.json
resources/lib/oojs-ui/i18n/id.json
resources/lib/oojs-ui/i18n/nb.json
resources/lib/oojs-ui/i18n/om.json
resources/lib/oojs-ui/i18n/sr-ec.json
resources/lib/oojs-ui/i18n/sv.json
resources/lib/oojs-ui/oojs-ui-apex.css
resources/lib/oojs-ui/oojs-ui-apex.js
resources/lib/oojs-ui/oojs-ui-apex.svg.css
resources/lib/oojs-ui/oojs-ui-mediawiki.css
resources/lib/oojs-ui/oojs-ui-mediawiki.js
resources/lib/oojs-ui/oojs-ui-mediawiki.svg.css
resources/lib/oojs-ui/oojs-ui.js
resources/lib/oojs/oojs.jquery.js
resources/src/jquery/jquery.tablesorter.js
resources/src/mediawiki.action/mediawiki.action.history.diff.css
resources/src/mediawiki.action/mediawiki.action.history.diff.print.css [new file with mode: 0644]
resources/src/mediawiki.action/mediawiki.action.view.dblClickEdit.js
resources/src/mediawiki.legacy/commonPrint.css
resources/src/mediawiki.less/mediawiki.mixins.less
resources/src/mediawiki.less/mediawiki.ui/mixins.less
resources/src/mediawiki.page/mediawiki.page.ready.js
resources/src/mediawiki.special/mediawiki.special.javaScriptTest.js
resources/src/mediawiki.special/mediawiki.special.preferences.js
resources/src/mediawiki.ui/components/checkbox.less
resources/src/mediawiki.ui/components/images/checked_disabled.png [new file with mode: 0644]
resources/src/mediawiki.ui/components/images/checked_disabled.svg [new file with mode: 0644]
resources/src/mediawiki.ui/components/inputs.less
resources/src/mediawiki/mediawiki.Uri.js
resources/src/mediawiki/mediawiki.debug.js
resources/src/mediawiki/mediawiki.debug.profile.css [deleted file]
resources/src/mediawiki/mediawiki.debug.profile.js [deleted file]
resources/src/mediawiki/mediawiki.util.js
tests/parser/parserTests.txt
tests/phpunit/includes/debug/MWDebugTest.php
tests/phpunit/includes/debug/logging/legacy/LoggerTest.php [new file with mode: 0644]
tests/phpunit/includes/libs/cdb/CdbTest.php [deleted file]
tests/phpunit/includes/media/FormatMetadataTest.php
tests/phpunit/includes/resourceloader/ResourceLoaderStartupModuleTest.php
tests/phpunit/includes/utils/UIDGeneratorTest.php
tests/phpunit/maintenance/DumpTestCase.php
tests/phpunit/maintenance/backupTextPassTest.php
tests/qunit/suites/resources/jquery/jquery.tablesorter.test.js

index ea5e8b8..2781b38 100644 (file)
@@ -19,6 +19,10 @@ production.
 * (bug 72951) The UserGetLanguageObject hook may be passed any IContextSource
   for its $context parameter. Formerly it was documented as receiving a
   RequestContext specifically.
+* Profiling was restructured and $wgProfiler now requires an 'output' parameter.
+  See StartProfiler.sample for details.
+* $wgMangleFlashPolicy was added to make MediaWiki's mangling of anything that
+  might be a flash policy directive configurable.
 
 === New features in 1.25 ===
 * (bug 62861) Updated plural rules to CLDR 26. Includes incompatible changes
@@ -47,6 +51,7 @@ production.
   User::matchEditToken will reject any older tokens.
 * The debug logging internals have been overhauled, and are now using the
   PSR-3 interfaces.
+* Update CSSJanus to v1.1.1.
 
 === Bug fixes in 1.25 ===
 * (bug 71003) No additional code will be generated to try to load CSS-embedded
@@ -93,6 +98,9 @@ production.
 * If the user has the 'deletedhistory' right, action=query's revids parameter
   will now recognize deleted revids.
 * prop=revisions may be used as a generator, generating revids.
+* (bug 66776) format=json results will no longer be corrupted when
+  $wgMangleFlashPolicy is in effect. format=php results will cleanly return an
+  error instead of returning invalid serialized data.
 
 === Action API internal changes in 1.25 ===
 * ApiHelp has been rewritten to support i18n and paginated HTML output.
@@ -188,6 +196,10 @@ changes to languages because of Bugzilla reports.
 * Deprecated the getInternalLinkAttributes, getInternalLinkAttributesObj,
   and getInternalLinkAttributes methods in Linker, and removed
   getExternalLinkAttributes method, which was deprecated in MediaWiki 1.18.
+* Removed Sites class, which was deprecated in 1.21 and replaced by SiteSQLStore.
+* The mw.api.getToken() method now uses action=query?meta=tokens. This will now
+  fail for custom tokens registered only via the deprecated ApiTokensGetTokenTypes
+  hook. The ApiQueryTokensRegisterTypes hook should be used for this to work.
 
 == Compatibility ==
 
index d9b5288..d20c0e1 100644 (file)
@@ -3,24 +3,31 @@
 /**
  * To use a profiler, copy this file to StartProfiler.php,
  * and add either:
+ *  $wgProfiler['class'] = 'ProfilerStandard';
+ *    or
+ *  $wgProfiler['class'] = 'ProfilerXhprof';
  *
- *   // Does not support the debugging toolbar
- *   // Stores profiling information in the database
- *   // Requires running maintenance/archives/patch-profiling.sql
- *   $wgProfiler['class'] = 'ProfilerSimpleDB'
+ * For output, add:
+ *  $wgProfiler['output'] = array( 'text' );
+ *    'text' can be one (or more) of 'text' 'udp' or 'db'
+ *    'db' requires creating the profiling table, see patch-profiling.sql
  *
- * or:
+ * The 'text' output will be added to the output page in a comment approriate
+ * to the output's mime type. For a text/html page, this display can be
+ * changed to a preformatted text block by setting the 'visible' configuration
+ * flag:
+ *  $wgProfiler['visible'] = true;
  *
- *   // Supports the debugging toolbar
- *   // Does not store profiling information in the database
- *   $wgProfiler['class'] = 'ProfilerStandard';
+ * The 'db' output expects a database table that can be created by applying
+ * maintenance/archives/patch-profiling.sql to your database.
  *
- * Or for a sampling profiler:
- *   if ( !mt_rand( 0, 100 ) ) {
- *       $wgProfiler['class'] = 'ProfilerSimpleDB';
- *   } else {
- *       $wgProfiler['class'] = 'ProfilerStub';
- *   }
+ * For a rudimentary sampling profiler:
+ *   $wgProfiler['class'] = 'ProfilerStandard';
+ *   $wgProfiler['output'] = array( 'db' );
+ *   $wgProfiler['sampling'] = 50; // one every 50 requests
+ * This will use ProfilerStub for non-sampled cases.
  *
- * Configuration of the profiler output can be done in LocalSettings.php
+ * For performance, the profiler is always disabled for CLI scripts
+ * as they could be long running and the data would accumulate. Use
+ * the --profiler parameter of maintenance scripts to override this.
  */
index c5fc22c..472d17e 100644 (file)
@@ -1,5 +1,5 @@
 <?php
-// This file is generated, do not adjust manually
+// This file is generated by maintenance/generateLocalAutoload.php, do not adjust manually
 
 global $wgAutoloadLocalClasses;
 
@@ -189,14 +189,9 @@ $wgAutoloadLocalClasses = array(
        'CategoryPage' => __DIR__ . '/includes/page/CategoryPage.php',
        'CategoryPager' => __DIR__ . '/includes/specials/SpecialCategories.php',
        'CategoryViewer' => __DIR__ . '/includes/CategoryViewer.php',
-       'CdbException' => __DIR__ . '/includes/libs/cdb/CdbException.php',
-       'CdbFunctions' => __DIR__ . '/includes/libs/cdb/CdbFunctions.php',
-       'CdbReader' => __DIR__ . '/includes/libs/cdb/CdbReader.php',
-       'CdbReaderDBA' => __DIR__ . '/includes/libs/cdb/CdbReaderDBA.php',
-       'CdbReaderPHP' => __DIR__ . '/includes/libs/cdb/CdbReaderPHP.php',
-       'CdbWriter' => __DIR__ . '/includes/libs/cdb/CdbWriter.php',
-       'CdbWriterDBA' => __DIR__ . '/includes/libs/cdb/CdbWriterDBA.php',
-       'CdbWriterPHP' => __DIR__ . '/includes/libs/cdb/CdbWriterPHP.php',
+       'CdbException' => __DIR__ . '/includes/CdbCompat.php',
+       'CdbReader' => __DIR__ . '/includes/CdbCompat.php',
+       'CdbWriter' => __DIR__ . '/includes/CdbCompat.php',
        'CgzCopyTransaction' => __DIR__ . '/maintenance/storage/recompressTracked.php',
        'ChangePassword' => __DIR__ . '/maintenance/changePassword.php',
        'ChangeTags' => __DIR__ . '/includes/ChangeTags.php',
@@ -686,6 +681,7 @@ $wgAutoloadLocalClasses = array(
        'MWLoggerLegacyLogger' => __DIR__ . '/includes/debug/logger/legacy/Logger.php',
        'MWLoggerLegacySpi' => __DIR__ . '/includes/debug/logger/legacy/Spi.php',
        'MWLoggerMonologHandler' => __DIR__ . '/includes/debug/logger/monolog/Handler.php',
+       'MWLoggerMonologLegacyFormatter' => __DIR__ . '/includes/debug/logger/monolog/LegacyFormatter.php',
        'MWLoggerMonologProcessor' => __DIR__ . '/includes/debug/logger/monolog/Processor.php',
        'MWLoggerMonologSpi' => __DIR__ . '/includes/debug/logger/monolog/Spi.php',
        'MWLoggerNullSpi' => __DIR__ . '/includes/debug/logger/NullSpi.php',
@@ -880,10 +876,11 @@ $wgAutoloadLocalClasses = array(
        'ProcessCacheLRU' => __DIR__ . '/includes/libs/ProcessCacheLRU.php',
        'ProfileSection' => __DIR__ . '/includes/profiler/ProfileSection.php',
        'Profiler' => __DIR__ . '/includes/profiler/Profiler.php',
-       'ProfilerSimpleDB' => __DIR__ . '/includes/profiler/ProfilerSimpleDB.php',
-       'ProfilerSimpleText' => __DIR__ . '/includes/profiler/ProfilerSimpleText.php',
+       'ProfilerOutput' => __DIR__ . '/includes/profiler/output/ProfilerOutput.php',
+       'ProfilerOutputDb' => __DIR__ . '/includes/profiler/output/ProfilerOutputDb.php',
+       'ProfilerOutputText' => __DIR__ . '/includes/profiler/output/ProfilerOutputText.php',
+       'ProfilerOutputUdp' => __DIR__ . '/includes/profiler/output/ProfilerOutputUdp.php',
        'ProfilerSimpleTrace' => __DIR__ . '/includes/profiler/ProfilerSimpleTrace.php',
-       'ProfilerSimpleUDP' => __DIR__ . '/includes/profiler/ProfilerSimpleUDP.php',
        'ProfilerStandard' => __DIR__ . '/includes/profiler/ProfilerStandard.php',
        'ProfilerStub' => __DIR__ . '/includes/profiler/ProfilerStub.php',
        'ProfilerXhprof' => __DIR__ . '/includes/profiler/ProfilerXhprof.php',
@@ -1032,7 +1029,6 @@ $wgAutoloadLocalClasses = array(
        'SiteStatsInit' => __DIR__ . '/includes/SiteStats.php',
        'SiteStatsUpdate' => __DIR__ . '/includes/deferred/SiteStatsUpdate.php',
        'SiteStore' => __DIR__ . '/includes/site/SiteStore.php',
-       'Sites' => __DIR__ . '/includes/site/SiteSQLStore.php',
        'Skin' => __DIR__ . '/includes/skins/Skin.php',
        'SkinApi' => __DIR__ . '/includes/skins/SkinApi.php',
        'SkinApiTemplate' => __DIR__ . '/includes/skins/SkinApiTemplate.php',
@@ -1309,10 +1305,5 @@ $wgAutoloadLocalClasses = array(
        'ZhConverter' => __DIR__ . '/languages/classes/LanguageZh.php',
        'ZipDirectoryReader' => __DIR__ . '/includes/utils/ZipDirectoryReader.php',
        'ZipDirectoryReaderError' => __DIR__ . '/includes/utils/ZipDirectoryReader.php',
-       'lessc' => __DIR__ . '/includes/libs/lessc.inc.php',
-       'lessc_formatter_classic' => __DIR__ . '/includes/libs/lessc.inc.php',
-       'lessc_formatter_compressed' => __DIR__ . '/includes/libs/lessc.inc.php',
-       'lessc_formatter_lessjs' => __DIR__ . '/includes/libs/lessc.inc.php',
-       'lessc_parser' => __DIR__ . '/includes/libs/lessc.inc.php',
        'profile_point' => __DIR__ . '/profileinfo.php',
 );
index 61f30ce..e5ade52 100644 (file)
                "wiki": "https://www.mediawiki.org/"
        },
        "require": {
+               "leafo/lessphp": "0.5.0",
                "php": ">=5.3.3",
                "psr/log": "1.0.0",
-               "cssjanus/cssjanus": "1.1.0"
+               "cssjanus/cssjanus": "1.1.1",
+               "cdb/cdb": "1.0.0"
        },
        "require-dev": {
                "phpunit/phpunit": "*"
index 7ec6ff5..1e4be55 100644 (file)
@@ -2882,7 +2882,7 @@ $user: User object
 &$timestamp: timestamp, change this to override local email authentication
   timestamp
 
-'UserGetImplicitGroups': Called in User::getImplicitGroups().
+'UserGetImplicitGroups': DEPRECATED, called in User::getImplicitGroups().
 &$groups: List of implicit (automatically-assigned) groups
 
 'UserGetLanguageObject': Called when getting user's interface language object.
diff --git a/docs/kss/styleguide-template/public/less.js b/docs/kss/styleguide-template/public/less.js
deleted file mode 100644 (file)
index 89b7637..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-//
-// LESS - Leaner CSS v1.2.1
-// http://lesscss.org
-// 
-// Copyright (c) 2009-2011, Alexis Sellier
-// Licensed under the Apache 2.0 License.
-//
-(function(a,b){function c(b){return a.less[b.split("/")[1]]}function m(){var a=document.getElementsByTagName("style");for(var b=0;b<a.length;b++)a[b].type.match(k)&&(new d.Parser).parse(a[b].innerHTML||"",function(c,d){var e=d.toCSS(),f=a[b];f.type="text/css",f.styleSheet?f.styleSheet.cssText=e:f.innerHTML=e})}function n(a,b){for(var c=0;c<d.sheets.length;c++)o(d.sheets[c],a,b,d.sheets.length-(c+1))}function o(b,c,e,f){var g=a.location.href.replace(/[#?].*$/,""),i=b.href.replace(/\?.*$/,""),j=h&&h.getItem(i),k=h&&h.getItem(i+":timestamp"),l={css:j,timestamp:k};/^(https?|file):/.test(i)||(i.charAt(0)=="/"?i=a.location.protocol+"//"+a.location.host+i:i=g.slice(0,g.lastIndexOf("/")+1)+i);var m=i.match(/([^\/]+)$/)[1];s(b.href,b.type,function(a,g){if(!e&&l&&g&&(new Date(g)).valueOf()===(new Date(l.timestamp)).valueOf())r(l.css,b),c(null,b,{local:!0,remaining:f});else try{(new d.Parser({optimization:d.optimization,paths:[i.replace(/[\w\.-]+$/,"")],mime:b.type,filename:m})).parse(a,function(d,e){if(d)return w(d,i);try{c(d,e,a,b,{local:!1,lastModified:g,remaining:f}),u(document.getElementById("less-error-message:"+q(i)))}catch(d){w(d,i)}})}catch(h){w(h,i)}},function(a,b){throw new Error("Couldn't load "+b+" ("+a+")")})}function q(a){return a.replace(/^[a-z]+:\/\/?[^\/]+/,"").replace(/^\//,"").replace(/\?.*$/,"").replace(/\.[^\.\/]+$/,"").replace(/[^\.\w-]+/g,"-").replace(/\./g,":")}function r(a,b,c){var d,e=b.href?b.href.replace(/\?.*$/,""):"",f="less:"+(b.title||q(e));(d=document.getElementById(f))===null&&(d=document.createElement("style"),d.type="text/css",d.media=b.media||"screen",d.id=f,document.getElementsByTagName("head")[0].appendChild(d));if(d.styleSheet)try{d.styleSheet.cssText=a}catch(g){throw new Error("Couldn't reassign styleSheet.cssText.")}else(function(a){d.childNodes.length>0?d.firstChild.nodeValue!==a.nodeValue&&d.replaceChild(a,d.firstChild):d.appendChild(a)})(document.createTextNode(a));c&&h&&(v("saving "+e+" to cache."),h.setItem(e,a),h.setItem(e+":timestamp",c))}function s(a,b,c,e){function i(b,c,d){b.status>=200&&b.status<300?c(b.responseText,b.getResponseHeader("Last-Modified")):typeof d=="function"&&d(b.status,a)}var f=t(),h=g?!1:d.async;typeof f.overrideMimeType=="function"&&f.overrideMimeType("text/css"),f.open("GET",a,h),f.setRequestHeader("Accept",b||"text/x-less, text/css; q=0.9, */*; q=0.5"),f.send(null),g?f.status===0||f.status>=200&&f.status<300?c(f.responseText):e(f.status,a):h?f.onreadystatechange=function(){f.readyState==4&&i(f,c,e)}:i(f,c,e)}function t(){if(a.XMLHttpRequest)return new XMLHttpRequest;try{return new ActiveXObject("MSXML2.XMLHTTP.3.0")}catch(b){return v("browser doesn't support AJAX."),null}}function u(a){return a&&a.parentNode.removeChild(a)}function v(a){d.env=="development"&&typeof console!="undefined"&&console.log("less: "+a)}function w(a,b){var c="less-error-message:"+q(b),e='<li><label>{line}</label><pre class="{class}">{content}</pre></li>',f=document.createElement("div"),g,h,i=[],j=a.filename||b;f.id=c,f.className="less-error-message",h="<h3>"+(a.message||"There is an error in your .less file")+"</h3>"+'<p>in <a href="'+j+'">'+j+"</a> ";var k=function(a,b,c){a.extract[b]&&i.push(e.replace(/\{line\}/,parseInt(a.line)+(b-1)).replace(/\{class\}/,c).replace(/\{content\}/,a.extract[b]))};a.stack?h+="<br/>"+a.stack.split("\n").slice(1).join("<br/>"):a.extract&&(k(a,0,""),k(a,1,"line"),k(a,2,""),h+="on line "+a.line+", column "+(a.column+1)+":</p>"+"<ul>"+i.join("")+"</ul>"),f.innerHTML=h,r([".less-error-message ul, .less-error-message li {","list-style-type: none;","margin-right: 15px;","padding: 4px 0;","margin: 0;","}",".less-error-message label {","font-size: 12px;","margin-right: 15px;","padding: 4px 0;","color: #cc7777;","}",".less-error-message pre {","color: #dd6666;","padding: 4px 0;","margin: 0;","display: inline-block;","}",".less-error-message pre.line {","color: #ff0000;","}",".less-error-message h3 {","font-size: 20px;","font-weight: bold;","padding: 15px 0 5px 0;","margin: 0;","}",".less-error-message a {","color: #10a","}",".less-error-message .error {","color: red;","font-weight: bold;","padding-bottom: 2px;","border-bottom: 1px dashed red;","}"].join("\n"),{title:"error-message"}),f.style.cssText=["font-family: Arial, sans-serif","border: 1px solid #e00","background-color: #eee","border-radius: 5px","-webkit-border-radius: 5px","-moz-border-radius: 5px","color: #e00","padding: 15px","margin-bottom: 15px"].join(";"),d.env=="development"&&(g=setInterval(function(){document.body&&(document.getElementById(c)?document.body.replaceChild(f,document.getElementById(c)):document.body.insertBefore(f,document.body.firstChild),clearInterval(g))},10))}Array.isArray||(Array.isArray=function(a){return Object.prototype.toString.call(a)==="[object Array]"||a instanceof Array}),Array.prototype.forEach||(Array.prototype.forEach=function(a,b){var c=this.length>>>0;for(var d=0;d<c;d++)d in this&&a.call(b,this[d],d,this)}),Array.prototype.map||(Array.prototype.map=function(a){var b=this.length>>>0,c=new Array(b),d=arguments[1];for(var e=0;e<b;e++)e in this&&(c[e]=a.call(d,this[e],e,this));return c}),Array.prototype.filter||(Array.prototype.filter=function(a){var b=[],c=arguments[1];for(var d=0;d<this.length;d++)a.call(c,this[d])&&b.push(this[d]);return b}),Array.prototype.reduce||(Array.prototype.reduce=function(a){var b=this.length>>>0,c=0;if(b===0&&arguments.length===1)throw new TypeError;if(arguments.length>=2)var d=arguments[1];else do{if(c in this){d=this[c++];break}if(++c>=b)throw new TypeError}while(!0);for(;c<b;c++)c in this&&(d=a.call(null,d,this[c],c,this));return d}),Array.prototype.indexOf||(Array.prototype.indexOf=function(a){var b=this.length,c=arguments[1]||0;if(!b)return-1;if(c>=b)return-1;c<0&&(c+=b);for(;c<b;c++){if(!Object.prototype.hasOwnProperty.call(this,c))continue;if(a===this[c])return c}return-1}),Object.keys||(Object.keys=function(a){var b=[];for(var c in a)Object.prototype.hasOwnProperty.call(a,c)&&b.push(c);return b}),String.prototype.trim||(String.prototype.trim=function(){return String(this).replace(/^\s\s*/,"").replace(/\s\s*$/,"")});var d,f;typeof environment=="object"&&{}.toString.call(environment)==="[object Environment]"?(typeof a=="undefined"?d={}:d=a.less={},f=d.tree={},d.mode="rhino"):typeof a=="undefined"?(d=exports,f=c("./tree"),d.mode="node"):(typeof a.less=="undefined"&&(a.less={}),d=a.less,f=a.less.tree={},d.mode="browser"),d.Parser=function(b){function t(){j=m[i],k=h,n=h}function u(){m[i]=j,h=k,n=h}function v(){h>n&&(m[i]=m[i].slice(h-n),n=h)}function w(a){var b,c,d,e,f,j,k,l;if(a instanceof Function)return a.call(o.parsers);if(typeof a=="string")b=g.charAt(h)===a?a:null,d=1,v();else{v();if(!(b=a.exec(m[i])))return null;d=b[0].length}if(b){l=h+=d,j=h+m[i].length-d;while(h<j){e=g.charCodeAt(h);if(e!==32&&e!==10&&e!==9)break;h++}return m[i]=m[i].slice(d+(h-l)),n=h,m[i].length===0&&i<m.length-1&&i++,typeof b=="string"?b:b.length===1?b[0]:b}}function x(a,b){var c=w(a);if(!!c)return c;y(b||(typeof a=="string"?"expected '"+a+"' got '"+g.charAt(h)+"'":"unexpected token"))}function y(a,b){throw{index:h,type:b||"Syntax",message:a}}function z(a){return typeof a=="string"?g.charAt(h)===a:a.test(m[i])?!0:!1}function A(a,b){return a.filename&&b.filename&&a.filename!==b.filename?o.imports.contents[a.filename]:g}function B(a,b){for(var c=a,d=-1;c>=0&&b.charAt(c)!=="\n";c--)d++;return{line:typeof a=="number"?(b.slice(0,a).match(/\n/g)||"").length:null,column:d}}function C(a,b){var c=A(a,b),d=B(a.index,c),e=d.line,f=d.column,g=c.split("\n");this.type=a.type||"Syntax",this.message=a.message,this.filename=a.filename||b.filename,this.index=a.index,this.line=typeof e=="number"?e+1:null,this.callLine=a.call&&B(a.call,c)+1,this.callExtract=g[B(a.call,c)],this.stack=a.stack,this.column=f,this.extract=[g[e-1],g[e],g[e+1]]}var g,h,i,j,k,l,m,n,o,q=this,r=function(){},s=this.imports={paths:b&&b.paths||[],queue:[],files:{},contents:{},mime:b&&b.mime,error:null,push:function(a,c){var e=this;this.queue.push(a),d.Parser.importer(a,this.paths,function(b,d,f){e.queue.splice(e.queue.indexOf(a),1),e.files[a]=d,e.contents[a]=f,b&&!e.error&&(e.error=b),c(b,d),e.queue.length===0&&r()},b)}};return this.env=b=b||{},this.optimization="optimization"in this.env?this.env.optimization:1,this.env.filename=this.env.filename||null,o={imports:s,parse:function(a,e){var j,k,p,q,s,t,u=[],v,x=null;h=i=n=l=0,m=[],g=a.replace(/\r\n/g,"\n"),m=function(a){var c=0,d=/[^"'`\{\}\/\(\)]+/g,e=/\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g,f=0,h,i=a[0],j,k;for(var l=0,m,n;l<g.length;l++){d.lastIndex=l,(h=d.exec(g))&&h.index===l&&(l+=h[0].length,i.push(h[0])),m=g.charAt(l),e.lastIndex=l,!k&&!j&&m==="/"&&(n=g.charAt(l+1),(n==="/"||n==="*")&&(h=e.exec(g))&&h.index===l&&(l+=h[0].length,i.push(h[0]),m=g.charAt(l)));if(m==="{"&&!k&&!j)f++,i.push(m);else if(m==="}"&&!k&&!j)f--,i.push(m),a[++c]=i=[];else if(m==="("&&!k&&!j)i.push(m),j=!0;else if(m===")"&&!k&&j)i.push(m),j=!1;else{if(m==='"'||m==="'"||m==="`")k?k=k===m?!1:k:k=m;i.push(m)}}if(f>0)throw{type:"Syntax",message:"Missing closing `}`",filename:b.filename};return a.map(function(a){return a.join("")})}([[]]);try{j=new f.Ruleset([],w(this.parsers.primary)),j.root=!0}catch(y){return e(new C(y,b))}j.toCSS=function(a){var e,g,h;return function(e,g){var h=[],i;e=e||{},typeof g=="object"&&!Array.isArray(g)&&(g=Object.keys(g).map(function(a){var b=g[a];return b instanceof f.Value||(b instanceof f.Expression||(b=new f.Expression([b])),b=new f.Value([b])),new f.Rule("@"+a,b,!1,0)}),h=[new f.Ruleset(null,g)]);try{var j=a.call(this,{frames:h}).toCSS([],{compress:e.compress||!1})}catch(k){throw new C(k,b)}if(i=o.imports.error)throw i instanceof C?i:new C(i,b);return e.yuicompress&&d.mode==="node"?c("./cssmin").compressor.cssmin(j):e.compress?j.replace(/(\s)+/g,"$1"):j}}(j.eval);if(h<g.length-1){h=l,t=g.split("\n"),s=(g.slice(0,h).match(/\n/g)||"").length+1;for(var z=h,A=-1;z>=0&&g.charAt(z)!=="\n";z--)A++;x={type:"Parse",message:"Syntax Error on line "+s,index:h,filename:b.filename,line:s,column:A,extract:[t[s-2],t[s-1],t[s]]}}this.imports.queue.length>0?r=function(){e(x,j)}:e(x,j)},parsers:{primary:function(){var a,b=[];while((a=w(this.mixin.definition)||w(this.rule)||w(this.ruleset)||w(this.mixin.call)||w(this.comment)||w(this.directive))||w(/^[\s\n]+/))a&&b.push(a);return b},comment:function(){var a;if(g.charAt(h)!=="/")return;if(g.charAt(h+1)==="/")return new f.Comment(w(/^\/\/.*/),!0);if(a=w(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/))return new f.Comment(a)},entities:{quoted:function(){var a,b=h,c;g.charAt(b)==="~"&&(b++,c=!0);if(g.charAt(b)!=='"'&&g.charAt(b)!=="'")return;c&&w("~");if(a=w(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/))return new f.Quoted(a[0],a[1]||a[2],c)},keyword:function(){var a;if(a=w(/^[_A-Za-z-][_A-Za-z0-9-]*/))return f.colors.hasOwnProperty(a)?new f.Color(f.colors[a].slice(1)):new f.Keyword(a)},call:function(){var a,c,d=h;if(!(a=/^([\w-]+|%|progid:[\w\.]+)\(/.exec(m[i])))return;a=a[1].toLowerCase();if(a==="url")return null;h+=a.length;if(a==="alpha")return w(this.alpha);w("("),c=w(this.entities.arguments);if(!w(")"))return;if(a)return new f.Call(a,c,d,b.filename)},arguments:function(){var a=[],b;while(b=w(this.entities.assignment)||w(this.expression)){a.push(b);if(!w(","))break}return a},literal:function(){return w(this.entities.dimension)||w(this.entities.color)||w(this.entities.quoted)},assignment:function(){var a,b;if((a=w(/^\w+(?=\s?=)/i))&&w("=")&&(b=w(this.entity)))return new f.Assignment(a,b)},url:function(){var a;if(g.charAt(h)!=="u"||!w(/^url\(/))return;return a=w(this.entities.quoted)||w(this.entities.variable)||w(this.entities.dataURI)||w(/^[-\w%@$\/.&=:;#+?~]+/)||"",x(")"),new f.URL(a.value||a.data||a instanceof f.Variable?a:new f.Anonymous(a),s.paths)},dataURI:function(){var a;if(w(/^data:/)){a={},a.mime=w(/^[^\/]+\/[^,;)]+/)||"",a.charset=w(/^;\s*charset=[^,;)]+/)||"",a.base64=w(/^;\s*base64/)||"",a.data=w(/^,\s*[^)]+/);if(a.data)return a}},variable:function(){var a,c=h;if(g.charAt(h)==="@"&&(a=w(/^@@?[\w-]+/)))return new f.Variable(a,c,b.filename)},color:function(){var a;if(g.charAt(h)==="#"&&(a=w(/^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})/)))return new f.Color(a[1])},dimension:function(){var a,b=g.charCodeAt(h);if(b>57||b<45||b===47)return;if(a=w(/^(-?\d*\.?\d+)(px|%|em|rem|pc|ex|in|deg|s|ms|pt|cm|mm|rad|grad|turn)?/))return new f.Dimension(a[1],a[2])},javascript:function(){var a,b=h,c;g.charAt(b)==="~"&&(b++,c=!0);if(g.charAt(b)!=="`")return;c&&w("~");if(a=w(/^`([^`]*)`/))return new f.JavaScript(a[1],h,c)}},variable:function(){var a;if(g.charAt(h)==="@"&&(a=w(/^(@[\w-]+)\s*:/)))return a[1]},shorthand:function(){var a,b;if(!z(/^[@\w.%-]+\/[@\w.-]+/))return;if((a=w(this.entity))&&w("/")&&(b=w(this.entity)))return new f.Shorthand(a,b)},mixin:{call:function(){var a=[],c,d,e,i=h,j=g.charAt(h),k=!1;if(j!=="."&&j!=="#")return;while(c=w(/^[#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/))a.push(new f.Element(d,c,h)),d=w(">");w("(")&&(e=w(this.entities.arguments))&&w(")"),w(this.important)&&(k=!0);if(a.length>0&&(w(";")||z("}")))return new f.mixin.Call(a,e,i,b.filename,k)},definition:function(){var a,b=[],c,d,e,i,j;if(g.charAt(h)!=="."&&g.charAt(h)!=="#"||z(/^[^{]*(;|})/))return;t();if(c=w(/^([#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+)\s*\(/)){a=c[1];while(e=w(this.entities.variable)||w(this.entities.literal)||w(this.entities.keyword)){e instanceof f.Variable?w(":")?(i=x(this.expression,"expected expression"),b.push({name:e.name,value:i})):b.push({name:e.name}):b.push({value:e});if(!w(","))break}x(")"),w(/^when/)&&(j=x(this.conditions,"expected condition")),d=w(this.block);if(d)return new f.mixin.Definition(a,b,d,j);u()}}},entity:function(){return w(this.entities.literal)||w(this.entities.variable)||w(this.entities.url)||w(this.entities.call)||w(this.entities.keyword)||w(this.entities.javascript)||w(this.comment)},end:function(){return w(";")||z("}")},alpha:function(){var a;if(!w(/^\(opacity=/i))return;if(a=w(/^\d+/)||w(this.entities.variable))return x(")"),new f.Alpha(a)},element:function(){var a,b,c,d;c=w(this.combinator),a=w(/^(?:\d+\.\d+|\d+)%/)||w(/^(?:[.#]?|:*)(?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/)||w("*")||w(this.attribute)||w(/^\([^)@]+\)/),a||w("(")&&(d=w(this.entities.variable))&&w(")")&&(a=new f.Paren(d));if(a)return new f.Element(c,a,h);if(c.value&&c.value.charAt(0)==="&")return new f.Element(c,null,h)},combinator:function(){var a,b=g.charAt(h);if(b===">"||b==="+"||b==="~"){h++;while(g.charAt(h)===" ")h++;return new f.Combinator(b)}if(b==="&"){a="&",h++,g.charAt(h)===" "&&(a="& ");while(g.charAt(h)===" ")h++;return new f.Combinator(a)}if(b===":"&&g.charAt(h+1)===":"){h+=2;while(g.charAt(h)===" ")h++;return new f.Combinator("::")}return g.charAt(h-1)===" "?new f.Combinator(" "):new f.Combinator(null)},selector:function(){var a,b,c=[],d,e;while(b=w(this.element)){d=g.charAt(h),c.push(b);if(d==="{"||d==="}"||d===";"||d===",")break}if(c.length>0)return new f.Selector(c)},tag:function(){return w(/^[a-zA-Z][a-zA-Z-]*[0-9]?/)||w("*")},attribute:function(){var a="",b,c,d;if(!w("["))return;if(b=w(/^[a-zA-Z-]+/)||w(this.entities.quoted))(d=w(/^[|~*$^]?=/))&&(c=w(this.entities.quoted)||w(/^[\w-]+/))?a=[b,d,c.toCSS?c.toCSS():c].join(""):a=b;if(!w("]"))return;if(a)return"["+a+"]"},block:function(){var a;if(w("{")&&(a=w(this.primary))&&w("}"))return a},ruleset:function(){var a=[],b,c,d;t();while(b=w(this.selector)){a.push(b),w(this.comment);if(!w(","))break;w(this.comment)}if(a.length>0&&(c=w(this.block)))return new f.Ruleset(a,c);l=h,u()},rule:function(){var a,b,c=g.charAt(h),d,e;t();if(c==="."||c==="#"||c==="&")return;if(a=w(this.variable)||w(this.property)){a.charAt(0)!="@"&&(e=/^([^@+\/'"*`(;{}-]*);/.exec(m[i]))?(h+=e[0].length-1,b=new f.Anonymous(e[1])):a==="font"?b=w(this.font):b=w(this.value),d=w(this.important);if(b&&w(this.end))return new f.Rule(a,b,d,k);l=h,u()}},"import":function(){var a,b,c=h;if(w(/^@import\s+/)&&(a=w(this.entities.quoted)||w(this.entities.url))){b=w(this.mediaFeatures);if(w(";"))return new f.Import(a,s,b,c)}},mediaFeature:function(){var a=[];do if(e=w(this.entities.keyword))a.push(e);else if(w("(")){p=w(this.property),e=w(this.entity);if(!w(")"))return null;if(p&&e)a.push(new f.Paren(new f.Rule(p,e,null,h,!0)));else{if(!e)return null;a.push(new f.Paren(e))}}while(e);if(a.length>0)return new f.Expression(a)},mediaFeatures:function(){var a,b=[];while(a=w(this.mediaFeature)){b.push(a);if(!w(","))break}return b.length>0?b:null},media:function(){var a;if(w(/^@media/)){a=w(this.mediaFeatures);if(rules=w(this.block))return new f.Directive("@media",rules,a)}},directive:function(){var a,b,c,d,e,i;if(g.charAt(h)!=="@")return;if(b=w(this["import"])||w(this.media))return b;if(a=w(/^@page|@keyframes/)||w(/^@(?:-webkit-|-moz-|-o-|-ms-)[a-z0-9-]+/)){d=(w(/^[^{]+/)||"").trim();if(c=w(this.block))return new f.Directive(a+" "+d,c)}else if(a=w(/^@[-a-z]+/))if(a==="@font-face"){if(c=w(this.block))return new f.Directive(a,c)}else if((b=w(this.entity))&&w(";"))return new f.Directive(a,b)},font:function(){var a=[],b=[],c,d,e,g;while(g=w(this.shorthand)||w(this.entity))b.push(g);a.push(new f.Expression(b));if(w(","))while(g=w(this.expression)){a.push(g);if(!w(","))break}return new f.Value(a)},value:function(){var a,b=[],c;while(a=w(this.expression)){b.push(a);if(!w(","))break}if(b.length>0)return new f.Value(b)},important:function(){if(g.charAt(h)==="!")return w(/^! *important/)},sub:function(){var a;if(w("(")&&(a=w(this.expression))&&w(")"))return a},multiplication:function(){var a,b,c,d;if(a=w(this.operand)){while(!z(/^\/\*/)&&(c=w("/")||w("*"))&&(b=w(this.operand)))d=new f.Operation(c,[d||a,b]);return d||a}},addition:function(){var a,b,c,d;if(a=w(this.multiplication)){while((c=w(/^[-+]\s+/)||g.charAt(h-1)!=" "&&(w("+")||w("-")))&&(b=w(this.multiplication)))d=new f.Operation(c,[d||a,b]);return d||a}},conditions:function(){var a,b,c=h,d;if(a=w(this.condition)){while(w(",")&&(b=w(this.condition)))d=new f.Condition("or",d||a,b,c);return d||a}},condition:function(){var a,b,c,d,e=h,g=!1;w(/^not/)&&(g=!0),x("(");if(a=w(this.addition)||w(this.entities.keyword)||w(this.entities.quoted))return(d=w(/^(?:>=|=<|[<=>])/))?(b=w(this.addition)||w(this.entities.keyword)||w(this.entities.quoted))?c=new f.Condition(d,a,b,e,g):y("expected expression"):c=new f.Condition("=",a,new f.Keyword("true"),e,g),x(")"),w(/^and/)?new f.Condition("and",c,w(this.condition)):c},operand:function(){var a,b=g.charAt(h+1);g.charAt(h)==="-"&&(b==="@"||b==="(")&&(a=w("-"));var c=w(this.sub)||w(this.entities.dimension)||w(this.entities.color)||w(this.entities.variable)||w(this.entities.call);return a?new f.Operation("*",[new f.Dimension(-1),c]):c},expression:function(){var a,b,c=[],d;while(a=w(this.addition)||w(this.entity))c.push(a);if(c.length>0)return new f.Expression(c)},property:function(){var a;if(a=w(/^(\*?-?[-a-z_0-9]+)\s*:/))return a[1]}}}};if(d.mode==="browser"||d.mode==="rhino")d.Parser.importer=function(a,b,c,d){a.charAt(0)!=="/"&&b.length>0&&(a=b[0]+a),o({href:a,title:a,type:d.mime},c,!0)};(function(a){function b(b){return a.functions.hsla(b.h,b.s,b.l,b.a)}function c(b){if(b instanceof a.Dimension)return parseFloat(b.unit=="%"?b.value/100:b.value);if(typeof b=="number")return b;throw{error:"RuntimeError",message:"color functions take numbers as parameters"}}function d(a){return Math.min(1,Math.max(0,a))}a.functions={rgb:function(a,b,c){return this.rgba(a,b,c,1)},rgba:function(b,d,e,f){var g=[b,d,e].map(function(a){return c(a)}),f=c(f);return new a.Color(g,f)},hsl:function(a,b,c){return this.hsla(a,b,c,1)},hsla:function(a,b,d,e){function h(a){return a=a<0?a+1:a>1?a-1:a,a*6<1?g+(f-g)*a*6:a*2<1?f:a*3<2?g+(f-g)*(2/3-a)*6:g}a=c(a)%360/360,b=c(b),d=c(d),e=c(e);var f=d<=.5?d*(b+1):d+b-d*b,g=d*2-f;return this.rgba(h(a+1/3)*255,h(a)*255,h(a-1/3)*255,e)},hue:function(b){return new a.Dimension(Math.round(b.toHSL().h))},saturation:function(b){return new a.Dimension(Math.round(b.toHSL().s*100),"%")},lightness:function(b){return new a.Dimension(Math.round(b.toHSL().l*100),"%")},alpha:function(b){return new a.Dimension(b.toHSL().a)},saturate:function(a,c){var e=a.toHSL();return e.s+=c.value/100,e.s=d(e.s),b(e)},desaturate:function(a,c){var e=a.toHSL();return e.s-=c.value/100,e.s=d(e.s),b(e)},lighten:function(a,c){var e=a.toHSL();return e.l+=c.value/100,e.l=d(e.l),b(e)},darken:function(a,c){var e=a.toHSL();return e.l-=c.value/100,e.l=d(e.l),b(e)},fadein:function(a,c){var e=a.toHSL();return e.a+=c.value/100,e.a=d(e.a),b(e)},fadeout:function(a,c){var e=a.toHSL();return e.a-=c.value/100,e.a=d(e.a),b(e)},fade:function(a,c){var e=a.toHSL();return e.a=c.value/100,e.a=d(e.a),b(e)},spin:function(a,c){var d=a.toHSL(),e=(d.h+c.value)%360;return d.h=e<0?360+e:e,b(d)},mix:function(b,c,d){var e=d.value/100,f=e*2-1,g=b.toHSL().a-c.toHSL().a,h=((f*g==-1?f:(f+g)/(1+f*g))+1)/2,i=1-h,j=[b.rgb[0]*h+c.rgb[0]*i,b.rgb[1]*h+c.rgb[1]*i,b.rgb[2]*h+c.rgb[2]*i],k=b.alpha*e+c.alpha*(1-e);return new a.Color(j,k)},greyscale:function(b){return this.desaturate(b,new a.Dimension(100))},e:function(b){return new a.Anonymous(b instanceof a.JavaScript?b.evaluated:b)},escape:function(b){return new a.Anonymous(encodeURI(b.value).replace(/=/g,"%3D").replace(/:/g,"%3A").replace(/#/g,"%23").replace(/;/g,"%3B").replace(/\(/g,"%28").replace(/\)/g,"%29"))},"%":function(b){var c=Array.prototype.slice.call(arguments,1),d=b.value;for(var e=0;e<c.length;e++)d=d.replace(/%[sda]/i,function(a){var b=a.match(/s/i)?c[e].value:c[e].toCSS();return a.match(/[A-Z]$/)?encodeURIComponent(b):b});return d=d.replace(/%%/g,"%"),new a.Quoted('"'+d+'"',d)},round:function(a){return this._math("round",a)},ceil:function(a){return this._math("ceil",a)},floor:function(a){return this._math("floor",a)},_math:function(b,d){if(d instanceof a.Dimension)return new a.Dimension(Math[b](c(d)),d.unit);if(typeof d=="number")return Math[b](d);throw{type:"Argument",message:"argument must be a number"}},argb:function(b){return new a.Anonymous(b.toARGB())},percentage:function(b){return new a.Dimension(b.value*100,"%")},color:function(b){if(b instanceof a.Quoted)return new a.Color(b.value.slice(1));throw{type:"Argument",message:"argument must be a string"}},iscolor:function(b){return this._isa(b,a.Color)},isnumber:function(b){return this._isa(b,a.Dimension)},isstring:function(b){return this._isa(b,a.Quoted)},iskeyword:function(b){return this._isa(b,a.Keyword)},isurl:function(b){return this._isa(b,a.URL)},ispixel:function(b){return b instanceof a.Dimension&&b.unit==="px"?a.True:a.False},ispercentage:function(b){return b instanceof a.Dimension&&b.unit==="%"?a.True:a.False},isem:function(b){return b instanceof a.Dimension&&b.unit==="em"?a.True:a.False},_isa:function(b,c){return b instanceof c?a.True:a.False}}})(c("./tree")),function(a){a.colors={aliceblue:"#f0f8ff",antiquewhite:"#faebd7",aqua:"#00ffff",aquamarine:"#7fffd4",azure:"#f0ffff",beige:"#f5f5dc",bisque:"#ffe4c4",black:"#000000",blanchedalmond:"#ffebcd",blue:"#0000ff",blueviolet:"#8a2be2",brown:"#a52a2a",burlywood:"#deb887",cadetblue:"#5f9ea0",chartreuse:"#7fff00",chocolate:"#d2691e",coral:"#ff7f50",cornflowerblue:"#6495ed",cornsilk:"#fff8dc",crimson:"#dc143c",cyan:"#00ffff",darkblue:"#00008b",darkcyan:"#008b8b",darkgoldenrod:"#b8860b",darkgray:"#a9a9a9",darkgrey:"#a9a9a9",darkgreen:"#006400",darkkhaki:"#bdb76b",darkmagenta:"#8b008b",darkolivegreen:"#556b2f",darkorange:"#ff8c00",darkorchid:"#9932cc",darkred:"#8b0000",darksalmon:"#e9967a",darkseagreen:"#8fbc8f",darkslateblue:"#483d8b",darkslategray:"#2f4f4f",darkslategrey:"#2f4f4f",darkturquoise:"#00ced1",darkviolet:"#9400d3",deeppink:"#ff1493",deepskyblue:"#00bfff",dimgray:"#696969",dimgrey:"#696969",dodgerblue:"#1e90ff",firebrick:"#b22222",floralwhite:"#fffaf0",forestgreen:"#228b22",fuchsia:"#ff00ff",gainsboro:"#dcdcdc",ghostwhite:"#f8f8ff",gold:"#ffd700",goldenrod:"#daa520",gray:"#808080",grey:"#808080",green:"#008000",greenyellow:"#adff2f",honeydew:"#f0fff0",hotpink:"#ff69b4",indianred:"#cd5c5c",indigo:"#4b0082",ivory:"#fffff0",khaki:"#f0e68c",lavender:"#e6e6fa",lavenderblush:"#fff0f5",lawngreen:"#7cfc00",lemonchiffon:"#fffacd",lightblue:"#add8e6",lightcoral:"#f08080",lightcyan:"#e0ffff",lightgoldenrodyellow:"#fafad2",lightgray:"#d3d3d3",lightgrey:"#d3d3d3",lightgreen:"#90ee90",lightpink:"#ffb6c1",lightsalmon:"#ffa07a",lightseagreen:"#20b2aa",lightskyblue:"#87cefa",lightslategray:"#778899",lightslategrey:"#778899",lightsteelblue:"#b0c4de",lightyellow:"#ffffe0",lime:"#00ff00",limegreen:"#32cd32",linen:"#faf0e6",magenta:"#ff00ff",maroon:"#800000",mediumaquamarine:"#66cdaa",mediumblue:"#0000cd",mediumorchid:"#ba55d3",mediumpurple:"#9370d8",mediumseagreen:"#3cb371",mediumslateblue:"#7b68ee",mediumspringgreen:"#00fa9a",mediumturquoise:"#48d1cc",mediumvioletred:"#c71585",midnightblue:"#191970",mintcream:"#f5fffa",mistyrose:"#ffe4e1",moccasin:"#ffe4b5",navajowhite:"#ffdead",navy:"#000080",oldlace:"#fdf5e6",olive:"#808000",olivedrab:"#6b8e23",orange:"#ffa500",orangered:"#ff4500",orchid:"#da70d6",palegoldenrod:"#eee8aa",palegreen:"#98fb98",paleturquoise:"#afeeee",palevioletred:"#d87093",papayawhip:"#ffefd5",peachpuff:"#ffdab9",peru:"#cd853f",pink:"#ffc0cb",plum:"#dda0dd",powderblue:"#b0e0e6",purple:"#800080",red:"#ff0000",rosybrown:"#bc8f8f",royalblue:"#4169e1",saddlebrown:"#8b4513",salmon:"#fa8072",sandybrown:"#f4a460",seagreen:"#2e8b57",seashell:"#fff5ee",sienna:"#a0522d",silver:"#c0c0c0",skyblue:"#87ceeb",slateblue:"#6a5acd",slategray:"#708090",slategrey:"#708090",snow:"#fffafa",springgreen:"#00ff7f",steelblue:"#4682b4",tan:"#d2b48c",teal:"#008080",thistle:"#d8bfd8",tomato:"#ff6347",turquoise:"#40e0d0",violet:"#ee82ee",wheat:"#f5deb3",white:"#ffffff",whitesmoke:"#f5f5f5",yellow:"#ffff00",yellowgreen:"#9acd32"}}(c("./tree")),function(a){a.Alpha=function(a){this.value=a},a.Alpha.prototype={toCSS:function(){return"alpha(opacity="+(this.value.toCSS?this.value.toCSS():this.value)+")"},eval:function(a){return this.value.eval&&(this.value=this.value.eval(a)),this}}}(c("../tree")),function(a){a.Anonymous=function(a){this.value=a.value||a},a.Anonymous.prototype={toCSS:function(){return this.value},eval:function(){return this}}}(c("../tree")),function(a){a.Assignment=function(a,b){this.key=a,this.value=b},a.Assignment.prototype={toCSS:function(){return this.key+"="+(this.value.toCSS?this.value.toCSS():this.value)},eval:function(a){return this.value.eval&&(this.value=this.value.eval(a)),this}}}(c("../tree")),function(a){a.Call=function(a,b,c,d){this.name=a,this.args=b,this.index=c,this.filename=d},a.Call.prototype={eval:function(b){var c=this.args.map(function(a){return a.eval(b)});if(!(this.name in a.functions))return new a.Anonymous(this.name+"("+c.map(function(a){return a.toCSS()}).join(", ")+")");try{return a.functions[this.name].apply(a.functions,c)}catch(d){throw{type:d.type||"Runtime",message:"error evaluating function `"+this.name+"`"+(d.message?": "+d.message:""),index:this.index,filename:this.filename}}},toCSS:function(a){return this.eval(a).toCSS()}}}(c("../tree")),function(a){a.Color=function(a,b){Array.isArray(a)?this.rgb=a:a.length==6?this.rgb=a.match(/.{2}/g).map(function(a){return parseInt(a,16)}):this.rgb=a.split("").map(function(a){return parseInt(a+a,16)}),this.alpha=typeof b=="number"?b:1},a.Color.prototype={eval:function(){return this},toCSS:function(){return this.alpha<1?"rgba("+this.rgb.map(function(a){return Math.round(a)}).concat(this.alpha).join(", ")+")":"#"+this.rgb.map(function(a){return a=Math.round(a),a=(a>255?255:a<0?0:a).toString(16),a.length===1?"0"+a:a}).join("")},operate:function(b,c){var d=[];c instanceof a.Color||(c=c.toColor());for(var e=0;e<3;e++)d[e]=a.operate(b,this.rgb[e],c.rgb[e]);return new a.Color(d,this.alpha+c.alpha)},toHSL:function(){var a=this.rgb[0]/255,b=this.rgb[1]/255,c=this.rgb[2]/255,d=this.alpha,e=Math.max(a,b,c),f=Math.min(a,b,c),g,h,i=(e+f)/2,j=e-f;if(e===f)g=h=0;else{h=i>.5?j/(2-e-f):j/(e+f);switch(e){case a:g=(b-c)/j+(b<c?6:0);break;case b:g=(c-a)/j+2;break;case c:g=(a-b)/j+4}g/=6}return{h:g*360,s:h,l:i,a:d}},toARGB:function(){var a=[Math.round(this.alpha*255)].concat(this.rgb);return"#"+a.map(function(a){return a=Math.round(a),a=(a>255?255:a<0?0:a).toString(16),a.length===1?"0"+a:a}).join("")}}}(c("../tree")),function(a){a.Comment=function(a,b){this.value=a,this.silent=!!b},a.Comment.prototype={toCSS:function(a){return a.compress?"":this.value},eval:function(){return this}}}(c("../tree")),function(a){a.Condition=function(a,b,c,d,e){this.op=a.trim(),this.lvalue=b,this.rvalue=c,this.index=d,this.negate=e},a.Condition.prototype.eval=function(a){var b=this.lvalue.eval(a),c=this.rvalue.eval(a),d=this.index,e,e=function(a){switch(a){case"and":return b&&c;case"or":return b||c;default:if(b.compare)e=b.compare(c);else{if(!c.compare)throw{type:"Type",message:"Unable to perform comparison",index:d};e=c.compare(b)}switch(e){case-1:return a==="<"||a==="=<";case 0:return a==="="||a===">="||a==="=<";case 1:return a===">"||a===">="}}}(this.op);return this.negate?!e:e}}(c("../tree")),function(a){a.Dimension=function(a,b){this.value=parseFloat(a),this.unit=b||null},a.Dimension.prototype={eval:function(){return this},toColor:function(){return new a.Color([this.value,this.value,this.value])},toCSS:function(){var a=this.value+this.unit;return a},operate:function(b,c){return new a.Dimension(a.operate(b,this.value,c.value),this.unit||c.unit)},compare:function(b){return b instanceof a.Dimension?b.value>this.value?-1:b.value<this.value?1:0:-1}}}(c("../tree")),function(a){a.Directive=function(b,c,d){this.name=b,this.features=d&&new a.Value(d),Array.isArray(c)?(this.ruleset=new a.Ruleset([],c),this.ruleset.allowImports=!0):this.value=c},a.Directive.prototype={toCSS:function(a,b){var c=this.features?" "+this.features.toCSS(b):"";return this.ruleset?(this.ruleset.root=!0,this.name+c+(b.compress?"{":" {\n  ")+this.ruleset.toCSS(a,b).trim().replace(/\n/g,"\n  ")+(b.compress?"}":"\n}\n")):this.name+" "+this.value.toCSS()+";\n"},eval:function(a){return this.features=this.features&&this.features.eval(a),a.frames.unshift(this),this.ruleset=this.ruleset&&this.ruleset.eval(a),a.frames.shift(),this},variable:function(b){return a.Ruleset.prototype.variable.call(this.ruleset,b)},find:function(){return a.Ruleset.prototype.find.apply(this.ruleset,arguments)},rulesets:function(){return a.Ruleset.prototype.rulesets.apply(this.ruleset)}}}(c("../tree")),function(a){a.Element=function(b,c,d){this.combinator=b instanceof a.Combinator?b:new a.Combinator(b),typeof c=="string"?this.value=c.trim():c?this.value=c:this.value="",this.index=d},a.Element.prototype.eval=function(b){return new a.Element(this.combinator,this.value.eval?this.value.eval(b):this.value,this.index)},a.Element.prototype.toCSS=function(a){return this.combinator.toCSS(a||{})+(this.value.toCSS?this.value.toCSS(a):this.value)},a.Combinator=function(a){a===" "?this.value=" ":a==="& "?this.value="& ":this.value=a?a.trim():""},a.Combinator.prototype.toCSS=function(a){return{"":""," ":" ","&":"","& ":" ",":":" :","::":"::","+":a.compress?"+":" + ","~":a.compress?"~":" ~ ",">":a.compress?">":" > "}[this.value]}}(c("../tree")),function(a){a.Expression=function(a){this.value=a},a.Expression.prototype={eval:function(b){return this.value.length>1?new a.Expression(this.value.map(function(a){return a.eval(b)})):this.value.length===1?this.value[0].eval(b):this},toCSS:function(a){return this.value.map(function(b){return b.toCSS?b.toCSS(a):""}).join(" ")}}}(c("../tree")),function(a){a.Import=function(b,c,d,e){var f=this;this.index=e,this._path=b,this.features=d&&new a.Value(d),b instanceof a.Quoted?this.path=/\.(le?|c)ss(\?.*)?$/.test(b.value)?b.value:b.value+".less":this.path=b.value.value||b.value,this.css=/css(\?.*)?$/.test(this.path),this.css||c.push(this.path,function(b,c){b&&(b.index=e),f.root=c||new a.Ruleset([],[])})},a.Import.prototype={toCSS:function(a){var b=this.features?" "+this.features.toCSS(a):"";return this.css?"@import "+this._path.toCSS()+b+";\n":""},eval:function(b){var c,d=this.features&&this.features.eval(b);if(this.css)return this;c=new a.Ruleset([],this.root.rules.slice(0));for(var e=0;e<c.rules.length;e++)c.rules[e]instanceof a.Import&&Array.prototype.splice.apply(c.rules,[e,1].concat(c.rules[e].eval(b)));return this.features?new a.Directive("@media",c.rules,this.features.value):c.rules}}}(c("../tree")),function(a){a.JavaScript=function(a,b,c){this.escaped=c,this.expression=a,this.index=b},a.JavaScript.prototype={eval:function(b){var c,d=this,e={},f=this.expression.replace(/@\{([\w-]+)\}/g,function(c,e){return a.jsify((new a.Variable("@"+e,d.index)).eval(b))});try{f=new Function("return ("+f+")")}catch(g){throw{message:"JavaScript evaluation error: `"+
-f+"`",index:this.index}}for(var h in b.frames[0].variables())e[h.slice(1)]={value:b.frames[0].variables()[h].value,toJS:function(){return this.value.eval(b).toCSS()}};try{c=f.call(e)}catch(g){throw{message:"JavaScript evaluation error: '"+g.name+": "+g.message+"'",index:this.index}}return typeof c=="string"?new a.Quoted('"'+c+'"',c,this.escaped,this.index):Array.isArray(c)?new a.Anonymous(c.join(", ")):new a.Anonymous(c)}}}(c("../tree")),function(a){a.Keyword=function(a){this.value=a},a.Keyword.prototype={eval:function(){return this},toCSS:function(){return this.value},compare:function(b){return b instanceof a.Keyword?b.value===this.value?0:1:-1}},a.True=new a.Keyword("true"),a.False=new a.Keyword("false")}(c("../tree")),function(a){a.mixin={},a.mixin.Call=function(b,c,d,e,f){this.selector=new a.Selector(b),this.arguments=c,this.index=d,this.filename=e,this.important=f},a.mixin.Call.prototype={eval:function(a){var b,c,d=[],e=!1;for(var f=0;f<a.frames.length;f++)if((b=a.frames[f].find(this.selector)).length>0){c=this.arguments&&this.arguments.map(function(b){return b.eval(a)});for(var g=0;g<b.length;g++)if(b[g].match(c,a))try{Array.prototype.push.apply(d,b[g].eval(a,this.arguments,this.important).rules),e=!0}catch(h){throw{message:h.message,index:h.index,filename:this.filename,stack:h.stack,call:this.index}}if(e)return d;throw{type:"Runtime",message:"No matching definition was found for `"+this.selector.toCSS().trim()+"("+this.arguments.map(function(a){return a.toCSS()}).join(", ")+")`",index:this.index,filename:this.filename}}throw{type:"Name",message:this.selector.toCSS().trim()+" is undefined",index:this.index,filename:this.filename}}},a.mixin.Definition=function(b,c,d,e){this.name=b,this.selectors=[new a.Selector([new a.Element(null,b)])],this.params=c,this.condition=e,this.arity=c.length,this.rules=d,this._lookups={},this.required=c.reduce(function(a,b){return!b.name||b.name&&!b.value?a+1:a},0),this.parent=a.Ruleset.prototype,this.frames=[]},a.mixin.Definition.prototype={toCSS:function(){return""},variable:function(a){return this.parent.variable.call(this,a)},variables:function(){return this.parent.variables.call(this)},find:function(){return this.parent.find.apply(this,arguments)},rulesets:function(){return this.parent.rulesets.apply(this)},evalParams:function(b,c){var d=new a.Ruleset(null,[]);for(var e=0,f;e<this.params.length;e++)if(this.params[e].name){if(!(f=c&&c[e]||this.params[e].value))throw{type:"Runtime",message:"wrong number of arguments for "+this.name+" ("+c.length+" for "+this.arity+")"};d.rules.unshift(new a.Rule(this.params[e].name,f.eval(b)))}return d},eval:function(b,c,d){var e=this.evalParams(b,c),f,g=[],h;for(var i=0;i<Math.max(this.params.length,c&&c.length);i++)g.push(c[i]||this.params[i].value);return e.rules.unshift(new a.Rule("@arguments",(new a.Expression(g)).eval(b))),h=d?this.rules.map(function(b){return new a.Rule(b.name,b.value,"!important",b.index)}):this.rules.slice(0),(new a.Ruleset(null,h)).eval({frames:[this,e].concat(this.frames,b.frames)})},match:function(a,b){var c=a&&a.length||0,d,e;if(c<this.required)return!1;if(this.required>0&&c>this.params.length)return!1;if(this.condition&&!this.condition.eval({frames:[this.evalParams(b,a)].concat(b.frames)}))return!1;d=Math.min(c,this.arity);for(var f=0;f<d;f++)if(!this.params[f].name&&a[f].eval(b).toCSS()!=this.params[f].value.eval(b).toCSS())return!1;return!0}}}(c("../tree")),function(a){a.Operation=function(a,b){this.op=a.trim(),this.operands=b},a.Operation.prototype.eval=function(b){var c=this.operands[0].eval(b),d=this.operands[1].eval(b),e;if(c instanceof a.Dimension&&d instanceof a.Color){if(this.op!=="*"&&this.op!=="+")throw{name:"OperationError",message:"Can't substract or divide a color from a number"};e=d,d=c,c=e}return c.operate(this.op,d)},a.operate=function(a,b,c){switch(a){case"+":return b+c;case"-":return b-c;case"*":return b*c;case"/":return b/c}}}(c("../tree")),function(a){a.Paren=function(a){this.value=a},a.Paren.prototype={toCSS:function(a){return"("+this.value.toCSS(a)+")"},eval:function(b){return new a.Paren(this.value.eval(b))}}}(c("../tree")),function(a){a.Quoted=function(a,b,c,d){this.escaped=c,this.value=b||"",this.quote=a.charAt(0),this.index=d},a.Quoted.prototype={toCSS:function(){return this.escaped?this.value:this.quote+this.value+this.quote},eval:function(b){var c=this,d=this.value.replace(/`([^`]+)`/g,function(d,e){return(new a.JavaScript(e,c.index,!0)).eval(b).value}).replace(/@\{([\w-]+)\}/g,function(d,e){var f=(new a.Variable("@"+e,c.index)).eval(b);return"value"in f?f.value:f.toCSS()});return new a.Quoted(this.quote+d+this.quote,d,this.escaped,this.index)}}}(c("../tree")),function(a){a.Rule=function(b,c,d,e,f){this.name=b,this.value=c instanceof a.Value?c:new a.Value([c]),this.important=d?" "+d.trim():"",this.index=e,this.inline=f||!1,b.charAt(0)==="@"?this.variable=!0:this.variable=!1},a.Rule.prototype.toCSS=function(a){return this.variable?"":this.name+(a.compress?":":": ")+this.value.toCSS(a)+this.important+(this.inline?"":";")},a.Rule.prototype.eval=function(b){return new a.Rule(this.name,this.value.eval(b),this.important,this.index,this.inline)},a.Shorthand=function(a,b){this.a=a,this.b=b},a.Shorthand.prototype={toCSS:function(a){return this.a.toCSS(a)+"/"+this.b.toCSS(a)},eval:function(){return this}}}(c("../tree")),function(a){a.Ruleset=function(a,b){this.selectors=a,this.rules=b,this._lookups={}},a.Ruleset.prototype={eval:function(b){var c=this.selectors&&this.selectors.map(function(a){return a.eval(b)}),d=new a.Ruleset(c,this.rules.slice(0));d.root=this.root,d.allowImports=this.allowImports,b.frames.unshift(d);if(d.root||d.allowImports)for(var e=0;e<d.rules.length;e++)d.rules[e]instanceof a.Import&&Array.prototype.splice.apply(d.rules,[e,1].concat(d.rules[e].eval(b)));for(var e=0;e<d.rules.length;e++)d.rules[e]instanceof a.mixin.Definition&&(d.rules[e].frames=b.frames.slice(0));for(var e=0;e<d.rules.length;e++)d.rules[e]instanceof a.mixin.Call&&Array.prototype.splice.apply(d.rules,[e,1].concat(d.rules[e].eval(b)));for(var e=0,f;e<d.rules.length;e++)f=d.rules[e],f instanceof a.mixin.Definition||(d.rules[e]=f.eval?f.eval(b):f);return b.frames.shift(),d},match:function(a){return!a||a.length===0},variables:function(){return this._variables?this._variables:this._variables=this.rules.reduce(function(b,c){return c instanceof a.Rule&&c.variable===!0&&(b[c.name]=c),b},{})},variable:function(a){return this.variables()[a]},rulesets:function(){return this._rulesets?this._rulesets:this._rulesets=this.rules.filter(function(b){return b instanceof a.Ruleset||b instanceof a.mixin.Definition})},find:function(b,c){c=c||this;var d=[],e,f,g=b.toCSS();return g in this._lookups?this._lookups[g]:(this.rulesets().forEach(function(e){if(e!==c)for(var g=0;g<e.selectors.length;g++)if(f=b.match(e.selectors[g])){b.elements.length>e.selectors[g].elements.length?Array.prototype.push.apply(d,e.find(new a.Selector(b.elements.slice(1)),c)):d.push(e);break}}),this._lookups[g]=d)},toCSS:function(b,c){var d=[],e=[],f=[],g=[],h,i;this.root||(b.length===0?g=this.selectors.map(function(a){return[a]}):this.joinSelectors(g,b,this.selectors));for(var j=0;j<this.rules.length;j++)i=this.rules[j],i.rules||i instanceof a.Directive?f.push(i.toCSS(g,c)):i instanceof a.Comment?i.silent||(this.root?f.push(i.toCSS(c)):e.push(i.toCSS(c))):i.toCSS&&!i.variable?e.push(i.toCSS(c)):i.value&&!i.variable&&e.push(i.value.toString());return f=f.join(""),this.root?d.push(e.join(c.compress?"":"\n")):e.length>0&&(h=g.map(function(a){return a.map(function(a){return a.toCSS(c)}).join("").trim()}).join(c.compress?",":g.length>3?",\n":", "),d.push(h,(c.compress?"{":" {\n  ")+e.join(c.compress?"":"\n  ")+(c.compress?"}":"\n}\n"))),d.push(f),d.join("")+(c.compress?"\n":"")},joinSelectors:function(a,b,c){for(var d=0;d<c.length;d++)this.joinSelector(a,b,c[d])},joinSelector:function(b,c,d){var e=[],f=[],g=[],h=[],i=!1,j;for(var k=0;k<d.elements.length;k++)j=d.elements[k],j.combinator.value.charAt(0)==="&"&&(i=!0),i?h.push(j):g.push(j);i||(h=g,g=[]),g.length>0&&e.push(new a.Selector(g)),h.length>0&&f.push(new a.Selector(h));for(var l=0;l<c.length;l++)b.push(e.concat(c[l]).concat(f))}}}(c("../tree")),function(a){a.Selector=function(a){this.elements=a,this.elements[0].combinator.value===""&&(this.elements[0].combinator.value=" ")},a.Selector.prototype.match=function(a){var b=this.elements.length,c=a.elements.length,d=Math.min(b,c);if(b<c)return!1;for(var e=0;e<d;e++)if(this.elements[e].value!==a.elements[e].value)return!1;return!0},a.Selector.prototype.eval=function(b){return new a.Selector(this.elements.map(function(a){return a.eval(b)}))},a.Selector.prototype.toCSS=function(a){return this._css?this._css:this._css=this.elements.map(function(b){return typeof b=="string"?" "+b.trim():b.toCSS(a)}).join("")}}(c("../tree")),function(b){b.URL=function(b,c){b.data?this.attrs=b:(typeof a!="undefined"&&!/^(?:https?:\/\/|file:\/\/|data:|\/)/.test(b.value)&&c.length>0&&(b.value=c[0]+(b.value.charAt(0)==="/"?b.value.slice(1):b.value)),this.value=b,this.paths=c)},b.URL.prototype={toCSS:function(){return"url("+(this.attrs?"data:"+this.attrs.mime+this.attrs.charset+this.attrs.base64+this.attrs.data:this.value.toCSS())+")"},eval:function(a){return this.attrs?this:new b.URL(this.value.eval(a),this.paths)}}}(c("../tree")),function(a){a.Value=function(a){this.value=a,this.is="value"},a.Value.prototype={eval:function(b){return this.value.length===1?this.value[0].eval(b):new a.Value(this.value.map(function(a){return a.eval(b)}))},toCSS:function(a){return this.value.map(function(b){return b.toCSS(a)}).join(a.compress?",":", ")}}}(c("../tree")),function(a){a.Variable=function(a,b,c){this.name=a,this.index=b,this.file=c},a.Variable.prototype={eval:function(b){var c,d,e=this.name;e.indexOf("@@")==0&&(e="@"+(new a.Variable(e.slice(1))).eval(b).value);if(c=a.find(b.frames,function(a){if(d=a.variable(e))return d.value.eval(b)}))return c;throw{type:"Name",message:"variable "+e+" is undefined",filename:this.file,index:this.index}}}}(c("../tree")),function(a){a.find=function(a,b){for(var c=0,d;c<a.length;c++)if(d=b.call(a,a[c]))return d;return null},a.jsify=function(a){return Array.isArray(a.value)&&a.value.length>1?"["+a.value.map(function(a){return a.toCSS(!1)}).join(", ")+"]":a.toCSS(!1)}}(c("./tree"));var g=location.protocol==="file:"||location.protocol==="chrome:"||location.protocol==="chrome-extension:"||location.protocol==="resource:";d.env=d.env||(location.hostname=="127.0.0.1"||location.hostname=="0.0.0.0"||location.hostname=="localhost"||location.port.length>0||g?"development":"production"),d.async=!1,d.poll=d.poll||(g?1e3:1500),d.watch=function(){return this.watchMode=!0},d.unwatch=function(){return this.watchMode=!1},d.env==="development"?(d.optimization=0,/!watch/.test(location.hash)&&d.watch(),d.watchTimer=setInterval(function(){d.watchMode&&n(function(a,b,c,d,e){b&&r(b.toCSS(),d,e.lastModified)})},d.poll)):d.optimization=3;var h;try{h=typeof a.localStorage=="undefined"?null:a.localStorage}catch(i){h=null}var j=document.getElementsByTagName("link"),k=/^text\/(x-)?less$/;d.sheets=[];for(var l=0;l<j.length;l++)(j[l].rel==="stylesheet/less"||j[l].rel.match(/stylesheet/)&&j[l].type.match(k))&&d.sheets.push(j[l]);d.refresh=function(a){var b,c;b=c=new Date,n(function(a,d,e,f,g){g.local?v("loading "+f.href+" from cache."):(v("parsed "+f.href+" successfully."),r(d.toCSS(),f,g.lastModified)),v("css for "+f.href+" generated in "+(new Date-c)+"ms"),g.remaining===0&&v("css generated in "+(new Date-b)+"ms"),c=new Date},a),m()},d.refreshStyles=m,d.refresh(d.env==="development")})(window);
\ No newline at end of file
index 5edcfb8..615558f 100644 (file)
@@ -1,57 +1,91 @@
 <!DOCTYPE html>
 <html lang="en" dir="ltr">
 <head>
-       <link rel="stylesheet" href="../../resources/mediawiki.action/mediawiki.action.history.diff.css">
+       <meta charset="utf-8">
+       <link rel="stylesheet" href="../../resources/src/mediawiki.action/mediawiki.action.history.diff.css">
+       <link rel="stylesheet" media="print" href="../../resources/src/mediawiki.action/mediawiki.action.history.diff.print.css">
 </head>
-<body style="background-color: #C0C0C0;">
-<p>
-This show various styles for our diff action, the background being hardcoded to gray (<code>#C0C0C0</code>) The reference style sheet is:</p>
-<p>
-<code><a href="../../resources/mediawiki.action/mediawiki.action.history.diff.css">resources/mediawiki.action/mediawiki.action.history.diff.css</a></code>.
-</p>
-<p>
-This file might help us fix our diff colors which have been a recurring issues among the community for a loooong time.</p>
-
-<p>
-First, show the diff mostly like it would be chown on a wiki</p>
-<table class="diff">
+<body>
+
+<p>This show various styles for our diff action. Style sheet: <code><a href="../../resources/src/mediawiki.action/mediawiki.action.history.diff.css">resources/src/mediawiki.action/mediawiki.action.history.diff.css</a></code>.</p>
+<p>This file might help us fix our diff colors which have been a recurring issues among the community for a loooong time.</p>
+<p>Try it out in print mode, too. Style sheet: <code><a href="../../resources/src/mediawiki.action/mediawiki.action.history.diff.print.css">resources/src/mediawiki.action/mediawiki.action.history.diff.print.css</a></code>.</p>
+
+<p>Practical example copied from MediaWiki's HTML output:</p>
+
+<table class="diff diff-contentalign-left">
+       <colgroup><col class="diff-marker">
+       <col class="diff-content">
+       <col class="diff-marker">
+       <col class="diff-content">
+       </colgroup>
+<tbody>
 <tr>
-       <td class="diff-marker">-</td>
-       <td class="diff-deletedline"><div>
-               Some content <span class="diffchange diffchange-inline">deleted / replaced</span>
-       </div></td>
+       <td class="diff-marker">−</td>
+       <td class="diff-deletedline"><div>Lorem ipsum dolor sit amet<del class="diffchange diffchange-inline">, consectetur adipisicing elit</del>, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</div></td>
        <td class="diff-marker">+</td>
-       <td class="diff-addedline"><div>
-               Some content <span class="diffchange diffchange-inline">added / replacement</span>
-       </div></td>
+       <td class="diff-addedline"><div>Lorem ipsum dolor sit amet, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</div></td>
 </tr>
-</table>
-
+<tr>
+       <td class="diff-marker">−</td>
+       <td class="diff-deletedline"></td>
+       <td colspan="2" class="diff-empty">&nbsp;</td>
+</tr>
+<tr>
+       <td class="diff-marker">−</td>
+       <td class="diff-deletedline"><div>Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</div></td>
+       <td colspan="2" class="diff-empty">&nbsp;</td>
+</tr>
+<tr>
+       <td class="diff-marker">&nbsp;</td>
+       <td class="diff-context"></td>
+       <td class="diff-marker">&nbsp;</td>
+       <td class="diff-context"></td>
+</tr>
+<tr>
+       <td class="diff-marker">&nbsp;</td>
+       <td class="diff-context"><div>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</div></td>
+       <td class="diff-marker">&nbsp;</td>
+       <td class="diff-context"><div>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</div></td>
+</tr>
+<tr>
+       <td class="diff-marker">&nbsp;</td>
+       <td class="diff-context"></td>
+       <td class="diff-marker">&nbsp;</td>
+       <td class="diff-context"></td>
+</tr>
+<tr>
+       <td class="diff-marker">−</td>
+       <td class="diff-deletedline"><div>Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim<del class="diffchange diffchange-inline"> id est laborum</del>.</div></td>
+       <td class="diff-marker">+</td>
+       <td class="diff-addedline"><div>Excepteur sint occaecat cupidatat non proident, sunt<ins class="diffchange diffchange-inline"> reprehenderit in voluptate</ins> in culpa qui officia deserunt mollit anim.</div></td>
+</tr>
+<tr>
+       <td colspan="2" class="diff-empty">&nbsp;</td>
+       <td class="diff-marker">+</td>
+       <td class="diff-addedline"></td>
+</tr>
+<tr>
+       <td colspan="2" class="diff-empty">&nbsp;</td>
+       <td class="diff-marker">+</td>
+       <td class="diff-addedline"><div>Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</div></td>
+</tr>
+</tbody></table>
 
-<p>
-Below are some basic lines being applied one or two classes. Mainly for debugging purposes</p>
+<p>Below are some basic lines being applied one or two classes. Mainly for debugging purposes.</p>
 
 <table class="diff">
-
        <tr><th>Diff</th></tr>
 
        <tr><td class="diff-addedline"><code>diff-addedline</code>: added line</td></tr>
        <tr><td class="diff-deletedline"><code>diff-deletedline</code>: deleted line</td></tr>
        <tr><td class="diff-context"><code>diff-context</code>: context</td></tr>
 
-
-       <tr><th>Same as above with a <code>&lt;span&gt;</code> child element having the <code>diffchange</code> class</th></tr>
+       <tr><th>Same as above with a <code>&lt;ins&gt;</code> or <code>&lt;del&gt;</code> child element having the <code>diffchange</code> class:</th></tr>
 
        <tr><td class="diffchange">Diffchange</td></tr>
-       <tr><td class="diff-addedline">
-               <span class="diffchange">Added line + diffchange</span>
-       </td></tr>
-       <tr><td class="diff-deletedline">
-               <span class="diffchange">Deleted line + diffchange</span>
-       </td></tr>
-       <tr><td class="diff-context">
-               <span class="diffchange">Context + diffchange</span>
-       </td></tr>
+       <tr><td class="diff-addedline"><ins class="diffchange">Added line + diffchange</ins></td></tr>
+       <tr><td class="diff-deletedline"><del class="diffchange">Deleted line + diffchange</del></td></tr>
 </table>
 
 </body>
diff --git a/includes/CdbCompat.php b/includes/CdbCompat.php
new file mode 100644 (file)
index 0000000..0c00b39
--- /dev/null
@@ -0,0 +1,42 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+/***
+ * This file contains a set of backwards-compatability class names
+ * after the cdb functions were moved out into a separate library
+ * and put under a proper namespace
+ *
+ * @since 1.25
+ */
+
+/**
+ * @deprecated since 1.25
+ */
+abstract class CdbReader extends \Cdb\Reader {}
+
+/**
+ * @deprecated since 1.25
+ */
+abstract class CdbWriter extends \Cdb\Writer {}
+
+/**
+ * @deprecated since 1.25
+ */
+class CdbException extends \Cdb\Exception {}
index e822655..f83c402 100644 (file)
@@ -1383,7 +1383,7 @@ $wgDjvuOutputExtension = 'jpg';
 /**
  * Site admin email address.
  *
- * Defaults to "wikiadmin@{$wgServerName}".
+ * Defaults to "wikiadmin@$wgServerName".
  */
 $wgEmergencyContact = false;
 
@@ -1392,7 +1392,7 @@ $wgEmergencyContact = false;
  *
  * The address we should use as sender when a user is requesting his password.
  *
- * Defaults to "apache@{$wgServerName}".
+ * Defaults to "apache@$wgServerName".
  */
 $wgPasswordSender = false;
 
@@ -3194,6 +3194,16 @@ $wgShowRollbackEditCount = 10;
  */
 $wgEnableCanonicalServerLink = false;
 
+/**
+ * When OutputHandler is used, mangle any output that contains
+ * <cross-domain-policy>. Without this, an attacker can send their own
+ * cross-domain policy unless it is prevented by the crossdomain.xml file at
+ * the domain root.
+ *
+ * @since 1.25
+ */
+$wgMangleFlashPolicy = true;
+
 /** @} */ # End of output format settings }
 
 /*************************************************************************//**
index 81f767d..1a901f3 100644 (file)
@@ -950,6 +950,8 @@ function wfMatchesDomainList( $url, $domains ) {
  * $wgDebugRawPage - if false, 'action=raw' hits will not result in debug output.
  * $wgDebugComments - if on, some debug items may appear in comments in the HTML output.
  *
+ * @since 1.25 support for additional context data
+ *
  * @param string $text
  * @param string|bool $dest Destination of the message:
  *     - 'all': both to the log and HTML (debug toolbar or HTML comments)
@@ -957,9 +959,11 @@ function wfMatchesDomainList( $url, $domains ) {
  *   For backward compatibility, it can also take a boolean:
  *     - true: same as 'all'
  *     - false: same as 'log'
+ * @param array $context Additional logging context data
  */
-function wfDebug( $text, $dest = 'all' ) {
+function wfDebug( $text, $dest = 'all', array $context = array() ) {
        global $wgDebugRawPage, $wgDebugLogPrefix;
+       global $wgDebugTimestamps, $wgRequestTime;
 
        if ( !$wgDebugRawPage && wfIsDebugRawPage() ) {
                return;
@@ -972,23 +976,36 @@ function wfDebug( $text, $dest = 'all' ) {
                $dest = 'log';
        }
 
-       $timer = wfDebugTimer();
-       if ( $timer !== '' ) {
-               // Prepend elapsed request time and real memory usage to each line
-               $text = preg_replace( '/[^\n]/', $timer . '\0', $text, 1 );
+       $text = trim( $text );
+
+       // Inline logic from deprecated wfDebugTimer()
+       if ( $wgDebugTimestamps ) {
+               $context['seconds_elapsed'] = sprintf(
+                       '%6.4f',
+                       microtime( true ) - $wgRequestTime
+               );
+               $context['memory_used'] = sprintf(
+                       '%5.1fM',
+                       ( memory_get_usage( true ) / ( 1024 * 1024 ) )
+               );
        }
 
        if ( $dest === 'all' ) {
-               MWDebug::debugMsg( $text );
+               $prefix = '';
+               if ( $wgDebugTimestamps ) {
+                       // Prepend elapsed request time and real memory usage with two
+                       // trailing spaces.
+                       $prefix = "{$context['seconds_elapsed']} {$context['memory_used']}  ";
+               }
+               MWDebug::debugMsg( "{$prefix}{$text}" );
        }
 
-       $ctx = array();
        if ( $wgDebugLogPrefix !== '' ) {
-               $ctx['prefix'] = $wgDebugLogPrefix;
+               $context['prefix'] = $wgDebugLogPrefix;
        }
 
        $logger = MWLogger::getInstance( 'wfDebug' );
-       $logger->debug( rtrim( $text, "\n" ), $ctx );
+       $logger->debug( $text, $context );
 }
 
 /**
@@ -1017,11 +1034,14 @@ function wfIsDebugRawPage() {
 /**
  * Get microsecond timestamps for debug logs
  *
+ * @deprecated since 1.25
  * @return string
  */
 function wfDebugTimer() {
        global $wgDebugTimestamps, $wgRequestTime;
 
+       wfDeprecated( __METHOD__, '1.25' );
+
        if ( !$wgDebugTimestamps ) {
                return '';
        }
@@ -1054,6 +1074,7 @@ function wfDebugMem( $exact = false ) {
  * a sampling factor.
  *
  * @since 1.23 support for sampling log messages via $wgDebugLogGroups.
+ * @since 1.25 support for additional context data
  *
  * @param string $logGroup
  * @param string $text
@@ -1065,8 +1086,11 @@ function wfDebugMem( $exact = false ) {
  *   For backward compatibility, it can also take a boolean:
  *     - true: same as 'all'
  *     - false: same as 'private'
+ * @param array $context Additional logging context data
  */
-function wfDebugLog( $logGroup, $text, $dest = 'all' ) {
+function wfDebugLog(
+       $logGroup, $text, $dest = 'all', array $context = array()
+) {
        // Turn $dest into a string if it's a boolean (for b/c)
        if ( $dest === true ) {
                $dest = 'all';
@@ -1081,19 +1105,21 @@ function wfDebugLog( $logGroup, $text, $dest = 'all' ) {
        }
 
        $logger = MWLogger::getInstance( $logGroup );
-       $logger->debug( $text, array(
-               'private' => ( $dest === 'private' ),
-       ) );
+       $context['private'] = ( $dest === 'private' );
+       $logger->debug( $text, $context );
 }
 
 /**
  * Log for database errors
  *
+ * @since 1.25 support for additional context data
+ *
  * @param string $text Database error message.
+ * @param array $context Additional logging context data
  */
-function wfLogDBError( $text ) {
+function wfLogDBError( $text, array $context = array() ) {
        $logger = MWLogger::getInstance( 'wfLogDBError' );
-       $logger->error( trim( $text ) );
+       $logger->error( trim( $text ), $context );
 }
 
 /**
@@ -1145,16 +1171,17 @@ function wfLogWarning( $msg, $callerOffset = 1, $level = E_USER_WARNING ) {
  *
  * Can also log to TCP or UDP with the syntax udp://host:port/prefix. This will
  * send lines to the specified port, prefixed by the specified prefix and a space.
+ * @since 1.25 support for additional context data
  *
  * @param string $text
  * @param string $file Filename
+ * @param array $context Additional logging context data
  * @throws MWException
  */
-function wfErrorLog( $text, $file ) {
+function wfErrorLog( $text, $file, array $context = array() ) {
        $logger = MWLogger::getInstance( 'wfErrorLog' );
-       $logger->info( trim( $text ), array(
-               'destination' => $file,
-       ) );
+       $context['destination'] = $file;
+       $logger->info( trim( $text ), $context );
 }
 
 /**
@@ -1226,10 +1253,9 @@ function wfLogProfilingData() {
        }
 
        $ctx['output'] = $profiler->getOutput();
-       $ctx['profile'] = $profiler->getRawData();
 
        $log = MWLogger::getInstance( 'profileoutput' );
-       $log->info( 'Elapsed: {elapsed}', $ctx );
+       $log->info( "Elapsed: {elapsed}; URL: <{url}>\n{output}", $ctx );
 }
 
 /**
@@ -1620,15 +1646,15 @@ function wfMsgExt( $key, $options ) {
        array_shift( $args );
        array_shift( $args );
        $options = (array)$options;
+       $validOptions = array( 'parse', 'parseinline', 'escape', 'escapenoentities', 'replaceafter',
+               'parsemag', 'content' );
 
        foreach ( $options as $arrayKey => $option ) {
                if ( !preg_match( '/^[0-9]+|language$/', $arrayKey ) ) {
-                       # An unknown index, neither numeric nor "language"
+                       // An unknown index, neither numeric nor "language"
                        wfWarn( "wfMsgExt called with incorrect parameter key $arrayKey", 1, E_USER_WARNING );
-               } elseif ( preg_match( '/^[0-9]+$/', $arrayKey ) && !in_array( $option,
-               array( 'parse', 'parseinline', 'escape', 'escapenoentities',
-               'replaceafter', 'parsemag', 'content' ) ) ) {
-                       # A numeric index with unknown value
+               } elseif ( preg_match( '/^[0-9]+$/', $arrayKey ) && !in_array( $option, $validOptions ) ) {
+                       // A numeric index with unknown value
                        wfWarn( "wfMsgExt called with incorrect parameter $option", 1, E_USER_WARNING );
                }
        }
@@ -4078,6 +4104,7 @@ function wfGetIP() {
  * @return bool
  */
 function wfIsTrustedProxy( $ip ) {
+       wfDeprecated( __METHOD__, '1.24' );
        return IP::isTrustedProxy( $ip );
 }
 
@@ -4090,5 +4117,6 @@ function wfIsTrustedProxy( $ip ) {
  * @since 1.23 Supports CIDR ranges in $wgSquidServersNoPurge
  */
 function wfIsConfiguredProxy( $ip ) {
+       wfDeprecated( __METHOD__, '1.24' );
        return IP::isConfiguredProxy( $ip );
 }
index fa868e3..b3437d3 100644 (file)
@@ -267,8 +267,7 @@ class Html {
                // In text/html, initial <html> and <head> tags can be omitted under
                // pretty much any sane circumstances, if they have no attributes.  See:
                // <http://www.whatwg.org/html/syntax.html#optional-tags>
-               if ( !$wgWellFormedXml && !$attribs
-               && in_array( $element, array( 'html', 'head' ) ) ) {
+               if ( !$wgWellFormedXml && !$attribs && in_array( $element, array( 'html', 'head' ) ) ) {
                        return '';
                }
 
@@ -301,8 +300,7 @@ class Html {
                                'tel',
                                'color',
                        );
-                       if ( isset( $attribs['type'] )
-                       && !in_array( $attribs['type'], $validTypes ) ) {
+                       if ( isset( $attribs['type'] ) && !in_array( $attribs['type'], $validTypes ) ) {
                                unset( $attribs['type'] );
                        }
                }
@@ -396,8 +394,9 @@ class Html {
                        }
 
                        // Simple checks using $attribDefaults
-                       if ( isset( $attribDefaults[$element][$lcattrib] ) &&
-                       $attribDefaults[$element][$lcattrib] == $value ) {
+                       if ( isset( $attribDefaults[$element][$lcattrib] )
+                               && $attribDefaults[$element][$lcattrib] == $value
+                       ) {
                                unset( $attribs[$attrib] );
                        }
 
@@ -407,8 +406,9 @@ class Html {
                }
 
                // More subtle checks
-               if ( $element === 'link' && isset( $attribs['type'] )
-               && strval( $attribs['type'] ) == 'text/css' ) {
+               if ( $element === 'link'
+                       && isset( $attribs['type'] ) && strval( $attribs['type'] ) == 'text/css'
+               ) {
                        unset( $attribs['type'] );
                }
                if ( $element === 'input' ) {
@@ -507,8 +507,7 @@ class Html {
 
                        // For boolean attributes, support array( 'foo' ) instead of
                        // requiring array( 'foo' => 'meaningless' ).
-                       if ( is_int( $key )
-                       && in_array( strtolower( $value ), self::$boolAttribs ) ) {
+                       if ( is_int( $key ) && in_array( strtolower( $value ), self::$boolAttribs ) ) {
                                $key = $value;
                        }
 
@@ -587,14 +586,13 @@ class Html {
                        // marks omitted, but not all.  (Although a literal " is not
                        // permitted, we don't check for that, since it will be escaped
                        // anyway.)
-                       #
+
                        // See also research done on further characters that need to be
                        // escaped: http://code.google.com/p/html5lib/issues/detail?id=93
                        $badChars = "\\x00- '=<>`/\x{00a0}\x{1680}\x{180e}\x{180F}\x{2000}\x{2001}"
                                . "\x{2002}\x{2003}\x{2004}\x{2005}\x{2006}\x{2007}\x{2008}\x{2009}"
                                . "\x{200A}\x{2028}\x{2029}\x{202F}\x{205F}\x{3000}";
-                       if ( $wgWellFormedXml || $value === ''
-                       || preg_match( "![$badChars]!u", $value ) ) {
+                       if ( $wgWellFormedXml || $value === '' || preg_match( "![$badChars]!u", $value ) ) {
                                $quote = '"';
                        } else {
                                $quote = '';
index 0e9ef2b..d84e5d0 100644 (file)
@@ -209,8 +209,9 @@ class Linker {
                $dummy = new DummyLinker; // dummy linker instance for bc on the hooks
 
                $ret = null;
-               if ( !wfRunHooks( 'LinkBegin', array( $dummy, $target, &$html,
-               &$customAttribs, &$query, &$options, &$ret ) ) ) {
+               if ( !wfRunHooks( 'LinkBegin',
+                       array( $dummy, $target, &$html, &$customAttribs, &$query, &$options, &$ret ) )
+               ) {
                        wfProfileOut( __METHOD__ );
                        return $ret;
                }
@@ -1490,9 +1491,12 @@ class Linker {
                # Foobar -- normal
                # :Foobar -- override special treatment of prefix (images, language links)
                # /Foobar -- convert to CurrentPage/Foobar
-               # /Foobar/ -- convert to CurrentPage/Foobar, strip the initial / from text
+               # /Foobar/ -- convert to CurrentPage/Foobar, strip the initial and final / from text
                # ../ -- convert to CurrentPage, from CurrentPage/CurrentSubPage
-               # ../Foobar -- convert to CurrentPage/Foobar, from CurrentPage/CurrentSubPage
+               # ../Foobar -- convert to CurrentPage/Foobar,
+               #              (from CurrentPage/CurrentSubPage)
+               # ../Foobar/ -- convert to CurrentPage/Foobar, use 'Foobar' as text
+               #              (from CurrentPage/CurrentSubPage)
 
                wfProfileIn( __METHOD__ );
                $ret = $target; # default return value is no change
@@ -1538,7 +1542,7 @@ class Linker {
                                                $ret = implode( '/', array_slice( $exploded, 0, -$dotdotcount ) );
                                                # / at the end means don't show full path
                                                if ( substr( $nodotdot, -1, 1 ) === '/' ) {
-                                                       $nodotdot = substr( $nodotdot, 0, -1 );
+                                                       $nodotdot = rtrim( $nodotdot, '/' );
                                                        if ( $text === '' ) {
                                                                $text = $nodotdot . $suffix;
                                                        }
index b3b3b88..b0bbcdd 100644 (file)
  * @return string
  */
 function wfOutputHandler( $s ) {
-       global $wgDisableOutputCompression, $wgValidateAllHtml;
-       $s = wfMangleFlashPolicy( $s );
+       global $wgDisableOutputCompression, $wgValidateAllHtml, $wgMangleFlashPolicy;
+       if ( $wgMangleFlashPolicy ) {
+               $s = wfMangleFlashPolicy( $s );
+       }
        if ( $wgValidateAllHtml ) {
                $headers = headers_list();
                $isHTML = false;
index a517788..2936ca3 100644 (file)
@@ -3163,6 +3163,7 @@ class OutputPage extends ContextSource {
                        'wgMonthNames' => $lang->getMonthNamesArray(),
                        'wgMonthNamesShort' => $lang->getMonthAbbreviationsArray(),
                        'wgRelevantPageName' => $relevantTitle->getPrefixedDBkey(),
+                       'wgRelevantArticleId' => $relevantTitle->getArticleId(),
                );
 
                if ( $user->isLoggedIn() ) {
index bca2f67..1654643 100644 (file)
@@ -510,15 +510,12 @@ class Sanitizer {
                                                $newparams = '';
                                        } else {
                                                # Keep track for later
-                                               if ( isset( $tabletags[$t] ) &&
-                                               !in_array( 'table', $tagstack ) ) {
+                                               if ( isset( $tabletags[$t] ) && !in_array( 'table', $tagstack ) ) {
                                                        $badtag = true;
-                                               } elseif ( in_array( $t, $tagstack ) &&
-                                               !isset( $htmlnest[$t] ) ) {
+                                               } elseif ( in_array( $t, $tagstack ) && !isset( $htmlnest[$t] ) ) {
                                                        $badtag = true;
                                                # Is it a self closed htmlpair ? (bug 5487)
-                                               } elseif ( $brace == '/>' &&
-                                               isset( $htmlpairs[$t] ) ) {
+                                               } elseif ( $brace == '/>' && isset( $htmlpairs[$t] ) ) {
                                                        $badtag = true;
                                                } elseif ( isset( $htmlsingleonly[$t] ) ) {
                                                        # Hack to force empty tag for unclosable elements
@@ -530,8 +527,7 @@ class Sanitizer {
                                                        # the tag stack so that we can match end tags
                                                        # instead of marking them as bad.
                                                        array_push( $tagstack, $t );
-                                               } elseif ( isset( $tabletags[$t] )
-                                               && in_array( $t, $tagstack ) ) {
+                                               } elseif ( isset( $tabletags[$t] ) && in_array( $t, $tagstack ) ) {
                                                        // New table tag but forgot to close the previous one
                                                        $text .= "</$t>";
                                                } else {
@@ -1120,14 +1116,14 @@ class Sanitizer {
                        $id = preg_replace( '/[ \t\n\r\f_\'"&#%]+/', '_', $id );
                        $id = trim( $id, '_' );
                        if ( $id === '' ) {
-                               # Must have been all whitespace to start with.
+                               // Must have been all whitespace to start with.
                                return '_';
                        } else {
                                return $id;
                        }
                }
 
-               # HTML4-style escaping
+               // HTML4-style escaping
                static $replace = array(
                        '%3A' => ':',
                        '%' => '.'
@@ -1136,8 +1132,7 @@ class Sanitizer {
                $id = urlencode( strtr( $id, ' ', '_' ) );
                $id = str_replace( array_keys( $replace ), array_values( $replace ), $id );
 
-               if ( !preg_match( '/^[a-zA-Z]/', $id )
-               && !in_array( 'noninitial', $options ) ) {
+               if ( !preg_match( '/^[a-zA-Z]/', $id ) && !in_array( 'noninitial', $options ) ) {
                        // Initial character must be a letter!
                        $id = "x$id";
                }
@@ -1368,8 +1363,7 @@ class Sanitizer {
        static function normalizeEntity( $name ) {
                if ( isset( self::$htmlEntityAliases[$name] ) ) {
                        return '&' . self::$htmlEntityAliases[$name] . ';';
-               } elseif ( in_array( $name,
-               array( 'lt', 'gt', 'amp', 'quot' ) ) ) {
+               } elseif ( in_array( $name, array( 'lt', 'gt', 'amp', 'quot' ) ) ) {
                        return "&$name;";
                } elseif ( isset( self::$htmlEntities[$name] ) ) {
                        return '&#' . self::$htmlEntities[$name] . ';';
index b97d36a..f913859 100644 (file)
@@ -503,7 +503,7 @@ class Title {
                }
 
                $t = new Title();
-               $t->mDbkeyform = Title::makeName( $ns, $title, $fragment, $interwiki );
+               $t->mDbkeyform = Title::makeName( $ns, $title, $fragment, $interwiki, true );
                if ( $t->secureAndSplit() ) {
                        return $t;
                } else {
@@ -747,12 +747,20 @@ class Title {
         * @param string $title The DB key form the title
         * @param string $fragment The link fragment (after the "#")
         * @param string $interwiki The interwiki prefix
+        * @param boolean $canoncialNamespace If true, use the canonical name for
+        *   $ns instead of the localized version.
         * @return string The prefixed form of the title
         */
-       public static function makeName( $ns, $title, $fragment = '', $interwiki = '' ) {
+       public static function makeName( $ns, $title, $fragment = '', $interwiki = '',
+               $canoncialNamespace = false
+       ) {
                global $wgContLang;
 
-               $namespace = $wgContLang->getNsText( $ns );
+               if ( $canoncialNamespace ) {
+                       $namespace = MWNamespace::getCanonicalName( $ns );
+               } else {
+                       $namespace = $wgContLang->getNsText( $ns );
+               }
                $name = $namespace == '' ? $title : "$namespace:$title";
                if ( strval( $interwiki ) != '' ) {
                        $name = "$interwiki:$name";
index 90d33fb..c4a6127 100644 (file)
@@ -641,10 +641,11 @@ class User implements IDBAccessObject {
                global $wgContLang, $wgMaxNameChars;
 
                if ( $name == ''
-               || User::isIP( $name )
-               || strpos( $name, '/' ) !== false
-               || strlen( $name ) > $wgMaxNameChars
-               || $name != $wgContLang->ucfirst( $name ) ) {
+                       || User::isIP( $name )
+                       || strpos( $name, '/' ) !== false
+                       || strlen( $name ) > $wgMaxNameChars
+                       || $name != $wgContLang->ucfirst( $name )
+               ) {
                        wfDebugLog( 'username', __METHOD__ .
                                ": '$name' invalid due to empty, IP, slash, length, or lowercase" );
                        return false;
@@ -4417,8 +4418,8 @@ class User implements IDBAccessObject {
                global $wgImplicitGroups;
 
                $groups = $wgImplicitGroups;
-               # Deprecated, use $wgImplictGroups instead
-               wfRunHooks( 'UserGetImplicitGroups', array( &$groups ) );
+               # Deprecated, use $wgImplicitGroups instead
+               wfRunHooks( 'UserGetImplicitGroups', array( &$groups ), '1.25' );
 
                return $groups;
        }
@@ -4511,6 +4512,7 @@ class User implements IDBAccessObject {
 
                // Same thing for remove
                if ( empty( $wgRemoveGroups[$group] ) ) {
+                       // Do nothing
                } elseif ( $wgRemoveGroups[$group] === true ) {
                        $groups['remove'] = self::getAllGroups();
                } elseif ( is_array( $wgRemoveGroups[$group] ) ) {
@@ -4536,6 +4538,7 @@ class User implements IDBAccessObject {
 
                // Now figure out what groups the user can add to him/herself
                if ( empty( $wgGroupsAddToSelf[$group] ) ) {
+                       // Do nothing
                } elseif ( $wgGroupsAddToSelf[$group] === true ) {
                        // No idea WHY this would be used, but it's there
                        $groups['add-self'] = User::getAllGroups();
@@ -4544,6 +4547,7 @@ class User implements IDBAccessObject {
                }
 
                if ( empty( $wgGroupsRemoveFromSelf[$group] ) ) {
+                       // Do nothing
                } elseif ( $wgGroupsRemoveFromSelf[$group] === true ) {
                        $groups['remove-self'] = User::getAllGroups();
                } elseif ( is_array( $wgGroupsRemoveFromSelf[$group] ) ) {
index dd27f3d..217ba3f 100644 (file)
@@ -111,7 +111,7 @@ wfProfileIn( 'WebStart.php-ob_start' );
 # Check that there is no previous output or previously set up buffers, because
 # that would cause us to potentially mix gzip and non-gzip output, creating a
 # big mess.
-if ( !defined( 'MW_NO_OUTPUT_BUFFER' ) && ob_get_level() == 0 ) {
+if ( ob_get_level() == 0 ) {
        require_once "$IP/includes/OutputHandler.php";
        ob_start( 'wfOutputHandler' );
 }
index 3f84f2a..99c0fb4 100644 (file)
@@ -2277,7 +2277,7 @@ abstract class ApiBase extends ContextSource {
                        ' "' . wfUrlencode( str_replace( ' ', '_', $this->getUser()->getName() ) ) . '"' .
                        ' "' . $request->getIP() . '"' .
                        ' "' . addslashes( $request->getHeader( 'Referer' ) ) . '"' .
-                       ' "' . addslashes( $request->getHeader( 'User-agent' ) ) . '"';
+                       ' "' . addslashes( $this->getMain()->getUserAgent() ) . '"';
                wfDebugLog( 'api-feature-usage', $s, 'private' );
        }
 
index ce8656e..966e82d 100644 (file)
@@ -67,6 +67,16 @@ class ApiFormatJson extends ApiFormatBase {
                        $this->getIsHtml(),
                        $params['utf8'] ? FormatJson::ALL_OK : FormatJson::XMLMETA_OK
                );
+
+               // Bug 66776: wfMangleFlashPolicy() is needed to avoid a nasty bug in
+               // Flash, but what it does isn't friendly for the API, so we need to
+               // work around it.
+               if ( preg_match( '/\<\s*cross-domain-policy\s*\>/i', $json ) ) {
+                       $json = preg_replace(
+                               '/\<(\s*cross-domain-policy\s*)\>/i', '\\u003C$1\\u003E', $json
+                       );
+               }
+
                $callback = $params['callback'];
                if ( $callback !== null ) {
                        $callback = preg_replace( "/[^][.\\'\\\"_A-Za-z0-9]/", '', $callback );
index ae93812..a4b4a11 100644 (file)
@@ -35,6 +35,22 @@ class ApiFormatPhp extends ApiFormatBase {
        }
 
        public function execute() {
-               $this->printText( serialize( $this->getResultData() ) );
+               $text = serialize( $this->getResultData() );
+
+               // Bug 66776: wfMangleFlashPolicy() is needed to avoid a nasty bug in
+               // Flash, but what it does isn't friendly for the API. There's nothing
+               // we can do here that isn't actively broken in some manner, so let's
+               // just be broken in a useful manner.
+               if ( $this->getConfig()->get( 'MangleFlashPolicy' ) &&
+                       in_array( 'wfOutputHandler', ob_list_handlers(), true ) &&
+                       preg_match( '/\<\s*cross-domain-policy\s*\>/i', $text )
+               ) {
+                       $this->dieUsage(
+                               'This response cannot be represented using format=php. See https://bugzilla.wikimedia.org/show_bug.cgi?id=66776',
+                               'internalerror'
+                       );
+               }
+
+               $this->printText( $text );
        }
 }
index 10a99c9..004bfae 100644 (file)
@@ -1242,6 +1242,21 @@ class ApiMain extends ApiBase {
                return $this->mModuleMgr;
        }
 
+       /**
+        * Fetches the user agent used for this request
+        *
+        * The value will be the combination of the 'Api-User-Agent' header (if
+        * any) and the standard User-Agent header (if any).
+        *
+        * @return string
+        */
+       public function getUserAgent() {
+               return trim(
+                       $this->getRequest()->getHeader( 'Api-user-agent' ) . ' ' .
+                       $this->getRequest()->getHeader( 'User-agent' )
+               );
+       }
+
        /************************************************************************//**
         * @name   Deprecated
         * @{
index 0b1accb..4e95f5b 100644 (file)
@@ -135,21 +135,64 @@ class ApiQueryAllDeletedRevisions extends ApiQueryRevisionsBase {
 
                if ( $mode == 'all' ) {
                        if ( $params['namespace'] !== null ) {
-                               $this->addWhereFld( 'ar_namespace', $params['namespace'] );
+                               $namespaces = $params['namespace'];
+                               $this->addWhereFld( 'ar_namespace', $namespaces );
+                       } else {
+                               $namespaces = MWNamespace::getValidNamespaces();
                        }
 
-                       $from = $params['from'] === null
-                               ? null
-                               : $this->titlePartToKey( $params['from'], $params['namespace'] );
-                       $to = $params['to'] === null
-                               ? null
-                               : $this->titlePartToKey( $params['to'], $params['namespace'] );
-                       $this->addWhereRange( 'ar_title', $dir, $from, $to );
+                       // For from/to/prefix, we have to consider the potential
+                       // transformations of the title in all specified namespaces.
+                       // Generally there will be only one transformation, but wikis with
+                       // some namespaces case-sensitive could have two.
+                       if ( $params['from'] !== null || $params['to'] !== null ) {
+                               $isDirNewer = ( $dir === 'newer' );
+                               $after = ( $isDirNewer ? '>=' : '<=' );
+                               $before = ( $isDirNewer ? '<=' : '>=' );
+                               $where = array();
+                               foreach ( $namespaces as $ns ) {
+                                       $w = array();
+                                       if ( $params['from'] !== null ) {
+                                               $w[] = 'ar_title' . $after .
+                                                       $db->addQuotes( $this->titlePartToKey( $params['from'], $ns ) );
+                                       }
+                                       if ( $params['to'] !== null ) {
+                                               $w[] = 'ar_title' . $before .
+                                                       $db->addQuotes( $this->titlePartToKey( $params['to'], $ns ) );
+                                       }
+                                       $w = $db->makeList( $w, LIST_AND );
+                                       $where[$w][] = $ns;
+                               }
+                               if ( count( $where ) == 1 ) {
+                                       $where = key( $where );
+                                       $this->addWhere( $where );
+                               } else {
+                                       $where2 = array();
+                                       foreach ( $where as $w => $ns ) {
+                                               $where2[] = $db->makeList( array( $w, 'ar_namespace' => $ns ), LIST_AND );
+                                       }
+                                       $this->addWhere( $db->makeList( $where2, LIST_OR ) );
+                               }
+                       }
 
                        if ( isset( $params['prefix'] ) ) {
-                               $this->addWhere( 'ar_title' . $db->buildLike(
-                                       $this->titlePartToKey( $params['prefix'], $params['namespace'] ),
-                                       $db->anyString() ) );
+                               $where = array();
+                               foreach ( $namespaces as $ns ) {
+                                       $w = 'ar_title' . $db->buildLike(
+                                               $this->titlePartToKey( $params['prefix'], $ns ),
+                                               $db->anyString() );
+                                       $where[$w][] = $ns;
+                               }
+                               if ( count( $where ) == 1 ) {
+                                       $where = key( $where );
+                                       $this->addWhere( $where );
+                               } else {
+                                       $where2 = array();
+                                       foreach ( $where as $w => $ns ) {
+                                               $where2[] = $db->makeList( array( $w, 'ar_namespace' => $ns ), LIST_AND );
+                                       }
+                                       $this->addWhere( $db->makeList( $where2, LIST_OR ) );
+                               }
                        }
                } else {
                        if ( $this->getConfig()->get( 'MiserMode' ) ) {
index 5e61ed1..263ab0d 100644 (file)
@@ -58,10 +58,10 @@ class ApiQueryInfo extends ApiQueryBase {
         */
        public function requestExtraData( $pageSet ) {
                $pageSet->requestField( 'page_restrictions' );
-               // when resolving redirects, no page will have this field
-               if ( !$pageSet->isResolvingRedirects() ) {
-                       $pageSet->requestField( 'page_is_redirect' );
-               }
+               // If the pageset is resolving redirects we won't get page_is_redirect.
+               // But we can't know for sure until the pageset is executed (revids may
+               // turn it off), so request it unconditionally.
+               $pageSet->requestField( 'page_is_redirect' );
                $pageSet->requestField( 'page_is_new' );
                $config = $this->getConfig();
                $pageSet->requestField( 'page_touched' );
index 9ddadcb..43e4c61 100644 (file)
@@ -182,8 +182,6 @@ class ApiUpload extends ApiBase {
                try {
                        $result['filekey'] = $this->performStash();
                        $result['sessionkey'] = $result['filekey']; // backwards compatibility
-               } catch ( UploadStashException $e ) {
-                       $this->handleStashException( $e );
                } catch ( MWException $e ) {
                        $result['warnings']['stashfailed'] = $e->getMessage();
                }
diff --git a/includes/api/i18n/ar.json b/includes/api/i18n/ar.json
new file mode 100644 (file)
index 0000000..8096504
--- /dev/null
@@ -0,0 +1,21 @@
+{
+       "@metadata": {
+               "authors": [
+                       "Meno25"
+               ]
+       },
+       "apihelp-main-param-format": "صيغة الخرج.",
+       "apihelp-block-description": "منع مستخدم.",
+       "apihelp-block-param-reason": "السبب للمنع.",
+       "apihelp-block-param-nocreate": "امنع إنشاء الحسابات.",
+       "apihelp-compare-param-fromtitle": "العنوان الأول للمقارنة.",
+       "apihelp-compare-param-fromid": "رقم الصفحة الأول للمقارنة.",
+       "apihelp-compare-param-fromrev": "أول مراجعة للمقارنة.",
+       "apihelp-compare-param-totitle": "العنوان الثاني للمقارنة.",
+       "apihelp-compare-param-toid": "رقم الصفحة الثاني للمقارنة.",
+       "apihelp-compare-param-torev": "المراجعة الثانية للمقارنة.",
+       "apihelp-createaccount-param-name": "اسم المستخدم.",
+       "apihelp-delete-description": "حذف صفحة.",
+       "apihelp-delete-param-unwatch": "أزل الصفحة من قائمة مراقبتك.",
+       "apihelp-edit-description": "إنشاء وتعديل الصفحات."
+}
index e7276ea..cfc644b 100644 (file)
@@ -8,5 +8,14 @@
        "apihelp-main-param-action": "Дзеяньне для выкананьня.",
        "apihelp-main-param-format": "Фармат вываду.",
        "apihelp-main-param-maxlag": "Максымальная затрымка можа ўжывацца, калі MediaWiki ўсталяваная ў клястэр з рэплікаванай базай зьвестак. Дзеля захаваньня дзеяньняў, якія выклікаюць затрымку рэплікацыі, гэты парамэтар можа прымусіць кліента чакаць, пакуль затрымка рэплікацыі меншая за яго значэньне. У выпадку доўгай затрымкі, вяртаецца код памылкі «maxlag» з паведамленьнем кшталту «Чаканьне $host: $lag сэкундаў затрымкі».<br />Глядзіце https://www.mediawiki.org/wiki/Manual:Maxlag_parameter дзеля дадатковай інфармацыі.",
-       "apihelp-main-param-smaxage": "Выстаўце загаловак s-maxage на зададзеную колькасьць сэкундаў. Памылкі ніколі не кэшуюцца."
+       "apihelp-main-param-smaxage": "Выстаўце загаловак s-maxage на зададзеную колькасьць сэкундаў. Памылкі ніколі не кэшуюцца.",
+       "apihelp-main-param-maxage": "Выстаўляе загаловак max-age на зададзеную колькасьць сэкундаў. Памылкі ніколі не кэшуюцца.",
+       "apihelp-main-param-assert": "Упэўніцеся, што ўдзельнік увайшоў у сыстэму, калі зададзена «user», або мае правы робата, калі зададзена «bot».",
+       "apihelp-main-param-requestid": "Любое значэньне, пададзенае тут, будзе ўключанае ў адказ. Можа быць выкарыстанае для адрозьненьня запытаў.",
+       "apihelp-main-param-servedby": "Уключае ў вынік назву сэрвэра, які апрацаваў запыт.",
+       "apihelp-main-param-curtimestamp": "Уключае ў вынік пазнаку актуальнага часу.",
+       "apihelp-main-param-origin": "Пры звароце да API з дапамогай міждамэннага AJAX-запыту (CORS), выстаўце парамэтру значэньне зыходнага дамэну. Ён мусіць быць уключаны ў кожны папярэдні запыт і такім чынам мусіць быць часткай URI-запыту (ня цела POST). Ён мусіць супадаць з адной з крыніц у загалоўку Origin, павінна быць зададзена нешта кшталту http://en.wikipedia.org або https://meta.wikimedia.org. Калі парамэтар не супадае з загалоўкам Origin, будзе вернуты адказ з кодам памылкі 403. Калі парамэтар супадае з загалоўкам Origin і крыніца знаходзіцца ў белым сьпісе, будзе выстаўлены загаловак Access-Control-Allow-Origin.",
+       "apihelp-main-param-uselang": "Мова для выкарыстаньня ў перакладах паведамленьняў. Сьпіс кодаў можа быць атрыманы з [[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo]] з siprop=languages, або трэба вызначыць «user», каб ужываць наладкі мовы цяперашняга карыстальніка, або вызначыць «content», каб ужываць мову зьместу гэтай вікі.",
+       "apihelp-block-description": "Блякаваньне ўдзельніка.",
+       "apihelp-block-param-user": "Імя ўдзельніка, IP-адрас або IP-дыяпазон, якія вы хочаце заблякаваць."
 }
index 302738f..4c9fef4 100644 (file)
@@ -3,7 +3,8 @@
                "authors": [
                        "Florian",
                        "Kghbln",
-                       "Metalhead64"
+                       "Metalhead64",
+                       "Inkowik"
                ]
        },
        "apihelp-main-description": "<div class=\"hlist plainlinks api-main-links\">\n* [https://www.mediawiki.org/wiki/API:Main_page/de Dokumentation]\n* [https://www.mediawiki.org/wiki/API:FAQ/de Häufig gestellte Fragen]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Mailingliste]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API-Ankündigungen]\n* [https://bugzilla.wikimedia.org/buglist.cgi?component=API&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&order=bugs.delta_ts Fehlerberichte und Anfragen]\n</div>\n<strong>Status:</strong> Alle auf dieser Seite gezeigten Funktionen sollten funktionieren, aber die API ist noch in aktiver Entwicklung und könnte sich zu jeder Zeit ändern. Abonniere die [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ MediaWiki-API-Ankündigungs-Mailingliste] für Mitteilungen über Aktualisierungen.\n\n<strong>Fehlerhafte Anfragen:</strong> Wenn fehlerhafte Anfragen an die API gesendet werden, wird ein HTTP-Header mit dem Schlüssel „MediaWiki-API-Error“ versandt. Die zurückgesandten Werte des Headers und des Fehlercodes werden auf den gleichen Wert gesetzt. Für weitere Informationen siehe https://www.mediawiki.org/wiki/API:Errors_and_warnings.",
        "apihelp-parse-example-text": "Wikitext parsen.",
        "apihelp-patrol-description": "Kontrolliert eine Seite oder Version.",
        "apihelp-patrol-example-revid": "Kontrolliert eine Version",
+       "apihelp-protect-description": "Ändert den Schutzstatus einer Seite.",
+       "apihelp-protect-param-title": "Titel der Seite, die du (ent-)sperren möchtest. Kann nicht zusammen mit $1pageid verwendet werden.",
+       "apihelp-protect-param-pageid": "Seitenkennung der Seite, die du (ent-)sperren möchtest. Kann nicht zusammen mit $1title verwendet werden.",
+       "apihelp-protect-param-protections": "Liste der Schutzebenen nach dem Format Aktion=Ebene (z.B. edit=sysop).\n\n'''HINWEIS:''' Wenn eine Aktion nicht angegeben wird, wird deren Schutz entfernt.",
+       "apihelp-protect-param-expiry": "Zeitstempel des Schutzablaufs. Wenn nur ein Zeitstempel übergeben wird, ist dieser für alle Seitenschutze gültig. Um eine unendliche Schutzdauer festzulegen, kannst du die Werte „infinite“, „indefinite“, „infinity“ oder „never“ übergeben.",
+       "apihelp-protect-param-reason": "Grund für den Seitenschutz oder dessen Aufhebung.",
+       "apihelp-protect-param-cascade": "Aktiviert den Kaskadenschutz (alle eingebundenen Seiten werden ebenfalls geschützt). Wenn die übergebenen Schutzebenen keinen Kaskadenschutz unterstützen, wird dieser Parameter ignoriert.",
+       "apihelp-protect-param-watch": "Wenn vorhanden, fügt dieser Parameter die zu (ent-)sperrende Seite der Beobachtungsliste hinzu.",
+       "apihelp-protect-param-watchlist": "Die Seite bedingungslos zu deiner Beobachtungsliste hinzufügen oder von ihr entfernen, Einstellungen verwenden oder Beobachtung nicht ändern.",
        "apihelp-protect-example-protect": "Schützt eine Seite",
+       "apihelp-protect-example-unprotect": "Eine Seite entsperren, indem die Einschränkungen durch den Schutz auf „all“ gestellt werden.",
+       "apihelp-protect-example-unprotect2": "Eine Seite entsperren, indem keine Einschränkungen übergeben werden",
        "apihelp-purge-param-forcelinkupdate": "Aktualisiert die Linktabellen.",
        "apihelp-query-param-list": "Welche Listen abgerufen werden sollen.",
        "apihelp-query+allcategories-description": "Alle Kategorien aufzählen.",
diff --git a/includes/api/i18n/el.json b/includes/api/i18n/el.json
new file mode 100644 (file)
index 0000000..d4d239f
--- /dev/null
@@ -0,0 +1,12 @@
+{
+       "@metadata": {
+               "authors": [
+                       "Glavkos"
+               ]
+       },
+       "apihelp-block-description": "Φραγή χρήστη",
+       "apihelp-block-param-user": "Όνομα χρήστη, διεύθυνση IP ή εύρος διευθύνσεων IP που θέλετε να επιβάλετε φραγή.",
+       "apihelp-block-param-reason": "Λόγος φραγής.",
+       "apihelp-createaccount-param-name": "Όνομα χρήστη.",
+       "apihelp-delete-description": "Διαγραφή σελίδας."
+}
index ba1d552..157b27b 100644 (file)
@@ -3,9 +3,13 @@
                "authors": [
                        "Macofe",
                        "Effy",
-                       "Alan"
+                       "Alan",
+                       "Fitoschido"
                ]
        },
+       "apihelp-main-param-action": "Qué acción se realizará.",
+       "apihelp-main-param-format": "El formato de la salida.",
+       "apihelp-main-param-curtimestamp": "Incluir la marca de tiempo actual en el resultado.",
        "apihelp-block-description": "Bloquear usuario",
        "apihelp-block-param-reason": "Razón para el bloqueo.",
        "apihelp-block-param-nocreate": "Prevenir la creación de cuentas.",
        "apihelp-delete-param-watch": "Añadir esta página a tu lista de seguimiento.",
        "apihelp-delete-param-unwatch": "Borrar esta página de tu lista de seguimiento.",
        "apihelp-delete-example-simple": "Borrar la Página Principal",
+       "apihelp-disabled-description": "Se desactivó este módulo.",
        "apihelp-edit-description": "Crear y editar páginas.",
+       "apihelp-edit-param-sectiontitle": "El título de una sección nueva.",
+       "apihelp-edit-param-text": "Contenido de la página.",
        "apihelp-edit-param-minor": "Edición menor.",
        "apihelp-edit-param-notminor": "Edición no menor.",
        "apihelp-edit-param-bot": "Marcar esta edición como de bot.",
        "apihelp-edit-example-edit": "Editar una página",
        "apihelp-expandtemplates-param-title": "Título de la página.",
+       "apihelp-expandtemplates-param-text": "Sintaxis wiki que se convertirá.",
+       "apihelp-feedcontributions-description": "Devuelve el canal de contribuciones de un usuario.",
+       "apihelp-feedcontributions-param-feedformat": "El formato del canal.",
        "apihelp-feedrecentchanges-param-hideminor": "Ocultar cambios menores.",
        "apihelp-login-param-name": "Nombre de usuario.",
        "apihelp-login-param-password": "Contraseña.",
@@ -36,5 +46,7 @@
        "apihelp-query+search-param-info": "Qué metadatos devolver.",
        "apihelp-query+userinfo-description": "Obtener información sobre el usuario actual.",
        "apihelp-query+watchlist-param-excludeuser": "No listar cambios de este usuario.",
-       "apihelp-query+watchlistraw-param-show": "Sólo listar los elementos que cumplen estos criterios."
+       "apihelp-query+watchlistraw-param-show": "Sólo listar los elementos que cumplen estos criterios.",
+       "api-help-parameters": "{{PLURAL:$1|Parámetro|Parámetros}}:",
+       "api-help-examples": "{{PLURAL:$1|Ejemplo|Ejemplos}}:"
 }
index 5a9e66b..4b1cf12 100644 (file)
        "apihelp-query+search-example-text": "Rechercher des textes pour « signification »",
        "apihelp-query+search-example-generator": "Obtenir les informations sur les pages renvoyées par une recherche de « signification »",
        "apihelp-query+siteinfo-description": "Renvoyer les informations générales sur le site.",
+       "apihelp-query+siteinfo-param-prop": "Quelles informations obtenir :\n;general:Information globale du système.\n;namespaces:Liste des espaces de nom déclarés et leur nom canonique.\n;namespacealiases:Liste des alias des espaces de nom déclarés.\n;specialpagealiases:Liste des alias des pages spéciales.\n;magicwords:Liste des mots magiques et leurs alias.\n;statistics:Renvoie les statistiques du site.\n;interwikimap:Renvoie la correspondance interwiki (éventuellement filtrée, (éventuellement localisée en utilisant $1inlanguagecode)).\n;dbrepllag:Renvoie le serveur de base de donnée avec la plus grande latence de réplication.\n;usergroups:Renvoie les groupes utilisateur et les droits associés.\n;extensions:Renvoie les extensions installées sur le wiki.\n;fileextensions:Renvoie la liste des extensions de fichier autorisées au téléchargement.\n;rightsinfo:Renvoie l’information sur les droits du wiki (sa licence), si elle est disponible.\n;restrictions:Renvoie l’information sur les types de restriction disponibles (protection).\n;languages:Renvoie une liste des langues que supporte MédiaWiki (éventuellement localisé en utilisant $1inlanguagecode).\n;skins:Renvoie une liste de tous les habillages activés (éventuellement localisé en utilisant $1inlanguagecode, sinon dans la langue du contenu).\n;extensiontags:Renvoie une liste des balises d’extension de l’analyseur.\n;functionhooks:Renvoie une liste des accroches de fonction de l’analyseur.\n;showhooks:Renvoie une liste de toutes les accroches souscrites (contenu de $wgHooks).\n;variables:Renvoie une liste des IDs de variable.\n;protocols:Renvoie une liste des protocoles qui sont autorisés dans les liens externes.\n;defaultoptions:Renvoie les valeurs par défaut pour les préférences utilisateur.",
+       "apihelp-query+siteinfo-param-filteriw": "Renvoyer uniquement les entrées locales ou uniquement les non locales de la correspondance interwiki.",
+       "apihelp-query+siteinfo-param-showalldb": "Lister tous les serveurs de base de données, pas seulement celui avec la plus grande latence.",
+       "apihelp-query+siteinfo-param-numberingroup": "Liste le nombre d’utilisateurs dans les groupes.",
+       "apihelp-query+siteinfo-param-inlanguagecode": "Code de langue pour les noms de langue localisés (du mieux possible) et les noms d’habillage.",
+       "apihelp-query+siteinfo-example-simple": "Extraire les informations du site",
+       "apihelp-query+siteinfo-example-interwiki": "Extraire une liste des préfixes interwiki locaux",
+       "apihelp-query+siteinfo-example-replag": "Vérifier la latence de réplication actuelle",
+       "apihelp-query+stashimageinfo-description": "Renvoie les informations de fichier des fichiers mis en réserve.",
+       "apihelp-query+stashimageinfo-param-filekey": "Clé qui identifie un téléchargement précédent qui a été temporairement mis en réserve.",
+       "apihelp-query+stashimageinfo-param-sessionkey": "Alias pour $1filekey, pour la compatibilité descendante.",
+       "apihelp-query+stashimageinfo-param-prop": "Quelles informations de l’image obtenir :\n;timestamp:Ajoute l’horodatage pour la version téléchargée.\n;canonicaltitle:Ajoute le titre canonique du fichier image.\n;url:Fournit l’URL de l’image et sa page de description.\n;size:Ajoute la taille de l’image en octets et la hauteur, la largeur et le nombre de pages (si c&est applicable).\n;dimensions:Alias pour la taille.\n;sha1:Ajoute le hachage SHA-1 pour l’image.\n;mime:Ajoute le type MIME de l’image.\n;thumbmime:Ajoute le type MIME de la vignette de l’image (nécessite l’URL et le paramètre $1urlwidth).\n;metadata:Liste les métadonnées Exif pour la version de l’image.\n;commonmetadata:Liste les métadonnées génériques du format de fichier pour la version de l’image.\n;extmetadata:Liste les métadonnées mises en forme combinées depuis diverses sources. Les résultats sont au format HTML.\n;bitdepth:Ajoute la profondeur de bits de la version.",
+       "apihelp-query+stashimageinfo-example-simple": "Renvoie les informations sur un fichier mis en réserve.",
+       "apihelp-query+stashimageinfo-example-params": "Renvoie les vignettes pour deux fichiers mis en réserve",
+       "apihelp-query+tags-description": "Lister les balises de modification.",
+       "apihelp-query+tags-param-limit": "Le nombre maximal de balises à lister.",
+       "apihelp-query+tags-param-prop": "Quelles propriétés récupérer :\n;name:Ajoute le nom de la balise.\n;displayname:Ajoute le message système pour la balise.\n;description:Ajoute la description de la balise.\n;hitcount:Ajoute le nombre de révisions qui ont cette balise.",
+       "apihelp-query+tags-example-simple": "Lister les balises disponibles",
+       "apihelp-query+templates-description": "Renvoie toutes les pages incluses dans les pages fournies.",
+       "apihelp-query+templates-param-namespace": "Afficher les modèles uniquement dans ces espaces de nom.",
+       "apihelp-query+templates-param-limit": "Combien de modèles renvoyer.",
+       "apihelp-query+templates-param-templates": "Lister uniquement ces modèles. Utile pour vérifier si une certaine page utilise un modèle donné.",
+       "apihelp-query+templates-param-dir": "La direction dans laquelle lister.",
+       "apihelp-query+templates-example-simple": "Obtenir les modèles de [[Main Page]]",
+       "apihelp-query+templates-example-generator": "Obtenir des informations sur les pages modèle de [[Main Page]]",
+       "apihelp-query+templates-example-namespaces": "Obtenir les modèles de [[Main Page]] dans les espaces de nom Utilisateur et Modèle",
+       "apihelp-query+tokens-description": "Récupère les jetons pour les actions de modification de données.",
+       "apihelp-query+tokens-param-type": "Types de jeton à demander.",
+       "apihelp-query+tokens-example-simple": "Récupérer un jeton csrf (par défaut)",
+       "apihelp-query+tokens-example-types": "Récupérer un jeton de suivi et un de patrouille",
+       "apihelp-query+transcludedin-description": "Trouver toutes les pages qui incluent les pages données.",
+       "apihelp-query+transcludedin-param-prop": "Quelles propriétés obtenir :\n;pageid:ID de page de chaque page.\n;title:Titre de chaque page.\n;redirect:Marque si cette page est une redirection.",
+       "apihelp-query+transcludedin-param-namespace": "Inclure uniquement les pages dans ces espaces de nom.",
+       "apihelp-query+transcludedin-param-limit": "Combien en renvoyer.",
+       "apihelp-query+transcludedin-param-show": "Afficher uniquement les éléments qui correspondent à ces critères:\n;redirect:Afficher uniquement les redirections.\n;!redirects:Afficher uniquement les non-redirections.",
+       "apihelp-query+transcludedin-example-simple": "Obtenir une liste des pages incluant [[Main Page]]",
+       "apihelp-query+transcludedin-example-generator": "Obtenir des informations sur les pages incluant [[Main Page]]",
+       "apihelp-query+usercontribs-description": "Obtenir toutes les modifications par un utilisateur.",
+       "apihelp-query+usercontribs-param-limit": "Le nombre maximal de contributions à renvoyer.",
+       "apihelp-query+usercontribs-param-start": "L’horodatage auquel démarrer le retour.",
+       "apihelp-query+usercontribs-param-end": "L’horodatage auquel arrêter le retour.",
+       "apihelp-query+usercontribs-param-user": "Les utilisateurs pour lesquels récupérer les contributions.",
+       "apihelp-query+usercontribs-param-userprefix": "Récupérer les contributions pour tous les utilisateurs dont les noms commencent par cette valeur. Écrase $1user.",
+       "apihelp-query+usercontribs-param-namespace": "Lister uniquement les contributions dans ces espaces de nom.",
+       "apihelp-query+usercontribs-param-prop": "Inclure des informations supplémentaires:\n;ids:Ajoute l’ID de page et l’ID de révision.\n;title:Ajoute le titre et l’ID d’espace de noms de la page.\n;timestamp:Ajoute l’horodatage de la modification.\n;comment:Ajoute le commentaire de la modification.\n;parsedcomment:Ajoute le commentaire analysé de la modification.\n;size:Ajoute la nouvelle taille de la modification.\n;sizediff:Ajoute le delta de taille de la modification par rapport à son parent.\n;flags:Ajoute les marques de la modification.\n;patrolled:Marque les modifications patrouillées.\n;tags:Liste les balises de la modification.",
+       "apihelp-query+usercontribs-param-show": "Afficher uniquement les éléments correspondant à ces critères, par ex. les modifications non mineures uniquement : $2show=!minor.\n\nSi $2show=patrolled ou $2show=!patrolled est positionné, les révisions plus anciennes que [https://www.mediawiki.org/wiki/Manual:$wgRCMaxAge $wgRCMaxAge] ($1 {{PLURAL:$1|seconde|secondes}}) ne seront pas affichées.",
+       "apihelp-query+usercontribs-param-tag": "Lister uniquement les révisions marquées avec cette balise.",
+       "apihelp-query+usercontribs-param-toponly": "Lister uniquement les modifications qui sont la dernière révision.",
+       "apihelp-query+usercontribs-example-user": "Afficher les contributions de [[User:Exemple]]",
+       "apihelp-query+usercontribs-example-ipprefix": "Afficher les contributions de toutes les adresses IP avec le préfixe « 192.0.2. »",
+       "apihelp-query+userinfo-description": "Obtenir de l’information sur l’utilisateur courant.",
+       "apihelp-query+userinfo-param-prop": "Quelles informations inclure :\n;blockinfo:Marque si l’utilisateur actuel est bloqué, par qui, et pour quelle raison.\n;hasmsg:Ajoute une balise « message » si l’utilisateur actuel a des messages en cours.\n;groups:Liste tous les groupes auxquels appartient l’utilisateur actuel.\n;implicitgroups:Liste tous les groupes dont l’utilisateur actuel est automatiquement membre.\n;rights:Liste tous les droits qu’a l’utilisateur actuel.\n;changeablegroups:Liste les groupes pour lesquels l’utilisateur actuel peut ajouter ou supprimer.\n;options:Liste toutes les préférences qu’a défini l’utilisateur actuel.\n;preferencestoken:OBSOLETE ! Obtient un jeton pour modifier les préférences de l’utilisateur actuel.\n;editcount:Ajoute le compteur de modifications de l’utilisateur actuel.\n;ratelimits:Liste toutes les limites de débit s’appliquant à l’utilisateur actuel.\n;realname:Ajoute le vrai nom de l’utilisateur actuel.\n;email:Ajoute l’adresse de courriel de l’utilisateur et sa date d’authentification.\n;acceptlang:Renvoie en écho l’entête Accept-Language envoyé par le client dans un format structuré.\n;registrationdate:Ajoute la date d’inscription de l’utilisateur.\n;unreadcount:Ajoute le compteur de pages non lues de la liste de suivi de l’utilisateur (au maximum $1 ; renvoie « $2 » s’il y en a plus).",
        "apihelp-format-example-generic": "Mettre en forme le résultat de la requête dans le format $1",
        "apihelp-dbg-description": "Extraire les données au format de var_export() de PHP.",
        "apihelp-dbgfm-description": "Extraire les données au format de var_export() de PHP (affiché proprement en HTML).",
index 8013687..b529fa9 100644 (file)
@@ -6,5 +6,6 @@
        },
        "apihelp-login-param-name": "Brûkersnamme.",
        "apihelp-login-param-password": "Wachtwurd.",
-       "apihelp-userrights-param-user": "Brûkersnamme."
+       "apihelp-userrights-param-user": "Brûkersnamme.",
+       "api-help-examples": "{{PLURAL:$1|Foarbyld|Foarbylden}}:"
 }
diff --git a/includes/api/i18n/ko.json b/includes/api/i18n/ko.json
new file mode 100644 (file)
index 0000000..a417e47
--- /dev/null
@@ -0,0 +1,10 @@
+{
+       "@metadata": {
+               "authors": [
+                       "Kwj2772"
+               ]
+       },
+       "apihelp-main-description": "<div class=\"hlist plainlinks api-main-links\"> * [https://www.mediawiki.org/wiki/API:Main_page 설명문서] * [https://www.mediawiki.org/wiki/API:FAQ FAQ] * [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api 메일링 리스트] * [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API 공지 사항] * [https://bugzilla.wikimedia.org/buglist.cgi?component=API&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&order=bugs.delta_ts 버그 및 요청] </div>\n<strong>상태:</strong> 이 페이지에 표시된 모든 기능은 정상 작동할 것이지만, API는 여전히 활발하게 개발되고 있으며, 언제든지 바뀔 수 있습니다. 업데이트 정보를 받아보려면 [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ the mediawiki-api-announce 메일링 리스트]를 구독하십시오.\n\n<strong>잘못된 요청:</strong> API에 잘못된 요청이 전송되면 HTTP 헤더에서 \"MediaWiki-API-Error\" 키를 보내고, 헤더 값과 오류 코드가 같게 설정됩니다. 자세한 정보에 대해서는 https://www.mediawiki.org/wiki/API:Errors_and_warnings 를 참고하십시오.",
+       "apihelp-main-param-action": "수행할 동작",
+       "apihelp-main-param-format": "출력값의 형식."
+}
index 515ab8b..a3fc0db 100644 (file)
        "apihelp-opensearch-param-suggest": "Не прави ништо ако [https://www.mediawiki.org/wiki/Manual:$wgEnableOpenSearchSuggest $wgEnableOpenSearchSuggest] е неточно.",
        "apihelp-opensearch-param-format": "Формат на изводот.",
        "apihelp-opensearch-example-te": "Најди страници што почнуваат со „Те“",
+       "apihelp-options-description": "Смени ги нагодувањата на тековниот корисник.\n\nМожат да се зададат само можностите заведени во јадрото или во едно од воспоставените додатоци, или пак можности со клуч кој ја има претставката „userjs-“ (предвиден за употреба од кориснички скрипти).",
        "apihelp-options-param-reset": "Ги враќа поставките по основно.",
        "apihelp-options-param-resetkinds": "Писок на типови можности за повраток кога е зададена можноста „$1reset“.",
        "apihelp-options-param-change": "Список на промени во форматот name=value (на пр. skin=vector). Вредностите не треба да содржат исправени црти. Ако не зададете вредност (дури ни знак за равенство), на пр., можност|другаможност|..., ќе биде зададена вредноста на можноста по основно.",
        "apihelp-query+allcategories-param-from": "Од која категорија да почне набројувањето.",
        "apihelp-query+allcategories-param-to": "На која категорија да запре набројувањето.",
        "apihelp-query+allcategories-param-dir": "Насока на подредувањето.",
+       "apihelp-query+alldeletedrevisions-param-from": "Почни го исписот од овој наслов.",
+       "apihelp-query+alldeletedrevisions-param-to": "Запри го исписот на овој наслов.",
+       "apihelp-query+alldeletedrevisions-example-user": "Список на последните 50 избришани придонеси на Корисник:Пример",
+       "apihelp-query+alldeletedrevisions-example-ns-main": "Список на последните 50 избришани преработки во главниот именски простор",
        "apihelp-query+alllinks-param-namespace": "Именскиот простор што се набројува.",
        "apihelp-query+alllinks-param-limit": "Колку вкупно ставки да се дадат.",
        "apihelp-query+alllinks-param-dir": "Насока на исписот.",
index a75ebc8..ebd9958 100644 (file)
@@ -3,7 +3,8 @@
                "authors": [
                        "Chrumps",
                        "Py64",
-                       "Pan Cube"
+                       "Pan Cube",
+                       "Alan ffm"
                ]
        },
        "apihelp-main-param-action": "Wybierz akcję do wykonania.",
        "apihelp-main-param-assert": "Sprawdź, czy użytkownik jest zalogowany jeżeli jest ustawiony na \"użytkownik\", lub ma prawa bota jeśli \"bot\".",
        "apihelp-block-description": "Zablokuj użytkownika.",
        "apihelp-block-param-reason": "Powód blokady.",
+       "apihelp-block-param-nocreate": "Zapobiegnij utworzeniu konta.",
        "apihelp-createaccount-param-name": "Nazwa użytkownika",
+       "apihelp-delete-description": "Usuń stronę.",
+       "apihelp-delete-example-simple": "Usuń stronę główną",
+       "apihelp-edit-param-text": "Zawartość strony.",
+       "apihelp-edit-param-minor": "Drobna zmiana.",
        "apihelp-edit-example-edit": "Edytuj stronę",
+       "apihelp-emailuser-description": "Wyślij e‐mail do użytkownika.",
        "apihelp-help-description": "Wyświetl pomoc dla określonych modułów.",
        "apihelp-help-param-modules": "Moduły do wyświetlenia pomocy dla (wartości akcji= i format= parametry, lub \"głównego\"). Może określić podmoduły z \"+\".",
        "apihelp-help-param-recursivesubmodules": "Zawiera pomoc dla podmodułów rekursywnie.",
        "apihelp-help-example-main": "Pomoc dla modułu głównego",
        "apihelp-help-example-recursive": "Cała pomoc na jednej stronie.",
        "apihelp-help-example-help": "Pomoc dla modułu pomocy",
+       "apihelp-login-param-name": "Nazwa użytkownika.",
+       "apihelp-login-param-password": "Hasło.",
+       "apihelp-login-example-login": "Zaloguj się",
        "apihelp-move-description": "Przenieś stronę.",
+       "apihelp-move-param-ignorewarnings": "Ignoruj wszystkie ostrzeżenia.",
        "apihelp-protect-example-protect": "Zabezpiecz stronę",
        "apihelp-query+search-description": "Wykonaj wyszukiwanie pełnotekstowe.",
        "apihelp-query+watchlist-param-excludeuser": "Nie wyświetlaj zmian wykonanych przez tego użytkownika.",
index 4ac5531..526869a 100644 (file)
@@ -6,12 +6,23 @@
                        "WikiPhoenix"
                ]
        },
+       "apihelp-main-param-action": "Vilken åtgärd som ska utföras.",
+       "apihelp-main-param-format": "Formatet för utdata.",
        "apihelp-main-param-curtimestamp": "Inkludera den aktuella tidsstämpeln i resultatet.",
        "apihelp-block-description": "Blockera en användare.",
        "apihelp-block-param-user": "Användare, IP-adress eller IP-intervall du vill blockera.",
        "apihelp-block-param-reason": "Orsak till blockering.",
        "apihelp-block-param-anononly": "Blockera endast anonyma användare (t.ex. inaktivera anonyma redigeringar för denna IP-adress).",
        "apihelp-block-param-nocreate": "Förhindra registrering av användarkonton.",
+       "apihelp-block-param-hidename": "Döljer användarnamnet från blockeringsloggen. (Kräver rättigheten \"hideuser\").",
+       "apihelp-block-param-allowusertalk": "Låter användaren redigera sin egen diskussionssida (beror på $wgBlockAllowsUTEdit).",
+       "apihelp-block-param-reblock": "Skriv över befintlig blockering om användaren redan är blockerad.",
+       "apihelp-block-param-watchuser": "Bevaka användarens eller IP-adressens användarsida och diskussionssida",
+       "apihelp-compare-param-fromid": "Första sid-ID att jämföra.",
+       "apihelp-compare-param-fromrev": "Första version att jämföra.",
+       "apihelp-compare-param-toid": "Andra sid-ID att jämföra.",
+       "apihelp-compare-param-torev": "Andra version att jämföra.",
+       "apihelp-compare-example-1": "Skapa en diff mellan version 1 och 2",
        "apihelp-createaccount-description": "Skapa ett nytt användarkonto.",
        "apihelp-createaccount-param-name": "Användarnamn.",
        "apihelp-createaccount-param-password": "Lösenord (ignoreras om $1mailpassword angetts).",
        "apihelp-delete-example-reason": "Raderar huvudsidan med orsaken \"Förbereder flyttning\"",
        "apihelp-disabled-description": "Denna modul har inaktiverats.",
        "apihelp-edit-description": "Skapa och redigera sidor.",
+       "apihelp-edit-param-sectiontitle": "Rubriken för ett nytt avsnitt.",
        "apihelp-edit-param-text": "Sidans innehåll.",
        "apihelp-edit-param-summary": "Redigeringssammanfattning. Även avsnittets rubrik när $1section=new och $1sectiontitle inte anges.",
        "apihelp-edit-param-minor": "Mindre redigering.",
+       "apihelp-edit-param-bot": "Markera denna redigering som robotredigering.",
        "apihelp-edit-param-watch": "Lägg till sidan i din bevakningslista.",
        "apihelp-edit-param-unwatch": "Ta bort sidan från din bevakningslista.",
        "apihelp-edit-example-edit": "Redigera en sida",
        "apihelp-filerevert-param-comment": "Ladda upp kommentar.",
        "apihelp-help-example-recursive": "All hjälp på en sida",
        "apihelp-help-example-help": "Hjälp för själva hjälpmodulen",
+       "apihelp-login-param-name": "Användarnamn.",
+       "apihelp-login-param-password": "Lösenord.",
+       "apihelp-login-example-login": "Logga in",
+       "apihelp-logout-description": "Logga ut och rensa sessionsdata.",
+       "apihelp-logout-example-logout": "Logga ut den aktuella användaren",
+       "apihelp-move-description": "Flytta en sida.",
+       "apihelp-move-param-noredirect": "Skapa inte en omdirigering.",
+       "apihelp-opensearch-param-search": "Söksträng.",
        "apihelp-query+stashimageinfo-description": "Returnerar filinformation för temporära filer.",
        "apihelp-query+stashimageinfo-param-filekey": "Nyckel som identifierar en tidigare uppladdning som lagrats temporärt.",
        "apihelp-query+stashimageinfo-example-simple": "Returnerar information för en temporär fil",
        "apihelp-upload-param-filekey": "Nyckel som identifierar en tidigare uppladdning som lagrats temporärt.",
-       "apihelp-upload-param-stash": "Om angiven, kommer servern inte att lägga till filen till centralförvaret och lagra den temporärt.",
+       "apihelp-upload-param-stash": "Om angiven, kommer servern att temporärt lagra filen istället för att lägga till den i centralförvaret.",
        "api-help-main-header": "Huvudmodul",
        "api-help-flag-deprecated": "Denna modul är föråldrad.",
        "api-help-flag-internal": "<strong>Denna modul är intern eller instabil.</strong> Dess funktion kan ändras utan föregående meddelande.",
index 2b6de32..4fe99be 100644 (file)
@@ -5,10 +5,11 @@
                        "Linforest",
                        "Liuxinyu970226",
                        "Papapasan",
-                       "LNDDYL"
+                       "LNDDYL",
+                       "Shizhao"
                ]
        },
-       "apihelp-main-description": "<div class=\"hlist plainlinks api-main-links\">\n* [https://www.mediawiki.org/wiki/API:Main_page 文档]\n* [https://www.mediawiki.org/wiki/API:FAQ 常见问题]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api 邮件列表]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API公告]\n* [https://bugzilla.wikimedia.org/buglist.cgi?component=API&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&order=bugs.delta_ts 程序错误与功能请求]\n</div>\n<strong>状态信息:</strong> 本页所展示的所有特性都应正常工作,但是API仍在开发当中,将会随时变化。请订阅[https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ mediawiki-api-announce 邮件列表]以便获得更新通知。\n\n<strong>错误请求:</strong> 当API收到错误请求时,HTTP header将会返回一个包含\"MediaWiki-API-Error\"的值,随后header的值与error code将会送回并设置为相同的值。详细信息请参阅https://www.mediawiki.org/wiki/API:Errors_and_warnings 。",
+       "apihelp-main-description": "<div class=\"hlist plainlinks api-main-links\">\n* [https://www.mediawiki.org/wiki/API:Main_page/zh 文档]\n* [https://www.mediawiki.org/wiki/API:FAQ/zh 常见问题]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api 邮件列表]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API公告]\n* [https://bugzilla.wikimedia.org/buglist.cgi?component=API&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&order=bugs.delta_ts 程序错误与功能请求]\n</div>\n<strong>状态信息:</strong> 本页所展示的所有特性都应正常工作,但是API仍在开发当中,将会随时变化。请订阅[https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ mediawiki-api-announce 邮件列表]以便获得更新通知。\n\n<strong>错误请求:</strong> 当API收到错误请求时,HTTP header将会返回一个包含\"MediaWiki-API-Error\"的值,随后header的值与error code将会送回并设置为相同的值。详细信息请参阅 https://www.mediawiki.org/wiki/API:Errors_and_warnings 。",
        "apihelp-main-param-action": "要执行的操作。",
        "apihelp-main-param-format": "输出的格式。",
        "apihelp-main-param-curtimestamp": "在结果中包括当前时间戳。",
        "apihelp-login-example-login": "登录",
        "apihelp-logout-example-logout": "退出当前用户",
        "apihelp-move-description": "移动一个页面。",
+       "apihelp-move-param-from": "您希望移动的页面标题。不能与$1fromid一起使用。",
+       "apihelp-move-param-fromid": "您希望移动的页面ID。不能与$1from一起使用。",
        "apihelp-move-param-reason": "移动原因。",
        "apihelp-move-param-movetalk": "移动讨论页,如果存在。",
        "apihelp-move-param-movesubpages": "移动子页面,如果可以。",
        "apihelp-query+allcategories-description": "枚举所有类别。",
        "apihelp-query+allcategories-param-from": "要作为枚举起始点的类别。",
        "apihelp-query+allcategories-param-to": "要作为枚举终止点的类别。",
+       "apihelp-query+allcategories-param-dir": "排序方向。",
        "apihelp-query+allcategories-param-limit": "要返回多少个类别。",
        "apihelp-query+alldeletedrevisions-paraminfo-useronly": "只可以与$3user一起使用。",
        "apihelp-query+alldeletedrevisions-paraminfo-nonuseronly": "不能与$3user一起使用。",
        "apihelp-query+allmessages-param-lang": "返回这种语言的信息。",
        "apihelp-query+allmessages-param-prefix": "返回带有该前缀的消息。",
        "apihelp-query+allmessages-example-ipb": "显示以“ipb-”开始的消息",
+       "apihelp-query+allmessages-example-de": "显示德语版的“八月”和“首页”消息",
        "apihelp-query+allpages-param-filterredir": "要列出哪些页面。",
        "apihelp-query+allpages-param-minsize": "限于至少这么多字节的页面。",
        "apihelp-query+allpages-param-maxsize": "限于至多这么多字节的页面。",
        "apihelp-query+allredirects-param-limit": "返回的总计项目数。",
        "apihelp-query+allredirects-example-unique-generator": "获得所有目标页面,标记丢失的",
        "apihelp-query+allredirects-example-generator": "获得包含重定向的页面",
+       "apihelp-query+alltransclusions-description": "列出所有嵌入页面(使用&#123;&#123;x&#125;&#125;嵌入的页面),包括不存在的。",
        "apihelp-query+alltransclusions-param-namespace": "要列举的名字空间。",
+       "apihelp-query+allusers-param-dir": "排序方向。",
        "apihelp-query+allusers-param-group": "只包含指定组中的用户。",
        "apihelp-query+allusers-param-excludegroup": "排除指定组中的用户。",
        "apihelp-query+allusers-param-witheditsonly": "只列出有编辑的用户。",
        "apihelp-query+backlinks-example-simple": "显示至[[首页]]的链接",
        "apihelp-query+backlinks-example-generator": "获取关于链接至[[首页]]的页面的信息",
        "apihelp-query+blocks-description": "列出所有被封禁的用户和IP地址。",
+       "apihelp-query+blocks-param-ids": "要列出的封禁ID列表(可选)。",
+       "apihelp-query+blocks-param-users": "要搜索的用户列表(可选)。",
        "apihelp-query+blocks-example-simple": "封禁列表",
+       "apihelp-query+blocks-example-users": "列出用户Alice和Bob的封禁",
        "apihelp-query+categories-param-show": "显示何种分类。",
        "apihelp-query+categories-param-limit": "返回多少分类。",
+       "apihelp-query+categories-example-simple": "获取属于[[阿尔伯特·爱因斯坦]]的分类列表",
+       "apihelp-query+categories-example-generator": "获取有关用于[[阿尔伯特·爱因斯坦]]的分类的信息",
        "apihelp-query+categoryinfo-example-simple": "获取有关[[:Category:Foo]]和[[:Category:Bar]]的信息",
        "apihelp-query+categorymembers-param-sort": "要作为排序方式的属性。",
        "apihelp-query+categorymembers-param-startsortkey": "请改用$1starthexsortkey。",
        "apihelp-query+embeddedin-example-simple": "显示嵌入[[Template:Stub]]的页面",
        "apihelp-query+embeddedin-example-generator": "获取有关显示嵌入[[Template:Stub]]的页面的信息",
        "apihelp-query+extlinks-param-limit": "返回多少链接。",
+       "apihelp-query+extlinks-example-simple": "获取[[首页]]的外部链接列表",
        "apihelp-query+exturlusage-param-limit": "返回多少页面。",
        "apihelp-query+exturlusage-example-simple": "显示链接至http://www.mediawiki.org的页面",
        "apihelp-query+filearchive-param-sha1": "图片的SHA1哈希值。覆盖$1sha1base36。",
        "apihelp-query+info-description": "获取基本页面信息。",
        "apihelp-query+info-param-token": "请改用[[Special:ApiHelp/query+tokens|action=query&meta=tokens]]。",
        "apihelp-query+info-example-simple": "获取有关[[首页]]的信息",
+       "apihelp-query+info-example-protection": "获取[[首页]]的一般和保护信息",
        "apihelp-query+iwbacklinks-param-prefix": "跨维基前缀。",
+       "apihelp-query+iwbacklinks-param-limit": "返回的总计页面数。",
+       "apihelp-query+iwbacklinks-param-prop": "要获取的属性:\n;iwprefix:加入跨wiki前缀。\n;iwtitle:加入跨wiki标题。",
        "apihelp-query+iwbacklinks-example-simple": "获取链接至[[wikibooks:Test]]的页面",
        "apihelp-query+iwbacklinks-example-generator": "获取有关链接至[[wikibooks:Test]]的页面的信息",
+       "apihelp-query+iwlinks-param-url": "是否获取完整URL(不能与$1prop一起使用)。",
        "apihelp-query+iwlinks-param-limit": "返回多少跨wiki链接。",
        "apihelp-query+iwlinks-param-prefix": "只返回此前缀的跨wiki链接。",
        "apihelp-query+iwlinks-param-title": "用于搜索的跨wiki链接。必须与$1prefix一起使用。",
        "apihelp-query+links-param-limit": "返回多少链接。",
        "apihelp-query+links-example-simple": "从[[首页]]获取链接",
        "apihelp-query+links-example-generator": "获取有关[[首页]]链接页面的信息",
+       "apihelp-query+links-example-namespaces": "获取用户和模板名字空间中来自[[首页]]的链接",
        "apihelp-query+linkshere-param-limit": "返回多少。",
        "apihelp-query+linkshere-example-simple": "获取链接至[[首页]]的页面列表",
        "apihelp-query+linkshere-example-generator": "获取有关链接至[[首页]]的页面的信息",
        "apihelp-query+revisions-example-last5": "获取“首页”的最近5次修订",
        "apihelp-query+revisions-example-first5": "获取“首页”的前5次修订版本",
        "apihelp-query+revisions-example-first5-after": "获取“首页”于2006年05月01日之后做出的前5次修订版本",
+       "apihelp-query+search-param-search": "搜索所有拥有此值的页面标题(或内容)。",
+       "apihelp-query+search-param-namespace": "只在这些名字空间搜索。",
        "apihelp-query+search-param-info": "要返回的元数据。",
        "apihelp-query+search-param-interwiki": "搜索结果中包含跨wiki结果,如果可用。",
        "apihelp-query+search-example-simple": "搜索“意义”",
        "apihelp-upload-param-stash": "如果设置,服务器将临时藏匿文件而不是加入存储库。",
        "apihelp-upload-param-chunk": "大块内容。",
        "apihelp-upload-example-url": "从URL上传",
+       "apihelp-userrights-description": "更改一位用户的组成员。",
        "apihelp-userrights-param-user": "用户名。",
        "apihelp-userrights-param-userid": "用户ID。",
        "apihelp-userrights-param-add": "将用户加入至这些组中。",
index 1038f71..7af61ae 100644 (file)
@@ -7,8 +7,73 @@
        },
        "apihelp-main-param-action": "要執行的動作。",
        "apihelp-main-param-format": "輸出的格式。",
+       "apihelp-block-description": "封鎖使用者。",
+       "apihelp-block-param-user": "您要封鎖的使用者名稱、IP 位址或 IP 範圍。",
+       "apihelp-block-param-reason": "封鎖原因。",
+       "apihelp-block-param-anononly": "僅封鎖匿名使用者 (禁止這個 IP 的匿名使用者編輯)。",
+       "apihelp-block-param-nocreate": "禁止建立帳號。",
+       "apihelp-block-param-autoblock": "自動封鎖最後使用的 IP 位址,以及在這之後嘗試登入的 IP 位址。",
+       "apihelp-block-param-noemail": "禁止使用者透過 Wiki 寄送電子郵件。 (需要 \"blockemail\" 權限)。",
+       "apihelp-block-param-hidename": "隱藏封鎖日誌的使用者名稱。 (需要 \"hideuser\" 權限)。",
+       "apihelp-block-param-allowusertalk": "允許使用者編輯自己的對話頁面 (依據 $wgBlockAllowsUTEdit 的設定)。",
+       "apihelp-block-param-reblock": "若使用者已被封鎖,覆寫既有的封鎖設定值。",
+       "apihelp-block-param-watchuser": "監視使用者或 IP 的使用者頁面與對話頁面。",
+       "apihelp-compare-param-fromtitle": "要比對的第一個標題。",
+       "apihelp-compare-param-fromid": "要比對的第一個頁面 ID。",
+       "apihelp-compare-param-fromrev": "要比對的第一個修訂。",
+       "apihelp-compare-param-totitle": "要比對的第二個標題。",
+       "apihelp-compare-param-toid": "要比對的第二個頁面 ID。",
+       "apihelp-compare-param-torev": "要比對的第二個修訂。",
+       "apihelp-compare-example-1": "建立修訂 1 與 1 的差異檔",
+       "apihelp-createaccount-description": "建立新使用者帳號。",
+       "apihelp-createaccount-param-name": "使用者名稱。",
+       "apihelp-createaccount-param-password": "密碼 (若有設定 $1mailpassword 則可略過)。",
+       "apihelp-createaccount-param-domain": "外部認証使用的網域 (選填)。",
+       "apihelp-createaccount-param-token": "已取得帳號建立密鑰於第一次請求。",
+       "apihelp-createaccount-param-email": "使用者的電子郵件位址 (選填)。",
+       "apihelp-createaccount-param-realname": "使用者的真實姓名 (選填)。",
+       "apihelp-createaccount-param-mailpassword": "若設為其他值,將會以電子郵件寄送隨機密碼給使用者。",
+       "apihelp-createaccount-param-reason": "建立帳號時選填的原因,會被記錄到日誌當中。",
+       "apihelp-createaccount-param-language": "要設定的使用者預設語言代碼 (選填,預設依據內容語言)。",
+       "apihelp-createaccount-example-pass": "建立使用者 \"testuser\" 使用密碼 \"test123\"",
+       "apihelp-createaccount-example-mail": "建立使用者 \"testmailuser\" 並且電子郵件通知隨機產生的密碼",
+       "apihelp-delete-description": "刪除頁面。",
+       "apihelp-delete-param-title": "您欲刪除的頁面標題。 無法與 $1pageid 同時使用。",
+       "apihelp-delete-param-pageid": "您欲刪除頁面的頁面 ID。 無法與 $1title 同時使用。",
+       "apihelp-delete-param-reason": "刪除的原因。 若未設定,將會使用自動產生的原因。",
+       "apihelp-delete-param-watch": "加入頁面至您的監視清單。",
+       "apihelp-delete-param-unwatch": "從您的監視清單中移除頁面。",
+       "apihelp-delete-example-simple": "刪除主頁面",
+       "apihelp-delete-example-reason": "刪除主頁面使用原因 \"準備移至它處\"",
+       "apihelp-disabled-description": "已停用此模組。",
+       "apihelp-edit-description": "建立與編輯頁面。",
+       "apihelp-edit-param-title": "您欲編輯的頁面標題。 無法與 $1pageid 同時使用。",
+       "apihelp-edit-param-pageid": "您欲編輯頁面的頁面 ID。 無法與 $1title 同時使用。",
+       "apihelp-edit-param-section": "章節編號。 0 代表最上層章節,\"new\" 代表新章節。",
+       "apihelp-edit-param-sectiontitle": "新章節的標題。",
+       "apihelp-edit-param-text": "頁面內容。",
+       "apihelp-edit-param-summary": "編輯摘要。 當未設定 $1section=new 與 $1sectiontitle 時也會當做章節標題。",
+       "apihelp-edit-param-createonly": "若頁面已存在,則不編輯頁面。",
+       "apihelp-edit-param-nocreate": "若頁面不存在,則產生錯誤。",
+       "apihelp-edit-param-watch": "加入頁面至您的監視清單。",
+       "apihelp-edit-param-unwatch": "從您的監視清單中移除頁面。",
+       "apihelp-edit-example-edit": "編輯頁面",
+       "apihelp-emailuser-description": "寄送電子郵件給使用者。",
+       "apihelp-emailuser-param-target": "電子郵件的收件使用者。",
+       "apihelp-emailuser-param-subject": "郵件主旨。",
+       "apihelp-emailuser-param-text": "郵件內容。",
+       "apihelp-emailuser-param-ccme": "寄送一份此郵件的複本給我。",
+       "apihelp-emailuser-example-email": "寄送電子郵件給使用者 \"WikiSysop\" 使用內容 \"Content\"",
+       "apihelp-expandtemplates-description": "展開所有於 wikitext 中的樣板。",
+       "apihelp-expandtemplates-param-title": "頁面標題。",
+       "apihelp-expandtemplates-param-text": "要轉換的 Wikitext。",
        "apihelp-login-param-name": "使用者名稱。",
+       "apihelp-login-example-login": "登入",
+       "apihelp-move-description": "移動頁面。",
+       "apihelp-opensearch-param-search": "搜尋字串。",
+       "apihelp-options-example-reset": "重設所有偏好設定",
        "apihelp-userrights-param-user": "使用者名稱。",
+       "apihelp-userrights-param-userid": "使用者 ID。",
        "apihelp-format-example-generic": "格式化查詢結果為 $1 格式",
        "apihelp-dbg-description": "使用 PHP 的 var_export() 格式輸出資料。",
        "apihelp-dbgfm-description": "使用 PHP 的 var_export() 格式輸出資料 (使用 HTML 格式顯示)。",
index ae27fba..2a3cd38 100644 (file)
@@ -20,6 +20,9 @@
  * @file
  */
 
+use Cdb\Exception as CdbException;
+use Cdb\Reader as CdbReader;
+use Cdb\Writer as CdbWriter;
 /**
  * Class for caching the contents of localisation files, Messages*.php
  * and *.i18n.php.
index a15b461..627f4f0 100644 (file)
@@ -102,7 +102,7 @@ abstract class BloomCache {
                                $status = $this->getStatus( $virtualKey );
                                if ( $status == false ) {
                                        wfDebug( "Could not query virtual bloom filter '$virtualKey'." );
-                                       return null;
+                                       return true;
                                }
 
                                $useFilter = call_user_func_array(
index 18cd39f..fc13eeb 100644 (file)
@@ -798,6 +798,23 @@ abstract class DatabaseBase implements IDatabase {
                $this->mPHPError = $errstr;
        }
 
+       /**
+        * Create a log context to pass to wfLogDBError or other logging functions.
+        *
+        * @param array $extras Additional data to add to context
+        * @return array
+        */
+       protected function getLogContext( array $extras = array() ) {
+               return array_merge(
+                       array(
+                               'db_server' => $this->mServer,
+                               'db_name' => $this->mDBname,
+                               'db_user' => $this->mUser,
+                       ),
+                       $extras
+               );
+       }
+
        /**
         * Closes a database connection.
         * if it is open : commits any open transactions
@@ -945,7 +962,8 @@ abstract class DatabaseBase implements IDatabase {
                $totalProf = '';
                $isMaster = !is_null( $this->getLBInfo( 'master' ) );
 
-               if ( !Profiler::instance()->isStub() ) {
+               $profiler = Profiler::instance();
+               if ( !$profiler->isStub() ) {
                        # generalizeSQL will probably cut down the query to reasonable
                        # logging size most of the time. The substr is really just a sanity check.
                        if ( $isMaster ) {
@@ -958,9 +976,8 @@ abstract class DatabaseBase implements IDatabase {
                        # Include query transaction state
                        $queryProf .= $this->mTrxShortId ? " [TRX#{$this->mTrxShortId}]" : "";
 
-                       $trx = $this->mTrxLevel ? 'TRX=yes' : 'TRX=no';
-                       wfProfileIn( $totalProf );
-                       wfProfileIn( $queryProf );
+                       $totalProfSection = $profiler->scopedProfileIn( $totalProf );
+                       $queryProfSection = $profiler->scopedProfileIn( $queryProf );
                }
 
                if ( $this->debug() ) {
@@ -983,12 +1000,15 @@ abstract class DatabaseBase implements IDatabase {
                }
 
                # Log the query time and feed it into the DB trx profiler
-               $queryStartTime = microtime( true );
-               $queryProfile = new ScopedCallback( function() use ( $queryStartTime, $queryProf ) {
-                       $elapsed = microtime( true ) - $queryStartTime;
-                       $trxProfiler = Profiler::instance()->getTransactionProfiler();
-                       $trxProfiler->recordFunctionCompletion( $queryProf, $elapsed );
-               } );
+               if ( $queryProf != '' ) {
+                       $queryStartTime = microtime( true );
+                       $queryProfile = new ScopedCallback(
+                               function() use ( $queryStartTime, $queryProf, $isMaster ) {
+                                       $trxProfiler = Profiler::instance()->getTransactionProfiler();
+                                       $trxProfiler->recordQueryCompletion( $queryProf, $queryStartTime, $isMaster );
+                               }
+                       );
+               }
 
                # Do the query and handle errors
                $ret = $this->doQuery( $commentedSql );
@@ -1015,7 +1035,13 @@ abstract class DatabaseBase implements IDatabase {
                                $elapsed = round( microtime( true ) - $wgRequestTime, 3 );
                                if ( $elapsed < 300 ) {
                                        # Not a database error to lose a transaction after a minute or two
-                                       wfLogDBError( "Connection lost and reconnected after {$elapsed}s, query: $sqlx" );
+                                       wfLogDBError(
+                                               "Connection lost and reconnected after {$elapsed}s, query: $sqlx",
+                                               $this->getLogContext( array(
+                                                       'method' => __METHOD__,
+                                                       'query' => $sqlx,
+                                               ) )
+                                       );
                                }
                                if ( $hadTrx ) {
                                        # Leave $ret as false and let an error be reported.
@@ -1034,11 +1060,6 @@ abstract class DatabaseBase implements IDatabase {
                        $this->reportQueryError( $this->lastError(), $this->lastErrno(), $sql, $fname, $tempIgnore );
                }
 
-               if ( !Profiler::instance()->isStub() ) {
-                       wfProfileOut( $queryProf );
-                       wfProfileOut( $totalProf );
-               }
-
                return $this->resultObject( $ret );
        }
 
@@ -1063,7 +1084,16 @@ abstract class DatabaseBase implements IDatabase {
                        $this->ignoreErrors( $ignore );
                } else {
                        $sql1line = mb_substr( str_replace( "\n", "\\n", $sql ), 0, 5 * 1024 );
-                       wfLogDBError( "$fname\t{$this->mServer}\t$errno\t$error\t$sql1line" );
+                       wfLogDBError(
+                               "{fname}\t{db_server}\t{errno}\t{error}\t{sql1line}",
+                               $this->getLogContext( array(
+                                       'method' => __METHOD__,
+                                       'errno' => $errno,
+                                       'error' => $error,
+                                       'sql1line' => $sql1line,
+                                       'fname' => $fname,
+                               ) )
+                       );
                        wfDebug( "SQL ERROR: " . $error . "\n" );
                        throw new DBQueryError( $this, $error, $errno, $sql, $fname );
                }
@@ -3338,7 +3368,12 @@ abstract class DatabaseBase implements IDatabase {
                                $msg = "$fname: Transaction already in progress (from {$this->mTrxFname}), " .
                                        " performing implicit commit!";
                                wfWarn( $msg );
-                               wfLogDBError( $msg );
+                               wfLogDBError( $msg,
+                                       $this->getLogContext( array(
+                                               'method' => __METHOD__,
+                                               'fname' => $fname,
+                                       ) )
+                               );
                        } else {
                                // if the transaction was automatic and has done write operations,
                                // log it if $wgDebugDBTransactions is enabled.
index 2008f4d..7dfae63 100644 (file)
@@ -96,7 +96,13 @@ abstract class DatabaseMysqlBase extends DatabaseBase {
                        if ( !$error ) {
                                $error = $this->lastError();
                        }
-                       wfLogDBError( "Error connecting to {$this->mServer}: $error" );
+                       wfLogDBError(
+                               "Error connecting to {db_server}: {error}",
+                               $this->getLogContext( array(
+                                       'method' => __METHOD__,
+                                       'error' => $error,
+                               ) )
+                       );
                        wfDebug( "DB connection error\n" .
                                "Server: $server, User: $user, Password: " .
                                substr( $password, 0, 3 ) . "..., error: " . $error . "\n" );
@@ -111,7 +117,12 @@ abstract class DatabaseMysqlBase extends DatabaseBase {
                        $success = $this->selectDB( $dbName );
                        wfRestoreWarnings();
                        if ( !$success ) {
-                               wfLogDBError( "Error selecting database $dbName on server {$this->mServer}" );
+                               wfLogDBError(
+                                       "Error selecting database {db_name} on server {db_server}",
+                                       $this->getLogContext( array(
+                                               'method' => __METHOD__,
+                                       ) )
+                               );
                                wfDebug( "Error selecting database $dbName on server {$this->mServer} " .
                                        "from client host " . wfHostname() . "\n" );
 
@@ -132,7 +143,12 @@ abstract class DatabaseMysqlBase extends DatabaseBase {
                        // Use doQuery() to avoid opening implicit transactions (DBO_TRX)
                        $success = $this->doQuery( "SET sql_mode = $mode", __METHOD__ );
                        if ( !$success ) {
-                               wfLogDBError( "Error setting sql_mode to $mode on server {$this->mServer}" );
+                               wfLogDBError(
+                                       "Error setting sql_mode to $mode on server {db_server}",
+                                       $this->getLogContext( array(
+                                               'method' => __METHOD__,
+                                       ) )
+                               );
                                wfProfileOut( __METHOD__ );
                                $this->reportConnectionError( "Error setting sql_mode to $mode" );
                        }
index 73456e2..2f93ce7 100644 (file)
@@ -109,7 +109,7 @@ abstract class LBFactory {
         * Construct a factory based on a configuration array (typically from $wgLBFactoryConf)
         * @param array $conf
         */
-       abstract function __construct( $conf );
+       abstract function __construct( array $conf );
 
        /**
         * Create a new load balancer object. The resulting object will be untracked,
@@ -156,7 +156,7 @@ abstract class LBFactory {
         * @param callable $callback
         * @param array $params
         */
-       abstract function forEachLB( $callback, $params = array() );
+       abstract function forEachLB( $callback, array $params = array() );
 
        /**
         * Prepare all tracked load balancers for shutdown
@@ -171,7 +171,7 @@ abstract class LBFactory {
         * @param string $methodName
         * @param array $args
         */
-       function forEachLBCallMethod( $methodName, $args = array() ) {
+       function forEachLBCallMethod( $methodName, array $args = array() ) {
                $this->forEachLB( array( $this, 'callMethod' ), array( $methodName, $args ) );
        }
 
@@ -227,7 +227,7 @@ class LBFactorySimple extends LBFactory {
        /** @var ChronologyProtector */
        protected $chronProt;
 
-       function __construct( $conf ) {
+       function __construct( array $conf ) {
                $this->chronProt = new ChronologyProtector;
        }
 
@@ -324,7 +324,7 @@ class LBFactorySimple extends LBFactory {
         * @param callable $callback
         * @param array $params
         */
-       function forEachLB( $callback, $params = array() ) {
+       function forEachLB( $callback, array $params = array() ) {
                if ( isset( $this->mainLB ) ) {
                        call_user_func_array( $callback, array_merge( array( $this->mainLB ), $params ) );
                }
@@ -352,7 +352,7 @@ class LBFactorySimple extends LBFactory {
  * LBFactory::enableBackend() to return to normal behavior
  */
 class LBFactoryFake extends LBFactory {
-       function __construct( $conf ) {
+       function __construct( array $conf ) {
        }
 
        function newMainLB( $wiki = false ) {
@@ -371,7 +371,7 @@ class LBFactoryFake extends LBFactory {
                throw new DBAccessError;
        }
 
-       function forEachLB( $callback, $params = array() ) {
+       function forEachLB( $callback, array $params = array() ) {
        }
 }
 
index bac9652..7100615 100644 (file)
@@ -152,7 +152,7 @@ class LBFactoryMulti extends LBFactory {
         * @param array $conf
         * @throws MWException
         */
-       function __construct( $conf ) {
+       function __construct( array $conf ) {
                $this->chronProt = new ChronologyProtector;
                $this->conf = $conf;
                $required = array( 'sectionsByDB', 'sectionLoads', 'serverTemplate' );
@@ -377,7 +377,7 @@ class LBFactoryMulti extends LBFactory {
         * @param callable $callback
         * @param array $params
         */
-       function forEachLB( $callback, $params = array() ) {
+       function forEachLB( $callback, array $params = array() ) {
                foreach ( $this->mainLBs as $lb ) {
                        call_user_func_array( $callback, array_merge( array( $lb ), $params ) );
                }
index 3a4d829..03b7fbe 100644 (file)
@@ -32,7 +32,7 @@ class LBFactorySingle extends LBFactory {
         * @param array $conf An associative array with one member:
         *  - connection: The DatabaseBase connection object
         */
-       function __construct( $conf ) {
+       function __construct( array $conf ) {
                $this->lb = new LoadBalancerSingle( $conf );
        }
 
@@ -74,7 +74,7 @@ class LBFactorySingle extends LBFactory {
         * @param string|callable $callback
         * @param array $params
         */
-       function forEachLB( $callback, $params = array() ) {
+       function forEachLB( $callback, array $params = array() ) {
                call_user_func_array( $callback, array_merge( array( $this->lb ), $params ) );
        }
 }
@@ -89,7 +89,7 @@ class LoadBalancerSingle extends LoadBalancer {
        /**
         * @param array $params
         */
-       function __construct( $params ) {
+       function __construct( array $params ) {
                $this->db = $params['connection'];
                parent::__construct( array( 'servers' => array( array(
                        'type' => $this->db->getType(),
index 0fb2d09..4e11af2 100644 (file)
  * @ingroup Database
  */
 class LoadBalancer {
-       /** @var array Map of (server index => server config array) */
+       /** @var array[] Map of (server index => server config array) */
        private $mServers;
-       /** @var array Map of (local/foreignUsed/foreignFree => server index => DatabaseBase array) */
+       /** @var array[] Map of (local/foreignUsed/foreignFree => server index => DatabaseBase array) */
        private $mConns;
        /** @var array Map of (server index => weight) */
        private $mLoads;
-       /** @var array Map of (group => server index => weight) */
+       /** @var array[] Map of (group => server index => weight) */
        private $mGroupLoads;
        /** @var bool Whether to disregard slave lag as a factor in slave selection */
        private $mAllowLagged;
@@ -67,7 +67,7 @@ class LoadBalancer {
         *   loadMonitor       Name of a class used to fetch server lag and load.
         * @throws MWException
         */
-       function __construct( $params ) {
+       function __construct( array $params ) {
                if ( !isset( $params['servers'] ) ) {
                        throw new MWException( __CLASS__ . ': missing servers parameter' );
                }
@@ -144,7 +144,7 @@ class LoadBalancer {
         * @param array $weights
         * @return bool|int|string
         */
-       function pickRandom( $weights ) {
+       function pickRandom( array $weights ) {
                return ArrayUtils::pickRandom( $weights );
        }
 
@@ -153,7 +153,7 @@ class LoadBalancer {
         * @param bool|string $wiki Wiki to get non-lagged for
         * @return bool|int|string
         */
-       function getRandomNonLagged( $loads, $wiki = false ) {
+       function getRandomNonLagged( array $loads, $wiki = false ) {
                # Unset excessively lagged servers
                $lags = $this->getLagTimes( $wiki );
                foreach ( $lags as $i => $lag ) {
@@ -197,8 +197,8 @@ class LoadBalancer {
         * always return a consistent index during a given invocation
         *
         * Side effect: opens connections to databases
-        * @param bool|string $group
-        * @param bool|string $wiki
+        * @param string|bool $group Query group, or false for the generic reader
+        * @param string|bool $wiki Wiki ID, or false for the current wiki
         * @throws MWException
         * @return bool|int|string
         */
@@ -218,7 +218,7 @@ class LoadBalancer {
                        return $this->mReadIndex;
                }
 
-               $section = new ProfileSection( __METHOD__ );
+               new ProfileSection( __METHOD__ );
 
                # Find the relevant load array
                if ( $group !== false ) {
@@ -410,7 +410,7 @@ class LoadBalancer {
 
                if ( $result == -1 || is_null( $result ) ) {
                        # Timed out waiting for slave, use master instead
-                       $server = $this->mServers[$index];
+                       $server = $this->mServers[$index]['host'];
                        $msg = __METHOD__ . ": Timed out waiting on $server pos {$this->mWaitForPos}";
                        wfDebug( "$msg\n" );
                        wfDebugLog( 'DBPerformance', "$msg:\n" . wfBacktrace( true ) );
@@ -432,8 +432,8 @@ class LoadBalancer {
         * This is the main entry point for this class.
         *
         * @param int $i Server index
-        * @param array $groups Query groups
-        * @param bool|string $wiki Wiki ID
+        * @param array|string|bool $groups Query group(s), or false for the generic reader
+        * @param string|bool $wiki Wiki ID, or false for the current wiki
         *
         * @throws MWException
         * @return DatabaseBase
@@ -556,8 +556,8 @@ class LoadBalancer {
         * @see LoadBalancer::getConnection() for parameter information
         *
         * @param int $db
-        * @param mixed $groups
-        * @param bool|string $wiki
+        * @param array|string|bool $groups Query group(s), or false for the generic reader
+        * @param string|bool $wiki Wiki ID, or false for the current wiki
         * @return DBConnRef
         */
        public function getConnectionRef( $db, $groups = array(), $wiki = false ) {
@@ -572,8 +572,8 @@ class LoadBalancer {
         * @see LoadBalancer::getConnection() for parameter information
         *
         * @param int $db
-        * @param mixed $groups
-        * @param bool|string $wiki
+        * @param array|string|bool $groups Query group(s), or false for the generic reader
+        * @param string|bool $wiki Wiki ID, or false for the current wiki
         * @return DBConnRef
         */
        public function getLazyConnectionRef( $db, $groups = array(), $wiki = false ) {
@@ -589,7 +589,7 @@ class LoadBalancer {
         * error will be available via $this->mErrorConnection.
         *
         * @param int $i Server index
-        * @param bool|string $wiki Wiki ID to open
+        * @param string|bool $wiki Wiki ID, or false for the current wiki
         * @return DatabaseBase
         *
         * @access private
@@ -760,17 +760,27 @@ class LoadBalancer {
         */
        private function reportConnectionError() {
                $conn = $this->mErrorConnection; // The connection which caused the error
+               $context = array(
+                       'method' => __METHOD__,
+                       'last_error' => $this->mLastError,
+               );
 
                if ( !is_object( $conn ) ) {
                        // No last connection, probably due to all servers being too busy
-                       wfLogDBError( "LB failure with no last connection. Connection error: {$this->mLastError}" );
+                       wfLogDBError(
+                               "LB failure with no last connection. Connection error: {last_error}",
+                               $context
+                       );
 
                        // If all servers were busy, mLastError will contain something sensible
                        throw new DBConnectionError( null, $this->mLastError );
                } else {
-                       $server = $conn->getProperty( 'mServer' );
-                       wfLogDBError( "Connection error: {$this->mLastError} ({$server})" );
-                       $conn->reportConnectionError( "{$this->mLastError} ({$server})" ); // throws DBConnectionError
+                       $context['db_server'] = $conn->getProperty( 'mServer' );
+                       wfLogDBError(
+                               "Connection error: {last_error} ({db_server})",
+                               $context
+                       );
+                       $conn->reportConnectionError( "{$this->mLastError} ({$context['db_server']})" ); // throws DBConnectionError
                }
 
                return false; /* not reached */
@@ -847,7 +857,7 @@ class LoadBalancer {
         * @param int $i
         * @param array $serverInfo
         */
-       function setServerInfo( $i, $serverInfo ) {
+       function setServerInfo( $i, array $serverInfo ) {
                $this->mServers[$i] = $serverInfo;
        }
 
@@ -1061,7 +1071,7 @@ class LoadBalancer {
         * @param callable $callback
         * @param array $params
         */
-       function forEachOpenConnection( $callback, $params = array() ) {
+       function forEachOpenConnection( $callback, array $params = array() ) {
                foreach ( $this->mConns as $conns2 ) {
                        foreach ( $conns2 as $conns3 ) {
                                foreach ( $conns3 as $conn ) {
@@ -1109,7 +1119,7 @@ class LoadBalancer {
         * Results are cached for a short time in memcached/process cache
         *
         * @param string|bool $wiki
-        * @return array Map of (server index => seconds)
+        * @return int[] Map of (server index => seconds)
         */
        function getLagTimes( $wiki = false ) {
                if ( $this->getServerCount() <= 1 ) {
index ffc6b3b..c4c6cf3 100644 (file)
@@ -26,8 +26,6 @@
  * By default, most methods do nothing ( self::$enabled = false ). You have
  * to explicitly call MWDebug::init() to enabled them.
  *
- * @todo Profiler support
- *
  * @since 1.19
  */
 class MWDebug {
@@ -534,7 +532,6 @@ class MWDebug {
                $result->setIndexedTagName( $debugInfo['debugLog'], 'msg' );
                $result->setIndexedTagName( $debugInfo['queries'], 'query' );
                $result->setIndexedTagName( $debugInfo['includes'], 'queries' );
-               $result->setIndexedTagName( $debugInfo['profile'], 'function' );
                $result->addValue( null, 'debuginfo', $debugInfo );
        }
 
@@ -578,7 +575,6 @@ class MWDebug {
                        'memory' => $context->getLanguage()->formatSize( memory_get_usage( $realMemoryUsage ) ),
                        'memoryPeak' => $context->getLanguage()->formatSize( memory_get_peak_usage( $realMemoryUsage ) ),
                        'includes' => self::getFilesIncluded( $context ),
-                       'profile' => Profiler::instance()->getRawData(),
                );
        }
 }
index daf3f51..c67bd7b 100644 (file)
@@ -77,7 +77,7 @@ class MWLoggerLegacyLogger extends \Psr\Log\AbstractLogger {
         * @return bool True if message should be sent to disk/network, false
         * otherwise
         */
-       protected static function shouldEmit( $channel, $message, $context ) {
+       public static function shouldEmit( $channel, $message, $context ) {
                global $wgDebugLogFile, $wgDBerrorLog, $wgDebugLogGroups;
 
                if ( $channel === 'wfLogDBError' ) {
@@ -102,10 +102,10 @@ class MWLoggerLegacyLogger extends \Psr\Log\AbstractLogger {
                        }
 
                } elseif ( isset( $context['private'] ) && $context['private'] ) {
-                       // Don't emit if the message didn't match previous checks based on the
-                       // channel and the event is marked as private. This check discards
-                       // messages sent via wfDebugLog() with dest == 'private' and no explicit
-                       // wgDebugLogGroups configuration.
+                       // Don't emit if the message didn't match previous checks based on
+                       // the channel and the event is marked as private. This check
+                       // discards messages sent via wfDebugLog() with dest == 'private'
+                       // and no explicit wgDebugLogGroups configuration.
                        $shouldEmit = false;
                } else {
                        // Default return value is the the same as the historic wfDebug
@@ -130,7 +130,7 @@ class MWLoggerLegacyLogger extends \Psr\Log\AbstractLogger {
         * @param array $context
         * @return string
         */
-       protected static function format( $channel, $message, $context ) {
+       public static function format( $channel, $message, $context ) {
                global $wgDebugLogGroups;
 
                if ( $channel === 'wfDebug' ) {
@@ -178,7 +178,8 @@ class MWLoggerLegacyLogger extends \Psr\Log\AbstractLogger {
                        // Default formatting is wfDebugLog's historic style
                        $text = self::formatAsWfDebugLog( $channel, $message, $context );
                }
-               return $text;
+
+               return self::interpolate( $text, $context );
        }
 
 
@@ -192,6 +193,11 @@ class MWLoggerLegacyLogger extends \Psr\Log\AbstractLogger {
         */
        protected static function formatAsWfDebug( $channel, $message, $context ) {
                $text = preg_replace( '![\x00-\x08\x0b\x0c\x0e-\x1f]!', ' ', $message );
+               if ( isset( $context['seconds_elapsed'] ) ) {
+                       // Prepend elapsed request time and real memory usage with two
+                       // trailing spaces.
+                       $text = "{$context['seconds_elapsed']} {$context['memory_used']}  {$text}";
+               }
                if ( isset( $context['prefix'] ) ) {
                        $text = "{$context['prefix']}{$text}";
                }
@@ -248,6 +254,24 @@ class MWLoggerLegacyLogger extends \Psr\Log\AbstractLogger {
        }
 
 
+       /**
+        * Interpolate placeholders in logging message.
+        *
+        * @param string $message
+        * @param array $context
+        */
+       public static function interpolate( $message, array $context ) {
+               if ( strpos( $message, '{' ) !== false ) {
+                       $replace = array();
+                       foreach ( $context as $key => $val ) {
+                               $replace['{' . $key . '}'] = $val;
+                       }
+                       $message = strtr( $message, $replace );
+               }
+               return $message;
+       }
+
+
        /**
         * Select the appropriate log output destination for the given log event.
         *
index 02ab309..b2e3012 100644 (file)
@@ -48,6 +48,12 @@ class MWLoggerMonologHandler extends \Monolog\Handler\AbstractProcessingHandler
         */
        protected $uri;
 
+       /**
+        * Filter log events using legacy rules
+        * @var bool $useLegacyFilter
+        */
+       protected $useLegacyFilter;
+
        /**
         * Log sink
         * @var resource $sink
@@ -77,16 +83,30 @@ class MWLoggerMonologHandler extends \Monolog\Handler\AbstractProcessingHandler
 
        /**
         * @param string $stream Stream URI
+        * @param bool $useLegacyFilter Filter log events using legacy rules
         * @param int $level Minimum logging level that will trigger handler
         * @param bool $bubble Can handled meesages bubble up the handler stack?
         */
        public function __construct(
-               $stream, $level = \Monolog\Logger::DEBUG, $bubble = true
+               $stream,
+               $useLegacyFilter = false,
+               $level = \Monolog\Logger::DEBUG,
+               $bubble = true
        ) {
                parent::__construct( $level, $bubble );
                $this->uri = $stream;
+               $this->useLegacyFilter = $useLegacyFilter;
        }
 
+       public function isHandling( array $record ) {
+               $levelOk = parent::isHandling( $record );
+               if ( $levelOk && $this->useLegacyFilter ) {
+                       return MWLoggerLegacyLogger::shouldEmit(
+                               $record['channel'], $record['message'], $record
+                       );
+               }
+               return $levelOk;
+       }
 
        /**
         * Open the log sink described by our stream URI.
diff --git a/includes/debug/logger/monolog/LegacyFormatter.php b/includes/debug/logger/monolog/LegacyFormatter.php
new file mode 100644 (file)
index 0000000..11dbc82
--- /dev/null
@@ -0,0 +1,45 @@
+<?php
+/**
+ * @section LICENSE
+ * 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
+ */
+
+
+/**
+ * Log message formatter that mimics the legacy log message formatting of
+ * `wfDebug`, `wfDebugLog`, `wfLogDBError` and `wfErrorLog` global functions by
+ * deligating the formatting to MWLoggerLegacyLogger.
+ *
+ * @since 1.25
+ * @author Bryan Davis <bd808@wikimedia.org>
+ * @copyright © 2013 Bryan Davis and Wikimedia Foundation.
+ * @see MWLoggerLegacyLogger
+ */
+class MWLoggerMonologLegacyFormatter extends \Monolog\Formatter\NormalizerFormatter {
+
+       public function __construct() {
+               parent::__construct( 'c' );
+       }
+
+       public function format( array $record ) {
+               $normalized = parent::format( $record );
+               return MWLoggerLegacyLogger::format(
+                       $normalized['channel'], $normalized['message'], $normalized
+               );
+       }
+}
index 0d90e66..9db04cb 100644 (file)
@@ -167,7 +167,39 @@ class MWExceptionHandler {
         * @param int $line
         */
        public static function handleError( $level, $message, $file = null, $line = null ) {
-               $e = new ErrorException( $message, 0, $level, $file, $line );
+               // Map error constant to error name (reverse-engineer PHP error reporting)
+               switch ( $level ) {
+                       case E_ERROR:
+                       case E_CORE_ERROR:
+                       case E_COMPILE_ERROR:
+                       case E_USER_ERROR:
+                       case E_RECOVERABLE_ERROR:
+                       case E_PARSE:
+                               $levelName = 'Error';
+                               break;
+                       case E_WARNING:
+                       case E_CORE_WARNING:
+                       case E_COMPILE_WARNING:
+                       case E_USER_WARNING:
+                               $levelName = 'Warning';
+                               break;
+                       case E_NOTICE:
+                       case E_USER_NOTICE:
+                               $levelName = 'Notice';
+                               break;
+                       case E_STRICT:
+                               $levelName = 'Strict Standards';
+                               break;
+                       case E_DEPRECATED:
+                       case E_USER_DEPRECATED:
+                               $levelName = 'Deprecated';
+                               break;
+                       default:
+                               $levelName = 'Unknown error';
+                               break;
+               }
+
+               $e = new ErrorException( "PHP $levelName: $message", 0, $level, $file, $line );
                self::logError( $e );
 
                // This handler is for logging only. Return false will instruct PHP
index bfffcc0..8b6eaca 100644 (file)
@@ -314,6 +314,8 @@ class FileBackendMultiWrite extends FileBackend {
                        $mStat = $mBackend->getFileStat( array( 'src' => $mPath, 'latest' => true ) );
                        if ( $mStat === null || ( $mSha1 !== false && !$mStat ) ) { // sanity
                                $status->fatal( 'backend-fail-internal', $this->name );
+                               wfDebugLog( 'FileOperation', __METHOD__
+                                       . ': File is not available on the master backend' );
                                continue; // file is not available on the master backend...
                        }
                        // Check of all clone backends agree with the master...
@@ -326,6 +328,8 @@ class FileBackendMultiWrite extends FileBackend {
                                $cStat = $cBackend->getFileStat( array( 'src' => $cPath, 'latest' => true ) );
                                if ( $cStat === null || ( $cSha1 !== false && !$cStat ) ) { // sanity
                                        $status->fatal( 'backend-fail-internal', $cBackend->getName() );
+                                       wfDebugLog( 'FileOperation', __METHOD__
+                                       . ': File is not available on the clone backend' );
                                        continue; // file is not available on the clone backend...
                                }
                                if ( $mSha1 === $cSha1 ) {
index 625b9b4..7234474 100644 (file)
@@ -537,6 +537,7 @@ class SwiftFileBackend extends FileBackendStore {
                        return $status; // already there
                } elseif ( $stat === null ) {
                        $status->fatal( 'backend-fail-internal', $this->name );
+                       wfDebugLog( 'SwiftBackend', __METHOD__ . ': cannot get container stat' );
 
                        return $status;
                }
@@ -568,6 +569,7 @@ class SwiftFileBackend extends FileBackendStore {
                        $status->fatal( 'backend-fail-usable', $params['dir'] );
                } else {
                        $status->fatal( 'backend-fail-internal', $this->name );
+                       wfDebugLog( 'SwiftBackend', __METHOD__ . ': cannot get container stat' );
                }
 
                return $status;
@@ -588,6 +590,7 @@ class SwiftFileBackend extends FileBackendStore {
                        $status->fatal( 'backend-fail-usable', $params['dir'] );
                } else {
                        $status->fatal( 'backend-fail-internal', $this->name );
+                       wfDebugLog( 'SwiftBackend', __METHOD__ . ': cannot get container stat' );
                }
 
                return $status;
@@ -607,6 +610,7 @@ class SwiftFileBackend extends FileBackendStore {
                        return $status; // ok, nothing to do
                } elseif ( !is_array( $stat ) ) {
                        $status->fatal( 'backend-fail-internal', $this->name );
+                       wfDebugLog( 'SwiftBackend', __METHOD__ . ': cannot get container stat' );
 
                        return $status;
                }
@@ -1253,6 +1257,7 @@ class SwiftFileBackend extends FileBackendStore {
 
                if ( $rcode != 204 && $rcode !== 202 ) {
                        $status->fatal( 'backend-fail-internal', $this->name );
+                       wfDebugLog( 'SwiftBackend', __METHOD__ . ': unexpected rcode value (' . $rcode . ')' );
                }
 
                return $status;
index 28876e2..d0ee13b 100644 (file)
@@ -17,8 +17,7 @@ class HTMLIntField extends HTMLFloatField {
                # phone numbers when you know that they are integers (the HTML5 type=tel
                # input does not require its value to be numeric).  If you want a tidier
                # value to, eg, save in the DB, clean it up with intval().
-               if ( !preg_match( '/^((\+|\-)?\d+)?$/', trim( $value ) )
-               ) {
+               if ( !preg_match( '/^((\+|\-)?\d+)?$/', trim( $value ) ) ) {
                        return $this->msg( 'htmlform-int-invalid' )->parseAsBlock();
                }
 
index 1175bd3..a1c0c95 100644 (file)
@@ -13,6 +13,7 @@
 class HTMLSelectAndOtherField extends HTMLSelectField {
        function __construct( $params ) {
                if ( array_key_exists( 'other', $params ) ) {
+                       // Do nothing
                } elseif ( array_key_exists( 'other-message', $params ) ) {
                        $params['other'] = wfMessage( $params['other-message'] )->plain();
                } else {
@@ -22,7 +23,7 @@ class HTMLSelectAndOtherField extends HTMLSelectField {
                parent::__construct( $params );
 
                if ( $this->getOptions() === null ) {
-                       # Sulk
+                       // Sulk
                        throw new MWException( 'HTMLSelectAndOtherField called without any options' );
                }
                if ( !in_array( 'other', $this->mOptions, true ) ) {
index 3094d55..22fb1df 100644 (file)
@@ -227,7 +227,7 @@ class WebInstallerOutput {
        public function getHeadAttribs() {
                return array(
                        'dir' => $this->getDir(),
-                       'lang' => $this->getLanguageCode(),
+                       'lang' => wfBCP47( $this->getLanguageCode() ),
                );
        }
 
index b2b0a69..9ecb24b 100644 (file)
@@ -1287,8 +1287,7 @@ class WebInstallerOptions extends WebInstallerPage {
 
                $retVal = true;
 
-               if ( !array_key_exists( $this->getVar( '_RightsProfile' ), $this->parent->rightsProfiles )
-               ) {
+               if ( !array_key_exists( $this->getVar( '_RightsProfile' ), $this->parent->rightsProfiles ) ) {
                        reset( $this->parent->rightsProfiles );
                        $this->setVar( '_RightsProfile', key( $this->parent->rightsProfiles ) );
                }
@@ -1464,7 +1463,7 @@ class WebInstallerComplete extends WebInstallerPage {
                        strpos( $_SERVER['HTTP_USER_AGENT'], 'MSIE' ) !== false
                ) {
                        // JS appears to be the only method that works consistently with IE7+
-                       $this->addHtml( "\n<script>jQuery( function () { document.location = " .
+                       $this->addHtml( "\n<script>jQuery( function () { location.href = " .
                                Xml::encodeJsVar( $lsUrl ) . "; } );</script>\n" );
                } else {
                        $this->parent->request->response()->header( "Refresh: 0;url=$lsUrl" );
index 7edb97c..36136a5 100644 (file)
        "config-restart": "نعم، إعادة التشغيل",
        "config-env-php": "بي إتش بي $1 مثبت.",
        "config-db-type": "نوع قاعدة البيانات:",
+       "config-db-host": "مضيف قاعدة البيانات:",
        "config-db-wiki-settings": "حدِّد هذا الويكي",
        "config-db-name": "اسم قاعدة البيانات",
+       "config-db-name-oracle": "سكيما قاعدة البيانات:",
        "config-db-username": "اسم مستخدم قاعدة البيانات:",
        "config-db-password": "كلمة سر قاعدة البيانات:",
+       "config-db-prefix": "بادئة جدول قاعدة البيانات:",
        "config-db-port": "منفذ قاعدة البيانات:",
        "config-db-schema": "سكيما لميدياويكي",
-       "config-type-mysql": "ماي إس كيو إل",
+       "config-type-mysql": "MySQL (أو متوافق)",
        "config-type-postgres": "بوستجر إس كيو إل",
        "config-type-sqlite": "إس كيو لايت",
        "config-type-oracle": "أوراكل",
+       "config-type-mssql": "خادم SQL لميكروسوفت",
        "config-header-mysql": "إعدادات MySQL",
        "config-header-postgres": "إعدادات PostgreSQL",
        "config-header-sqlite": "إعدادات SQLite",
        "config-install-extensions": "متضمنا الامتدادات",
        "config-install-database": "إنشاء قاعدة البيانات",
        "config-install-schema": "إنشاء السكيما",
+       "config-install-pg-commit": "تنفيذ التغييرات",
        "config-install-user": "إنشاء مستخدم قاعدة البيانات",
        "config-install-user-alreadyexists": "المستخدم \"$1\" موجود بالفعل",
        "config-install-user-create-failed": "إنشاء مستخدم \"$1\" فشل:$2",
        "config-install-tables": "إنشاء الجداول",
        "config-install-keys": "توليد المفاتيح السرية",
        "config-help": "مساعدة",
+       "config-help-tooltip": "اضغط للتوسيع",
        "mainpagetext": "'''تم تثبيت ميدياويكي بنجاح.'''",
        "mainpagedocfooter": "استشر [//meta.wikimedia.org/wiki/Help:Contents دليل المستخدم] لمعلومات حول استخدام برنامج الويكي.\n\n== البداية ==\n\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings قائمة إعدادات الضبط]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ أسئلة متكررة حول ميدياويكي]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce القائمة البريدية الخاصة بإصدار ميدياويكي]"
 }
index cca993a..e3838e3 100644 (file)
@@ -2,9 +2,28 @@
        "@metadata": {
                "authors": [
                        "Haqmar",
-                       "Seb35"
+                       "Seb35",
+                       "Рустам Нурыев"
                ]
        },
+       "config-desc": "MediaWiki йөкләүсе",
+       "config-title": "MediaWiki $1 йөкләмеше",
+       "config-information": "Мәғлүмәт",
+       "config-localsettings-key": "Яңыртыу асҡысы:",
+       "config-localsettings-badkey": "Дөрөҫ булмаған асҡыс күрһәттегеҙ",
+       "config-your-language": "Һеҙҙең тел:",
+       "config-back": "← Кире",
+       "config-continue": "Дауам итергә →",
+       "config-page-language": "Тел",
+       "config-page-welcome": "MediaWiki-ға рәхим итегеҙ!",
+       "config-page-name": "Исем",
+       "config-page-options": "Көйләүҙәр",
+       "config-page-complete": "Тамам!",
+       "config-page-readme": "Мине уҡы",
+       "config-page-releasenotes": "Өлгө тураһында мәғлүмәт",
+       "config-page-copying": "Рөхсәтнәмә",
+       "config-page-upgradedoc": "Яңыртыу",
+       "config-restart": "Эйе, яңынан башларға",
        "mainpagetext": "«MediaWiki» уңышлы рәүештә ҡоролдо.",
        "mainpagedocfooter": "Был вики менән эшләү тураһында мәғлүмәтте [//meta.wikimedia.org/wiki/Help:Contents ошонда] табып була.\n\n== Файҙалы сығанаҡтар ==\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Көйләүҙәр исемлеге (инг.)];\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ MediaWiki тураһында йыш бирелгән һорауҙар һәм яуаптар (инг.)];\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce MediaWiki-ның яңы версиялары тураһында хәбәрҙәр алып тороу].\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Localise MediaWiki for your language]"
 }
index f329db9..ab750a5 100644 (file)
@@ -13,7 +13,8 @@
                        "아라",
                        "Se4598",
                        "Suriyaa Kudo",
-                       "Das Schäfchen"
+                       "Das Schäfchen",
+                       "Florian"
                ]
        },
        "config-desc": "Das MediaWiki-Installationsprogramm",
@@ -23,7 +24,7 @@
        "config-localsettings-cli-upgrade": "Eine Datei <code>LocalSettings.php</code> wurde gefunden.\nUm die vorhandene Installation zu aktualisieren, muss die Datei <code>update.php</code> ausgeführt werden.",
        "config-localsettings-key": "Aktualisierungsschlüssel:",
        "config-localsettings-badkey": "Der angegebene Aktualisierungsschlüssel ist falsch.",
-       "config-upgrade-key-missing": "Eine MediaWiki-Installation wurde gefunden.\nUm die vorhandene Installation aktualisieren zu können, muss die unten angegebene Codezeile in die Datei <code>LocalSettings.php</code> an deren Ende eingefügt werden:\n\n$1",
+       "config-upgrade-key-missing": "Eine MediaWiki-Installation wurde gefunden.\nUm die vorhandene Installation aktualisieren zu können, muss die unten angegebene Codezeile an das Ende der Datei <code>LocalSettings.php</code> eingefügt werden:\n\n$1",
        "config-localsettings-incomplete": "Die vorhandene Datei <code>LocalSettings.php</code> scheint unvollständig zu sein.\nDie Variable <code>$1</code> wurde nicht definiert.\nDie Datei <code>LocalSettings.php</code> muss entsprechend geändert werden, so dass sie definiert ist. Klicke danach auf „{{int:Config-continue}}“.",
        "config-localsettings-connection-error": "Beim Verbindungsversuch zur Datenbank ist, unter Verwendung der in der Datei <code>LocalSettings.php</code> hinterlegten Einstellungen, ein Fehler aufgetreten. Diese Einstellungen müssen korrigiert werden. Danach kann ein erneuter Versuch unternommen werden.\n\n$1",
        "config-session-error": "Fehler beim Starten der Sitzung: $1",
index 325ee98..e3a6075 100644 (file)
@@ -37,7 +37,6 @@
        "config-env-good": "Το περιβάλλον έχει ελεγχθεί.\nΜπορείτε να εγκαταστήσετε το MediaWiki.",
        "config-env-bad": "Το περιβάλλον έχει ελεγχθεί.\nΔεν μπορείτε να εγκαταστήσετε το MediaWiki.",
        "config-env-php": "H PHP $1 είναι εγκατεστημένη.",
-       "config-env-php-toolow": "Η PHP $1 είναι εγκατεστημένη.\nΩστόσο, το MediaWiki απαιτεί την PHP $2 ή μεταγενέστερη έκδοση.",
        "config-apc": "Το [http://www.php.net/apc APC] είναι εγκατεστημένο",
        "config-diff3-bad": "Το GNU diff3 δεν βρέθηκε.",
        "config-db-type": "Τύπος βάσης δεδομένων:",
        "config-db-host-oracle": "Βάση δεδομένων TNS:",
        "config-db-wiki-settings": "Αναγνώριση αυτού του wiki",
        "config-db-name": "Όνομα βάσης δεδομένων:",
+       "config-db-name-oracle": "Σχήμα βάσης δεδομένων:",
        "config-db-install-account": "Λογαριασμός χρήστη για την εγκατάσταση",
        "config-db-username": "Όνομα χρήστη βάσης δεδομένων:",
        "config-db-password": "Κωδικός πρόσβασης βάσης δεδομένων:",
        "config-db-wiki-account": "Λογαριασμός χρήστη για κανονική λειτουργία",
        "config-charset-mysql5-binary": "MySQL 4.1/5.0 δυαδικό",
+       "config-charset-mysql5": "MySQL 4.1/5.0 UTF-8",
        "config-db-port": "Θύρα βάσης δεδομένων:",
        "config-header-mysql": "Ρυθμίσεις MySQL",
        "config-header-postgres": "Ρυθμίσεις PostgreSQL",
@@ -74,6 +75,7 @@
        "config-ns-generic": "Εγχείρημα",
        "config-ns-site-name": "Ίδιο με το όνομα του wiki: $1",
        "config-ns-other": "Άλλο (προσδιορίστε)",
+       "config-ns-other-default": "ΤοWikiμου",
        "config-admin-box": "Λογαριασμός διαχειριστή",
        "config-admin-name": "Το όνομα χρήστη σας:",
        "config-admin-password": "Κωδικός πρόσβασης:",
        "config-profile-no-anon": "Απαιτείται η δημιουργία λογαριασμού",
        "config-profile-fishbowl": "Εξουσιοδοτημένοι συντάκτες μόνο",
        "config-profile-private": "Ιδιωτικό wiki",
+       "config-license-pd": "Κοινό Κτήμα",
        "config-license-cc-choose": "Επιλέξτε μια προσαρμοσμένη άδεια Creative Commons",
        "config-email-settings": "Ρυθμίσεις ηλεκτρονικού ταχυδρομείου",
        "config-email-usertalk": "Ενεργοποίηση ειδοποίησης σελίδας συζήτησης χρήστη",
        "config-email-auth": "Ενεργοποίηση ταυτοποίησης μέσω ηλεκτρονικού ταχυδρομείου",
        "config-upload-settings": "Ανέβασμα εικόνων και άλλων αρχείων",
        "config-upload-enable": "Ενεργοποιήστε το ανέβασμα αρχείων",
+       "config-upload-deleted": "Καταλόγου για διαγραφέντα αρχεία:",
        "config-logo": "Διεύθυνση URL λογότυπου:",
+       "config-instantcommons": "Ενεργοποίηση Instant Commons",
        "config-cc-again": "Επιλέξτε ξανά...",
        "config-advanced-settings": "Προηγμένες ρυθμίσεις παραμέτρων",
        "config-extensions": "Επεκτάσεις",
+       "config-skins": "Θέματα εμφάνισης",
+       "config-skins-help": "Τα skins που αναφέρονται παραπάνω, εντοπίστηκαν στον κατάλογο <code>./skins</code>. Πρέπει να  ενεργοποιήσετε τουλάχιστον ένα, και επιλέξτε το προεπιλεγμένο.",
+       "config-skins-use-as-default": "Χρησιμοποιήστε αυτό το skin ως προεπιλογή",
+       "config-skins-must-enable-default": "Το skin που επιλέχθηκε ως προεπιλεγμένο πρέπει να ενεργοποιηθεί.",
        "config-install-step-done": "έγινε",
        "config-install-step-failed": "απέτυχε",
+       "config-install-database": "Ρύθμιση βάσης δεδομένων",
        "config-install-user-alreadyexists": "Ο χρήστης \"$1\" υπάρχει ήδη",
        "config-install-tables": "Γίνεται δημιουργία πινάκων",
        "config-install-tables-failed": "<strong>Σφάλμα:</strong>Η δημιουργία πινάκων απέτυχε με το ακόλουθο μήνυμα λάθους: $1",
index 1eb07b9..ab59ac1 100644 (file)
@@ -5,9 +5,12 @@
                        "Robin0van0der0vliet"
                ]
        },
+       "config-information": "Ynformaasje",
+       "config-back": "← Foarige",
        "config-page-language": "Taal",
        "config-page-name": "Namme",
        "config-page-options": "Opsjes",
+       "config-mysql-binary": "Binêr",
        "config-ns-generic": "Projekt",
        "config-admin-password": "Wachtwurd:",
        "config-help": "help",
index 333aa7d..fcade71 100644 (file)
@@ -4,7 +4,8 @@
                        "Firilacroco",
                        "Minisarm",
                        "Stelistcristi",
-                       "XXN"
+                       "XXN",
+                       "Tuxilina"
                ]
        },
        "config-desc": "Programul de instalare pentru MediaWiki",
@@ -39,7 +40,9 @@
        "config-page-copying": "Copiere",
        "config-page-upgradedoc": "Actualizare",
        "config-page-existingwiki": "Wiki existent",
+       "config-help-restart": "Doriți să ștergeți toate datele salvate introduse și să reporniți procesul de instalare?",
        "config-restart": "Da, repornește.",
+       "config-env-good": "Verificarea mediului a fost efectuată cu succes.\nPuteți instala MediaWiki.",
        "config-env-php": "PHP $1 este instalat.",
        "config-env-hhvm": "HHVM $1 este instalat.",
        "config-xcache": "[http://xcache.lighttpd.net/ XCache] este instalat",
index eef6cac..40dca4d 100644 (file)
        "config-mysql-engine": "Движок базы данных:",
        "config-mysql-innodb": "InnoDB",
        "config-mysql-myisam": "MyISAM",
-       "config-mysql-myisam-dep": "''' Внимание.''' Вы выбрали механизм MyISAM для хранения данных MySQL. Он не рекомендуется к использованию по следующим причинам:\n* он слабо поддерживает параллелизм из-за табличных блокировок;\n* более склонен к потере данных, по сравнению с другими механизмами;\n* код MediaWiki не всегда учитывает особенности MyISAM должным образом.\n\nЕсли ваша установка MySQL поддерживает InnoDB, настоятельно рекомендуется выбрать этот механизм.\nЕсли ваша установка MySQL не поддерживает InnoDB, возможно, настало время обновиться.",
+       "config-mysql-myisam-dep": "''' Внимание.''' Вы выбрали механизм MyISAM для хранения данных MySQL. Он не рекомендуется к использованию по следующим причинам:\n* он слабо поддерживает параллелизм из-за табличных блокировок;\n* более склонен к потере данных, по сравнению с другими механизмами;\n* код MediaWiki не всегда учитывает особенности MyISAM должным образом.\n\nЕсли ваша MySQL поддерживает InnoDB, настоятельно рекомендуется выбрать этот механизм.\nЕсли ваша MySQL не поддерживает InnoDB, возможно, настало время обновиться.",
        "config-mysql-only-myisam-dep": "'''Предупреждение:''' MyISAM является единственной доступной системой хранения данных для MySQL на этом компьютере, и она не рекомендуется для использования с MediaWiki, потому что:\n * он слабо поддерживает параллелизм из-за блокировки таблиц\n * она больше других систем подвержена повреждению\n * кодовая база MediaWiki не всегда обрабатывает MyISAM так, как следует\n\nВаша MySQL не поддерживает InnoDB, так что, возможно, настало время для обновления.",
        "config-mysql-engine-help": "'''InnoDB''' почти всегда предпочтительнее, так как он лучше справляется с параллельным доступом.\n\n'''MyISAM''' может оказаться быстрее для вики с одним пользователем или с минимальным количеством поступающих правок, однако базы данных на нём портятся чаще, чем на InnoDB.",
        "config-mysql-charset": "Кодировка базы данных:",
index ce1b260..e12137e 100644 (file)
        "config-skins": "外觀",
        "config-skins-help": "系統偵測到您於 <code>./skins</code> 資料夾中含有外觀如上清單。 您必須開啟其中一項並設為預設值。",
        "config-skins-use-as-default": "使用這種外觀作為預設",
-       "config-skins-missing": "沒有發現任何外觀;MediaWiki在您安裝一些恰當的外觀前將會使用備用外觀。",
+       "config-skins-missing": "沒有發現任何外觀;MediaWiki 在您安裝一些恰當的外觀前將會使用備用外觀。",
        "config-skins-must-enable-some": "您必須至少選擇一個外觀以啟用。",
        "config-skins-must-enable-default": "必須啟用選為預設的外觀。",
        "config-install-alreadydone": "<strong>警告:</strong>您已經安裝 MediaWiki,並且試圖重新安裝。\n請點繼續前往下一個頁面。",
index 55b2506..37a9fcf 100644 (file)
@@ -19,6 +19,8 @@
  *
  * @file
  */
+use \Cdb\Exception as CdbException;
+use \Cdb\Reader as CdbReader;
 
 /**
  * The interwiki class
diff --git a/includes/libs/cdb/CdbException.php b/includes/libs/cdb/CdbException.php
deleted file mode 100644 (file)
index 6cda529..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-<?php
-/**
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- */
-
-/**
- * Exception for Cdb errors.
- * This explicitly doesn't subclass MWException to encourage reuse.
- */
-class CdbException extends Exception {
-}
diff --git a/includes/libs/cdb/CdbFunctions.php b/includes/libs/cdb/CdbFunctions.php
deleted file mode 100644 (file)
index e74924c..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-<?php
-/**
- * This is a port of D.J. Bernstein's CDB to PHP. It's based on the copy that
- * appears in PHP 5.3. Changes are:
- *    * Error returns replaced with exceptions
- *    * Exception thrown if sizes or offsets are between 2GB and 4GB
- *    * Some variables renamed
- *
- * 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
- */
-
-/**
- * Common functions for readers and writers
- */
-class CdbFunctions {
-       /**
-        * Take a modulo of a signed integer as if it were an unsigned integer.
-        * $b must be less than 0x40000000 and greater than 0
-        *
-        * @param int $a
-        * @param int $b
-        *
-        * @return int
-        */
-       public static function unsignedMod( $a, $b ) {
-               if ( $a & 0x80000000 ) {
-                       $m = ( $a & 0x7fffffff ) % $b + 2 * ( 0x40000000 % $b );
-
-                       return $m % $b;
-               } else {
-                       return $a % $b;
-               }
-       }
-
-       /**
-        * Shift a signed integer right as if it were unsigned
-        * @param int $a
-        * @param int $b
-        * @return int
-        */
-       public static function unsignedShiftRight( $a, $b ) {
-               if ( $b == 0 ) {
-                       return $a;
-               }
-               if ( $a & 0x80000000 ) {
-                       return ( ( $a & 0x7fffffff ) >> $b ) | ( 0x40000000 >> ( $b - 1 ) );
-               } else {
-                       return $a >> $b;
-               }
-       }
-
-       /**
-        * The CDB hash function.
-        *
-        * @param string $s
-        *
-        * @return int
-        */
-       public static function hash( $s ) {
-               $h = 5381;
-               $len = strlen( $s );
-               for ( $i = 0; $i < $len; $i++ ) {
-                       $h5 = ( $h << 5 ) & 0xffffffff;
-                       // Do a 32-bit sum
-                       // Inlined here for speed
-                       $sum = ( $h & 0x3fffffff ) + ( $h5 & 0x3fffffff );
-                       $h =
-                               (
-                                       ( $sum & 0x40000000 ? 1 : 0 )
-                                       + ( $h & 0x80000000 ? 2 : 0 )
-                                       + ( $h & 0x40000000 ? 1 : 0 )
-                                       + ( $h5 & 0x80000000 ? 2 : 0 )
-                                       + ( $h5 & 0x40000000 ? 1 : 0 )
-                               ) << 30
-                               | ( $sum & 0x3fffffff );
-                       $h ^= ord( $s[$i] );
-                       $h &= 0xffffffff;
-               }
-
-               return $h;
-       }
-}
diff --git a/includes/libs/cdb/CdbReader.php b/includes/libs/cdb/CdbReader.php
deleted file mode 100644 (file)
index 0ca9b9d..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-<?php
-/**
- * Native CDB file reader and writer.
- *
- * 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
- */
-
-/**
- * Read from a CDB file.
- * Native and pure PHP implementations are provided.
- * http://cr.yp.to/cdb.html
- */
-abstract class CdbReader {
-       /**
-        * The file handle
-        */
-       protected $handle;
-
-       /**
-        * Open a file and return a subclass instance
-        *
-        * @param string $fileName
-        *
-        * @return CdbReader
-        */
-       public static function open( $fileName ) {
-               return self::haveExtension() ?
-                       new CdbReaderDBA( $fileName ) :
-                       new CdbReaderPHP( $fileName );
-       }
-
-       /**
-        * Returns true if the native extension is available
-        *
-        * @return bool
-        */
-       public static function haveExtension() {
-               if ( !function_exists( 'dba_handlers' ) ) {
-                       return false;
-               }
-               $handlers = dba_handlers();
-               if ( !in_array( 'cdb', $handlers ) || !in_array( 'cdb_make', $handlers ) ) {
-                       return false;
-               }
-
-               return true;
-       }
-
-       /**
-        * Create the object and open the file
-        *
-        * @param string $fileName
-        */
-       abstract public function __construct( $fileName );
-
-       /**
-        * Close the file. Optional, you can just let the variable go out of scope.
-        */
-       abstract public function close();
-
-       /**
-        * Get a value with a given key. Only string values are supported.
-        *
-        * @param string $key
-        */
-       abstract public function get( $key );
-}
diff --git a/includes/libs/cdb/CdbReaderDBA.php b/includes/libs/cdb/CdbReaderDBA.php
deleted file mode 100644 (file)
index e0cab73..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-<?php
-/**
- * DBA-based CDB reader/writer
- *
- * 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
- */
-
-/**
- * Reader class which uses the DBA extension
- */
-class CdbReaderDBA extends CdbReader {
-       public function __construct( $fileName ) {
-               $this->handle = dba_open( $fileName, 'r-', 'cdb' );
-               if ( !$this->handle ) {
-                       throw new CdbException( 'Unable to open CDB file "' . $fileName . '"' );
-               }
-       }
-
-       public function close() {
-               if ( isset( $this->handle ) ) {
-                       dba_close( $this->handle );
-               }
-               unset( $this->handle );
-       }
-
-       public function get( $key ) {
-               return dba_fetch( $key, $this->handle );
-       }
-}
diff --git a/includes/libs/cdb/CdbReaderPHP.php b/includes/libs/cdb/CdbReaderPHP.php
deleted file mode 100644 (file)
index e448414..0000000
+++ /dev/null
@@ -1,214 +0,0 @@
-<?php
-/**
- * This is a port of D.J. Bernstein's CDB to PHP. It's based on the copy that
- * appears in PHP 5.3. Changes are:
- *    * Error returns replaced with exceptions
- *    * Exception thrown if sizes or offsets are between 2GB and 4GB
- *    * Some variables renamed
- *
- * 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
- */
-
-/**
- * CDB reader class
- */
-class CdbReaderPHP extends CdbReader {
-       /** The filename */
-       protected $fileName;
-
-       /* number of hash slots searched under this key */
-       protected $loop;
-
-       /* initialized if loop is nonzero */
-       protected $khash;
-
-       /* initialized if loop is nonzero */
-       protected $kpos;
-
-       /* initialized if loop is nonzero */
-       protected $hpos;
-
-       /* initialized if loop is nonzero */
-       protected $hslots;
-
-       /* initialized if findNext() returns true */
-       protected $dpos;
-
-       /* initialized if cdb_findnext() returns 1 */
-       protected $dlen;
-
-       /**
-        * @param string $fileName
-        * @throws CdbException
-        */
-       public function __construct( $fileName ) {
-               $this->fileName = $fileName;
-               $this->handle = fopen( $fileName, 'rb' );
-               if ( !$this->handle ) {
-                       throw new CdbException( 'Unable to open CDB file "' . $this->fileName . '".' );
-               }
-               $this->findStart();
-       }
-
-       public function close() {
-               if ( isset( $this->handle ) ) {
-                       fclose( $this->handle );
-               }
-               unset( $this->handle );
-       }
-
-       /**
-        * @param mixed $key
-        * @return bool|string
-        */
-       public function get( $key ) {
-               // strval is required
-               if ( $this->find( strval( $key ) ) ) {
-                       return $this->read( $this->dlen, $this->dpos );
-               } else {
-                       return false;
-               }
-       }
-
-       /**
-        * @param string $key
-        * @param int $pos
-        * @return bool
-        */
-       protected function match( $key, $pos ) {
-               $buf = $this->read( strlen( $key ), $pos );
-
-               return $buf === $key;
-       }
-
-       protected function findStart() {
-               $this->loop = 0;
-       }
-
-       /**
-        * @throws CdbException
-        * @param int $length
-        * @param int $pos
-        * @return string
-        */
-       protected function read( $length, $pos ) {
-               if ( fseek( $this->handle, $pos ) == -1 ) {
-                       // This can easily happen if the internal pointers are incorrect
-                       throw new CdbException(
-                               'Seek failed, file "' . $this->fileName . '" may be corrupted.' );
-               }
-
-               if ( $length == 0 ) {
-                       return '';
-               }
-
-               $buf = fread( $this->handle, $length );
-               if ( $buf === false || strlen( $buf ) !== $length ) {
-                       throw new CdbException(
-                               'Read from CDB file failed, file "' . $this->fileName . '" may be corrupted.' );
-               }
-
-               return $buf;
-       }
-
-       /**
-        * Unpack an unsigned integer and throw an exception if it needs more than 31 bits
-        * @param string $s
-        * @throws CdbException
-        * @return mixed
-        */
-       protected function unpack31( $s ) {
-               $data = unpack( 'V', $s );
-               if ( $data[1] > 0x7fffffff ) {
-                       throw new CdbException(
-                               'Error in CDB file "' . $this->fileName . '", integer too big.' );
-               }
-
-               return $data[1];
-       }
-
-       /**
-        * Unpack a 32-bit signed integer
-        * @param string $s
-        * @return int
-        */
-       protected function unpackSigned( $s ) {
-               $data = unpack( 'va/vb', $s );
-
-               return $data['a'] | ( $data['b'] << 16 );
-       }
-
-       /**
-        * @param string $key
-        * @return bool
-        */
-       protected function findNext( $key ) {
-               if ( !$this->loop ) {
-                       $u = CdbFunctions::hash( $key );
-                       $buf = $this->read( 8, ( $u << 3 ) & 2047 );
-                       $this->hslots = $this->unpack31( substr( $buf, 4 ) );
-                       if ( !$this->hslots ) {
-                               return false;
-                       }
-                       $this->hpos = $this->unpack31( substr( $buf, 0, 4 ) );
-                       $this->khash = $u;
-                       $u = CdbFunctions::unsignedShiftRight( $u, 8 );
-                       $u = CdbFunctions::unsignedMod( $u, $this->hslots );
-                       $u <<= 3;
-                       $this->kpos = $this->hpos + $u;
-               }
-
-               while ( $this->loop < $this->hslots ) {
-                       $buf = $this->read( 8, $this->kpos );
-                       $pos = $this->unpack31( substr( $buf, 4 ) );
-                       if ( !$pos ) {
-                               return false;
-                       }
-                       $this->loop += 1;
-                       $this->kpos += 8;
-                       if ( $this->kpos == $this->hpos + ( $this->hslots << 3 ) ) {
-                               $this->kpos = $this->hpos;
-                       }
-                       $u = $this->unpackSigned( substr( $buf, 0, 4 ) );
-                       if ( $u === $this->khash ) {
-                               $buf = $this->read( 8, $pos );
-                               $keyLen = $this->unpack31( substr( $buf, 0, 4 ) );
-                               if ( $keyLen == strlen( $key ) && $this->match( $key, $pos + 8 ) ) {
-                                       // Found
-                                       $this->dlen = $this->unpack31( substr( $buf, 4 ) );
-                                       $this->dpos = $pos + 8 + $keyLen;
-
-                                       return true;
-                               }
-                       }
-               }
-
-               return false;
-       }
-
-       /**
-        * @param mixed $key
-        * @return bool
-        */
-       protected function find( $key ) {
-               $this->findStart();
-
-               return $this->findNext( $key );
-       }
-}
-
diff --git a/includes/libs/cdb/CdbWriter.php b/includes/libs/cdb/CdbWriter.php
deleted file mode 100644 (file)
index b0a90c3..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-<?php
-/**
- * Native CDB file reader and writer.
- *
- * 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
- */
-
-/**
- * Write to a CDB file.
- * Native and pure PHP implementations are provided.
- * http://cr.yp.to/cdb.html
- */
-abstract class CdbWriter {
-       /**
-        * The file handle
-        */
-       protected $handle;
-
-       /**
-        * File we'll be writing to when we're done
-        * @var string
-        */
-       protected $realFileName;
-
-       /**
-        * File we write to temporarily until we're done
-        * @var string
-        */
-       protected $tmpFileName;
-
-       /**
-        * Open a writer and return a subclass instance.
-        * The user must have write access to the directory, for temporary file creation.
-        *
-        * @param string $fileName
-        *
-        * @return CdbWriterDBA|CdbWriterPHP
-        */
-       public static function open( $fileName ) {
-               return CdbReader::haveExtension() ?
-                       new CdbWriterDBA( $fileName ) :
-                       new CdbWriterPHP( $fileName );
-       }
-
-       /**
-        * Create the object and open the file
-        *
-        * @param string $fileName
-        */
-       abstract public function __construct( $fileName );
-
-       /**
-        * Set a key to a given value. The value will be converted to string.
-        * @param string $key
-        * @param string $value
-        */
-       abstract public function set( $key, $value );
-
-       /**
-        * Close the writer object. You should call this function before the object
-        * goes out of scope, to write out the final hashtables.
-        */
-       abstract public function close();
-
-       /**
-        * If the object goes out of scope, close it for sanity
-        */
-       public function __destruct() {
-               if ( isset( $this->handle ) ) {
-                       $this->close();
-               }
-       }
-
-       /**
-        * Are we running on Windows?
-        * @return bool
-        */
-       protected function isWindows() {
-               return substr( php_uname(), 0, 7 ) == 'Windows';
-       }
-}
diff --git a/includes/libs/cdb/CdbWriterDBA.php b/includes/libs/cdb/CdbWriterDBA.php
deleted file mode 100644 (file)
index 1de371d..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-<?php
-/**
- * DBA-based CDB reader/writer
- *
- * 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
- */
-
-/**
- * Writer class which uses the DBA extension
- */
-class CdbWriterDBA extends CdbWriter {
-       public function __construct( $fileName ) {
-               $this->realFileName = $fileName;
-               $this->tmpFileName = $fileName . '.tmp.' . mt_rand( 0, 0x7fffffff );
-               $this->handle = dba_open( $this->tmpFileName, 'n', 'cdb_make' );
-               if ( !$this->handle ) {
-                       throw new CdbException( 'Unable to open CDB file for write "' . $fileName . '"' );
-               }
-       }
-
-       public function set( $key, $value ) {
-               return dba_insert( $key, $value, $this->handle );
-       }
-
-       public function close() {
-               if ( isset( $this->handle ) ) {
-                       dba_close( $this->handle );
-               }
-               if ( $this->isWindows() ) {
-                       unlink( $this->realFileName );
-               }
-               if ( !rename( $this->tmpFileName, $this->realFileName ) ) {
-                       throw new CdbException( 'Unable to move the new CDB file into place.' );
-               }
-               unset( $this->handle );
-       }
-}
diff --git a/includes/libs/cdb/CdbWriterPHP.php b/includes/libs/cdb/CdbWriterPHP.php
deleted file mode 100644 (file)
index bfc0d87..0000000
+++ /dev/null
@@ -1,234 +0,0 @@
-<?php
-/**
- * This is a port of D.J. Bernstein's CDB to PHP. It's based on the copy that
- * appears in PHP 5.3. Changes are:
- *    * Error returns replaced with exceptions
- *    * Exception thrown if sizes or offsets are between 2GB and 4GB
- *    * Some variables renamed
- *
- * 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
- */
-
-/**
- * CDB writer class
- */
-class CdbWriterPHP extends CdbWriter {
-       protected $hplist;
-
-       protected $numentries;
-
-       protected $pos;
-
-       /**
-        * @param string $fileName
-        */
-       public function __construct( $fileName ) {
-               $this->realFileName = $fileName;
-               $this->tmpFileName = $fileName . '.tmp.' . mt_rand( 0, 0x7fffffff );
-               $this->handle = fopen( $this->tmpFileName, 'wb' );
-               if ( !$this->handle ) {
-                       $this->throwException(
-                               'Unable to open CDB file "' . $this->tmpFileName . '" for write.' );
-               }
-               $this->hplist = array();
-               $this->numentries = 0;
-               $this->pos = 2048; // leaving space for the pointer array, 256 * 8
-               if ( fseek( $this->handle, $this->pos ) == -1 ) {
-                       $this->throwException( 'fseek failed in file "' . $this->tmpFileName . '".' );
-               }
-       }
-
-       /**
-        * @param string $key
-        * @param string $value
-        */
-       public function set( $key, $value ) {
-               if ( strval( $key ) === '' ) {
-                       // DBA cross-check hack
-                       return;
-               }
-               $this->addbegin( strlen( $key ), strlen( $value ) );
-               $this->write( $key );
-               $this->write( $value );
-               $this->addend( strlen( $key ), strlen( $value ), CdbFunctions::hash( $key ) );
-       }
-
-       /**
-        * @throws CdbException
-        */
-       public function close() {
-               $this->finish();
-               if ( isset( $this->handle ) ) {
-                       fclose( $this->handle );
-               }
-               if ( $this->isWindows() && file_exists( $this->realFileName ) ) {
-                       unlink( $this->realFileName );
-               }
-               if ( !rename( $this->tmpFileName, $this->realFileName ) ) {
-                       $this->throwException( 'Unable to move the new CDB file into place.' );
-               }
-               unset( $this->handle );
-       }
-
-       /**
-        * @throws CdbException
-        * @param string $buf
-        */
-       protected function write( $buf ) {
-               $len = fwrite( $this->handle, $buf );
-               if ( $len !== strlen( $buf ) ) {
-                       $this->throwException( 'Error writing to CDB file "' . $this->tmpFileName . '".' );
-               }
-       }
-
-       /**
-        * @throws CdbException
-        * @param int $len
-        */
-       protected function posplus( $len ) {
-               $newpos = $this->pos + $len;
-               if ( $newpos > 0x7fffffff ) {
-                       $this->throwException(
-                               'A value in the CDB file "' . $this->tmpFileName . '" is too large.' );
-               }
-               $this->pos = $newpos;
-       }
-
-       /**
-        * @param int $keylen
-        * @param int $datalen
-        * @param int $h
-        */
-       protected function addend( $keylen, $datalen, $h ) {
-               $this->hplist[] = array(
-                       'h' => $h,
-                       'p' => $this->pos
-               );
-
-               $this->numentries++;
-               $this->posplus( 8 );
-               $this->posplus( $keylen );
-               $this->posplus( $datalen );
-       }
-
-       /**
-        * @throws CdbException
-        * @param int $keylen
-        * @param int $datalen
-        */
-       protected function addbegin( $keylen, $datalen ) {
-               if ( $keylen > 0x7fffffff ) {
-                       $this->throwException( 'Key length too long in file "' . $this->tmpFileName . '".' );
-               }
-               if ( $datalen > 0x7fffffff ) {
-                       $this->throwException( 'Data length too long in file "' . $this->tmpFileName . '".' );
-               }
-               $buf = pack( 'VV', $keylen, $datalen );
-               $this->write( $buf );
-       }
-
-       /**
-        * @throws CdbException
-        */
-       protected function finish() {
-               // Hack for DBA cross-check
-               $this->hplist = array_reverse( $this->hplist );
-
-               // Calculate the number of items that will be in each hashtable
-               $counts = array_fill( 0, 256, 0 );
-               foreach ( $this->hplist as $item ) {
-                       ++$counts[255 & $item['h']];
-               }
-
-               // Fill in $starts with the *end* indexes
-               $starts = array();
-               $pos = 0;
-               for ( $i = 0; $i < 256; ++$i ) {
-                       $pos += $counts[$i];
-                       $starts[$i] = $pos;
-               }
-
-               // Excessively clever and indulgent code to simultaneously fill $packedTables
-               // with the packed hashtables, and adjust the elements of $starts
-               // to actually point to the starts instead of the ends.
-               $packedTables = array_fill( 0, $this->numentries, false );
-               foreach ( $this->hplist as $item ) {
-                       $packedTables[--$starts[255 & $item['h']]] = $item;
-               }
-
-               $final = '';
-               for ( $i = 0; $i < 256; ++$i ) {
-                       $count = $counts[$i];
-
-                       // The size of the hashtable will be double the item count.
-                       // The rest of the slots will be empty.
-                       $len = $count + $count;
-                       $final .= pack( 'VV', $this->pos, $len );
-
-                       $hashtable = array();
-                       for ( $u = 0; $u < $len; ++$u ) {
-                               $hashtable[$u] = array( 'h' => 0, 'p' => 0 );
-                       }
-
-                       // Fill the hashtable, using the next empty slot if the hashed slot
-                       // is taken.
-                       for ( $u = 0; $u < $count; ++$u ) {
-                               $hp = $packedTables[$starts[$i] + $u];
-                               $where = CdbFunctions::unsignedMod(
-                                       CdbFunctions::unsignedShiftRight( $hp['h'], 8 ), $len );
-                               while ( $hashtable[$where]['p'] ) {
-                                       if ( ++$where == $len ) {
-                                               $where = 0;
-                                       }
-                               }
-                               $hashtable[$where] = $hp;
-                       }
-
-                       // Write the hashtable
-                       for ( $u = 0; $u < $len; ++$u ) {
-                               $buf = pack( 'vvV',
-                                       $hashtable[$u]['h'] & 0xffff,
-                                       CdbFunctions::unsignedShiftRight( $hashtable[$u]['h'], 16 ),
-                                       $hashtable[$u]['p'] );
-                               $this->write( $buf );
-                               $this->posplus( 8 );
-                       }
-               }
-
-               // Write the pointer array at the start of the file
-               rewind( $this->handle );
-               if ( ftell( $this->handle ) != 0 ) {
-                       $this->throwException( 'Error rewinding to start of file "' . $this->tmpFileName . '".' );
-               }
-               $this->write( $final );
-       }
-
-       /**
-        * Clean up the temp file and throw an exception
-        *
-        * @param string $msg
-        * @throws CdbException
-        */
-       protected function throwException( $msg ) {
-               if ( $this->handle ) {
-                       fclose( $this->handle );
-                       unlink( $this->tmpFileName );
-               }
-               throw new CdbException( $msg );
-       }
-}
diff --git a/includes/libs/lessc.inc.php b/includes/libs/lessc.inc.php
deleted file mode 100644 (file)
index 61ed771..0000000
+++ /dev/null
@@ -1,3796 +0,0 @@
-<?php
-// @codingStandardsIgnoreFile File external to MediaWiki. Ignore coding conventions checks.
-/**
- * lessphp v0.4.0@2cc77e3c7b
- * http://leafo.net/lessphp
- *
- * LESS CSS compiler, adapted from http://lesscss.org
- *
- * For ease of distribution, lessphp 0.4.0 is under a dual license.
- * You are free to pick which one suits your needs.
- *
- * MIT LICENSE
- *
- * Copyright 2013, Leaf Corcoran <leafot@gmail.com>
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
- * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- *
- * GPL VERSION 3
- *
- * Please refer to http://www.gnu.org/licenses/gpl-3.0.html for the full
- * text of the GPL version 3
- */
-
-
-/**
- * The LESS compiler and parser.
- *
- * Converting LESS to CSS is a three stage process. The incoming file is parsed
- * by `lessc_parser` into a syntax tree, then it is compiled into another tree
- * representing the CSS structure by `lessc`. The CSS tree is fed into a
- * formatter, like `lessc_formatter` which then outputs CSS as a string.
- *
- * During the first compile, all values are *reduced*, which means that their
- * types are brought to the lowest form before being dump as strings. This
- * handles math equations, variable dereferences, and the like.
- *
- * The `parse` function of `lessc` is the entry point.
- *
- * In summary:
- *
- * The `lessc` class creates an instance of the parser, feeds it LESS code,
- * then transforms the resulting tree to a CSS tree. This class also holds the
- * evaluation context, such as all available mixins and variables at any given
- * time.
- *
- * The `lessc_parser` class is only concerned with parsing its input.
- *
- * The `lessc_formatter` takes a CSS tree, and dumps it to a formatted string,
- * handling things like indentation.
- */
-class lessc {
-       static public $VERSION = "v0.4.0";
-
-       static public $TRUE = array("keyword", "true");
-       static public $FALSE = array("keyword", "false");
-
-       protected $libFunctions = array();
-       protected $registeredVars = array();
-       protected $preserveComments = false;
-
-       public $vPrefix = '@'; // prefix of abstract properties
-       public $mPrefix = '$'; // prefix of abstract blocks
-       public $parentSelector = '&';
-
-       public $importDisabled = false;
-       public $importDir = '';
-
-       protected $numberPrecision = null;
-
-       protected $allParsedFiles = array();
-
-       // set to the parser that generated the current line when compiling
-       // so we know how to create error messages
-       protected $sourceParser = null;
-       protected $sourceLoc = null;
-
-       static protected $nextImportId = 0; // uniquely identify imports
-
-       // attempts to find the path of an import url, returns null for css files
-       protected function findImport($url) {
-               foreach ((array)$this->importDir as $dir) {
-                       $full = $dir.(substr($dir, -1) != '/' ? '/' : '').$url;
-                       if ($this->fileExists($file = $full.'.less') || $this->fileExists($file = $full)) {
-                               return $file;
-                       }
-               }
-
-               return null;
-       }
-
-       protected function fileExists($name) {
-               return is_file($name);
-       }
-
-       static public function compressList($items, $delim) {
-               if (!isset($items[1]) && isset($items[0])) return $items[0];
-               else return array('list', $delim, $items);
-       }
-
-       static public function preg_quote($what) {
-               return preg_quote($what, '/');
-       }
-
-       protected function tryImport($importPath, $parentBlock, $out) {
-               if ($importPath[0] == "function" && $importPath[1] == "url") {
-                       $importPath = $this->flattenList($importPath[2]);
-               }
-
-               $str = $this->coerceString($importPath);
-               if ($str === null) return false;
-
-               $url = $this->compileValue($this->lib_e($str));
-
-               // don't import if it ends in css
-               if (substr_compare($url, '.css', -4, 4) === 0) return false;
-
-               $realPath = $this->findImport($url);
-
-               if ($realPath === null) return false;
-
-               if ($this->importDisabled) {
-                       return array(false, "/* import disabled */");
-               }
-
-               if (isset($this->allParsedFiles[realpath($realPath)])) {
-                       return array(false, null);
-               }
-
-               $this->addParsedFile($realPath);
-               $parser = $this->makeParser($realPath);
-               $root = $parser->parse(file_get_contents($realPath));
-
-               // set the parents of all the block props
-               foreach ($root->props as $prop) {
-                       if ($prop[0] == "block") {
-                               $prop[1]->parent = $parentBlock;
-                       }
-               }
-
-               // copy mixins into scope, set their parents
-               // bring blocks from import into current block
-               // TODO: need to mark the source parser these came from this file
-               foreach ($root->children as $childName => $child) {
-                       if (isset($parentBlock->children[$childName])) {
-                               $parentBlock->children[$childName] = array_merge(
-                                       $parentBlock->children[$childName],
-                                       $child);
-                       } else {
-                               $parentBlock->children[$childName] = $child;
-                       }
-               }
-
-               $pi = pathinfo($realPath);
-               $dir = $pi["dirname"];
-
-               list($top, $bottom) = $this->sortProps($root->props, true);
-               $this->compileImportedProps($top, $parentBlock, $out, $parser, $dir);
-
-               return array(true, $bottom, $parser, $dir);
-       }
-
-       protected function compileImportedProps($props, $block, $out, $sourceParser, $importDir) {
-               $oldSourceParser = $this->sourceParser;
-
-               $oldImport = $this->importDir;
-
-               // TODO: this is because the importDir api is stupid
-               $this->importDir = (array)$this->importDir;
-               array_unshift($this->importDir, $importDir);
-
-               foreach ($props as $prop) {
-                       $this->compileProp($prop, $block, $out);
-               }
-
-               $this->importDir = $oldImport;
-               $this->sourceParser = $oldSourceParser;
-       }
-
-       /**
-        * Recursively compiles a block.
-        *
-        * A block is analogous to a CSS block in most cases. A single LESS document
-        * is encapsulated in a block when parsed, but it does not have parent tags
-        * so all of it's children appear on the root level when compiled.
-        *
-        * Blocks are made up of props and children.
-        *
-        * Props are property instructions, array tuples which describe an action
-        * to be taken, eg. write a property, set a variable, mixin a block.
-        *
-        * The children of a block are just all the blocks that are defined within.
-        * This is used to look up mixins when performing a mixin.
-        *
-        * Compiling the block involves pushing a fresh environment on the stack,
-        * and iterating through the props, compiling each one.
-        *
-        * See lessc::compileProp()
-        *
-        */
-       protected function compileBlock($block) {
-               switch ($block->type) {
-               case "root":
-                       $this->compileRoot($block);
-                       break;
-               case null:
-                       $this->compileCSSBlock($block);
-                       break;
-               case "media":
-                       $this->compileMedia($block);
-                       break;
-               case "directive":
-                       $name = "@" . $block->name;
-                       if (!empty($block->value)) {
-                               $name .= " " . $this->compileValue($this->reduce($block->value));
-                       }
-
-                       $this->compileNestedBlock($block, array($name));
-                       break;
-               default:
-                       $this->throwError("unknown block type: $block->type\n");
-               }
-       }
-
-       protected function compileCSSBlock($block) {
-               $env = $this->pushEnv();
-
-               $selectors = $this->compileSelectors($block->tags);
-               $env->selectors = $this->multiplySelectors($selectors);
-               $out = $this->makeOutputBlock(null, $env->selectors);
-
-               $this->scope->children[] = $out;
-               $this->compileProps($block, $out);
-
-               $block->scope = $env; // mixins carry scope with them!
-               $this->popEnv();
-       }
-
-       protected function compileMedia($media) {
-               $env = $this->pushEnv($media);
-               $parentScope = $this->mediaParent($this->scope);
-
-               $query = $this->compileMediaQuery($this->multiplyMedia($env));
-
-               $this->scope = $this->makeOutputBlock($media->type, array($query));
-               $parentScope->children[] = $this->scope;
-
-               $this->compileProps($media, $this->scope);
-
-               if (count($this->scope->lines) > 0) {
-                       $orphanSelelectors = $this->findClosestSelectors();
-                       if (!is_null($orphanSelelectors)) {
-                               $orphan = $this->makeOutputBlock(null, $orphanSelelectors);
-                               $orphan->lines = $this->scope->lines;
-                               array_unshift($this->scope->children, $orphan);
-                               $this->scope->lines = array();
-                       }
-               }
-
-               $this->scope = $this->scope->parent;
-               $this->popEnv();
-       }
-
-       protected function mediaParent($scope) {
-               while (!empty($scope->parent)) {
-                       if (!empty($scope->type) && $scope->type != "media") {
-                               break;
-                       }
-                       $scope = $scope->parent;
-               }
-
-               return $scope;
-       }
-
-       protected function compileNestedBlock($block, $selectors) {
-               $this->pushEnv($block);
-               $this->scope = $this->makeOutputBlock($block->type, $selectors);
-               $this->scope->parent->children[] = $this->scope;
-
-               $this->compileProps($block, $this->scope);
-
-               $this->scope = $this->scope->parent;
-               $this->popEnv();
-       }
-
-       protected function compileRoot($root) {
-               $this->pushEnv();
-               $this->scope = $this->makeOutputBlock($root->type);
-               $this->compileProps($root, $this->scope);
-               $this->popEnv();
-       }
-
-       protected function compileProps($block, $out) {
-               foreach ($this->sortProps($block->props) as $prop) {
-                       $this->compileProp($prop, $block, $out);
-               }
-               $out->lines = $this->deduplicate($out->lines);
-       }
-
-       /**
-        * Deduplicate lines in a block. Comments are not deduplicated. If a
-        * duplicate rule is detected, the comments immediately preceding each
-        * occurence are consolidated.
-        */
-       protected function deduplicate($lines) {
-               $unique = array();
-               $comments = array();
-
-               foreach($lines as $line) {
-                       if (strpos($line, '/*') === 0) {
-                               $comments[] = $line;
-                               continue;
-                       }
-                       if (!in_array($line, $unique)) {
-                               $unique[] = $line;
-                       }
-                       array_splice($unique, array_search($line, $unique), 0, $comments);
-                       $comments = array();
-               }
-               return array_merge($unique, $comments);
-       }
-
-       protected function sortProps($props, $split = false) {
-               $vars = array();
-               $imports = array();
-               $other = array();
-               $stack = array();
-
-               foreach ($props as $prop) {
-                       switch ($prop[0]) {
-                       case "comment":
-                               $stack[] = $prop;
-                               break;
-                       case "assign":
-                               $stack[] = $prop;
-                               if (isset($prop[1][0]) && $prop[1][0] == $this->vPrefix) {
-                                       $vars = array_merge($vars, $stack);
-                               } else {
-                                       $other = array_merge($other, $stack);
-                               }
-                               $stack = array();
-                               break;
-                       case "import":
-                               $id = self::$nextImportId++;
-                               $prop[] = $id;
-                               $stack[] = $prop;
-                               $imports = array_merge($imports, $stack);
-                               $other[] = array("import_mixin", $id);
-                               $stack = array();
-                               break;
-                       default:
-                               $stack[] = $prop;
-                               $other = array_merge($other, $stack);
-                               $stack = array();
-                               break;
-                       }
-               }
-               $other = array_merge($other, $stack);
-
-               if ($split) {
-                       return array(array_merge($vars, $imports), $other);
-               } else {
-                       return array_merge($vars, $imports, $other);
-               }
-       }
-
-       protected function compileMediaQuery($queries) {
-               $compiledQueries = array();
-               foreach ($queries as $query) {
-                       $parts = array();
-                       foreach ($query as $q) {
-                               switch ($q[0]) {
-                               case "mediaType":
-                                       $parts[] = implode(" ", array_slice($q, 1));
-                                       break;
-                               case "mediaExp":
-                                       if (isset($q[2])) {
-                                               $parts[] = "($q[1]: " .
-                                                       $this->compileValue($this->reduce($q[2])) . ")";
-                                       } else {
-                                               $parts[] = "($q[1])";
-                                       }
-                                       break;
-                               case "variable":
-                                       $parts[] = $this->compileValue($this->reduce($q));
-                               break;
-                               }
-                       }
-
-                       if (count($parts) > 0) {
-                               $compiledQueries[] =  implode(" and ", $parts);
-                       }
-               }
-
-               $out = "@media";
-               if (!empty($parts)) {
-                       $out .= " " .
-                               implode($this->formatter->selectorSeparator, $compiledQueries);
-               }
-               return $out;
-       }
-
-       protected function multiplyMedia($env, $childQueries = null) {
-               if (is_null($env) ||
-                       !empty($env->block->type) && $env->block->type != "media")
-               {
-                       return $childQueries;
-               }
-
-               // plain old block, skip
-               if (empty($env->block->type)) {
-                       return $this->multiplyMedia($env->parent, $childQueries);
-               }
-
-               $out = array();
-               $queries = $env->block->queries;
-               if (is_null($childQueries)) {
-                       $out = $queries;
-               } else {
-                       foreach ($queries as $parent) {
-                               foreach ($childQueries as $child) {
-                                       $out[] = array_merge($parent, $child);
-                               }
-                       }
-               }
-
-               return $this->multiplyMedia($env->parent, $out);
-       }
-
-       protected function expandParentSelectors(&$tag, $replace) {
-               $parts = explode("$&$", $tag);
-               $count = 0;
-               foreach ($parts as &$part) {
-                       $part = str_replace($this->parentSelector, $replace, $part, $c);
-                       $count += $c;
-               }
-               $tag = implode($this->parentSelector, $parts);
-               return $count;
-       }
-
-       protected function findClosestSelectors() {
-               $env = $this->env;
-               $selectors = null;
-               while ($env !== null) {
-                       if (isset($env->selectors)) {
-                               $selectors = $env->selectors;
-                               break;
-                       }
-                       $env = $env->parent;
-               }
-
-               return $selectors;
-       }
-
-
-       // multiply $selectors against the nearest selectors in env
-       protected function multiplySelectors($selectors) {
-               // find parent selectors
-
-               $parentSelectors = $this->findClosestSelectors();
-               if (is_null($parentSelectors)) {
-                       // kill parent reference in top level selector
-                       foreach ($selectors as &$s) {
-                               $this->expandParentSelectors($s, "");
-                       }
-
-                       return $selectors;
-               }
-
-               $out = array();
-               foreach ($parentSelectors as $parent) {
-                       foreach ($selectors as $child) {
-                               $count = $this->expandParentSelectors($child, $parent);
-
-                               // don't prepend the parent tag if & was used
-                               if ($count > 0) {
-                                       $out[] = trim($child);
-                               } else {
-                                       $out[] = trim($parent . ' ' . $child);
-                               }
-                       }
-               }
-
-               return $out;
-       }
-
-       // reduces selector expressions
-       protected function compileSelectors($selectors) {
-               $out = array();
-
-               foreach ($selectors as $s) {
-                       if (is_array($s)) {
-                               list(, $value) = $s;
-                               $out[] = trim($this->compileValue($this->reduce($value)));
-                       } else {
-                               $out[] = $s;
-                       }
-               }
-
-               return $out;
-       }
-
-       protected function eq($left, $right) {
-               return $left == $right;
-       }
-
-       protected function patternMatch($block, $orderedArgs, $keywordArgs) {
-               // match the guards if it has them
-               // any one of the groups must have all its guards pass for a match
-               if (!empty($block->guards)) {
-                       $groupPassed = false;
-                       foreach ($block->guards as $guardGroup) {
-                               foreach ($guardGroup as $guard) {
-                                       $this->pushEnv();
-                                       $this->zipSetArgs($block->args, $orderedArgs, $keywordArgs);
-
-                                       $negate = false;
-                                       if ($guard[0] == "negate") {
-                                               $guard = $guard[1];
-                                               $negate = true;
-                                       }
-
-                                       $passed = $this->reduce($guard) == self::$TRUE;
-                                       if ($negate) $passed = !$passed;
-
-                                       $this->popEnv();
-
-                                       if ($passed) {
-                                               $groupPassed = true;
-                                       } else {
-                                               $groupPassed = false;
-                                               break;
-                                       }
-                               }
-
-                               if ($groupPassed) break;
-                       }
-
-                       if (!$groupPassed) {
-                               return false;
-                       }
-               }
-
-               if (empty($block->args)) {
-                       return $block->isVararg || empty($orderedArgs) && empty($keywordArgs);
-               }
-
-               $remainingArgs = $block->args;
-               if ($keywordArgs) {
-                       $remainingArgs = array();
-                       foreach ($block->args as $arg) {
-                               if ($arg[0] == "arg" && isset($keywordArgs[$arg[1]])) {
-                                       continue;
-                               }
-
-                               $remainingArgs[] = $arg;
-                       }
-               }
-
-               $i = -1; // no args
-               // try to match by arity or by argument literal
-               foreach ($remainingArgs as $i => $arg) {
-                       switch ($arg[0]) {
-                       case "lit":
-                               if (empty($orderedArgs[$i]) || !$this->eq($arg[1], $orderedArgs[$i])) {
-                                       return false;
-                               }
-                               break;
-                       case "arg":
-                               // no arg and no default value
-                               if (!isset($orderedArgs[$i]) && !isset($arg[2])) {
-                                       return false;
-                               }
-                               break;
-                       case "rest":
-                               $i--; // rest can be empty
-                               break 2;
-                       }
-               }
-
-               if ($block->isVararg) {
-                       return true; // not having enough is handled above
-               } else {
-                       $numMatched = $i + 1;
-                       // greater than becuase default values always match
-                       return $numMatched >= count($orderedArgs);
-               }
-       }
-
-       protected function patternMatchAll($blocks, $orderedArgs, $keywordArgs, $skip=array()) {
-               $matches = null;
-               foreach ($blocks as $block) {
-                       // skip seen blocks that don't have arguments
-                       if (isset($skip[$block->id]) && !isset($block->args)) {
-                               continue;
-                       }
-
-                       if ($this->patternMatch($block, $orderedArgs, $keywordArgs)) {
-                               $matches[] = $block;
-                       }
-               }
-
-               return $matches;
-       }
-
-       // attempt to find blocks matched by path and args
-       protected function findBlocks($searchIn, $path, $orderedArgs, $keywordArgs, $seen=array()) {
-               if ($searchIn == null) return null;
-               if (isset($seen[$searchIn->id])) return null;
-               $seen[$searchIn->id] = true;
-
-               $name = $path[0];
-
-               if (isset($searchIn->children[$name])) {
-                       $blocks = $searchIn->children[$name];
-                       if (count($path) == 1) {
-                               $matches = $this->patternMatchAll($blocks, $orderedArgs, $keywordArgs, $seen);
-                               if (!empty($matches)) {
-                                       // This will return all blocks that match in the closest
-                                       // scope that has any matching block, like lessjs
-                                       return $matches;
-                               }
-                       } else {
-                               $matches = array();
-                               foreach ($blocks as $subBlock) {
-                                       $subMatches = $this->findBlocks($subBlock,
-                                               array_slice($path, 1), $orderedArgs, $keywordArgs, $seen);
-
-                                       if (!is_null($subMatches)) {
-                                               foreach ($subMatches as $sm) {
-                                                       $matches[] = $sm;
-                                               }
-                                       }
-                               }
-
-                               return count($matches) > 0 ? $matches : null;
-                       }
-               }
-               if ($searchIn->parent === $searchIn) return null;
-               return $this->findBlocks($searchIn->parent, $path, $orderedArgs, $keywordArgs, $seen);
-       }
-
-       // sets all argument names in $args to either the default value
-       // or the one passed in through $values
-       protected function zipSetArgs($args, $orderedValues, $keywordValues) {
-               $assignedValues = array();
-
-               $i = 0;
-               foreach ($args as  $a) {
-                       if ($a[0] == "arg") {
-                               if (isset($keywordValues[$a[1]])) {
-                                       // has keyword arg
-                                       $value = $keywordValues[$a[1]];
-                               } elseif (isset($orderedValues[$i])) {
-                                       // has ordered arg
-                                       $value = $orderedValues[$i];
-                                       $i++;
-                               } elseif (isset($a[2])) {
-                                       // has default value
-                                       $value = $a[2];
-                               } else {
-                                       $this->throwError("Failed to assign arg " . $a[1]);
-                                       $value = null; // :(
-                               }
-
-                               $value = $this->reduce($value);
-                               $this->set($a[1], $value);
-                               $assignedValues[] = $value;
-                       } else {
-                               // a lit
-                               $i++;
-                       }
-               }
-
-               // check for a rest
-               $last = end($args);
-               if ($last[0] == "rest") {
-                       $rest = array_slice($orderedValues, count($args) - 1);
-                       $this->set($last[1], $this->reduce(array("list", " ", $rest)));
-               }
-
-               // wow is this the only true use of PHP's + operator for arrays?
-               $this->env->arguments = $assignedValues + $orderedValues;
-       }
-
-       // compile a prop and update $lines or $blocks appropriately
-       protected function compileProp($prop, $block, $out) {
-               // set error position context
-               $this->sourceLoc = isset($prop[-1]) ? $prop[-1] : -1;
-
-               switch ($prop[0]) {
-               case 'assign':
-                       list(, $name, $value) = $prop;
-                       if ($name[0] == $this->vPrefix) {
-                               $this->set($name, $value);
-                       } else {
-                               $out->lines[] = $this->formatter->property($name,
-                                               $this->compileValue($this->reduce($value)));
-                       }
-                       break;
-               case 'block':
-                       list(, $child) = $prop;
-                       $this->compileBlock($child);
-                       break;
-               case 'mixin':
-                       list(, $path, $args, $suffix) = $prop;
-
-                       $orderedArgs = array();
-                       $keywordArgs = array();
-                       foreach ((array)$args as $arg) {
-                               $argval = null;
-                               switch ($arg[0]) {
-                               case "arg":
-                                       if (!isset($arg[2])) {
-                                               $orderedArgs[] = $this->reduce(array("variable", $arg[1]));
-                                       } else {
-                                               $keywordArgs[$arg[1]] = $this->reduce($arg[2]);
-                                       }
-                                       break;
-
-                               case "lit":
-                                       $orderedArgs[] = $this->reduce($arg[1]);
-                                       break;
-                               default:
-                                       $this->throwError("Unknown arg type: " . $arg[0]);
-                               }
-                       }
-
-                       $mixins = $this->findBlocks($block, $path, $orderedArgs, $keywordArgs);
-
-                       if ($mixins === null) {
-                               $this->throwError("{$prop[1][0]} is undefined");
-                       }
-
-                       foreach ($mixins as $mixin) {
-                               if ($mixin === $block && !$orderedArgs) {
-                                       continue;
-                               }
-
-                               $haveScope = false;
-                               if (isset($mixin->parent->scope)) {
-                                       $haveScope = true;
-                                       $mixinParentEnv = $this->pushEnv();
-                                       $mixinParentEnv->storeParent = $mixin->parent->scope;
-                               }
-
-                               $haveArgs = false;
-                               if (isset($mixin->args)) {
-                                       $haveArgs = true;
-                                       $this->pushEnv();
-                                       $this->zipSetArgs($mixin->args, $orderedArgs, $keywordArgs);
-                               }
-
-                               $oldParent = $mixin->parent;
-                               if ($mixin != $block) $mixin->parent = $block;
-
-                               foreach ($this->sortProps($mixin->props) as $subProp) {
-                                       if ($suffix !== null &&
-                                               $subProp[0] == "assign" &&
-                                               is_string($subProp[1]) &&
-                                               $subProp[1]{0} != $this->vPrefix)
-                                       {
-                                               $subProp[2] = array(
-                                                       'list', ' ',
-                                                       array($subProp[2], array('keyword', $suffix))
-                                               );
-                                       }
-
-                                       $this->compileProp($subProp, $mixin, $out);
-                               }
-
-                               $mixin->parent = $oldParent;
-
-                               if ($haveArgs) $this->popEnv();
-                               if ($haveScope) $this->popEnv();
-                       }
-
-                       break;
-               case 'raw':
-                       $out->lines[] = $prop[1];
-                       break;
-               case "directive":
-                       list(, $name, $value) = $prop;
-                       $out->lines[] = "@$name " . $this->compileValue($this->reduce($value)).';';
-                       break;
-               case "comment":
-                       $out->lines[] = $prop[1];
-                       break;
-               case "import";
-                       list(, $importPath, $importId) = $prop;
-                       $importPath = $this->reduce($importPath);
-
-                       if (!isset($this->env->imports)) {
-                               $this->env->imports = array();
-                       }
-
-                       $result = $this->tryImport($importPath, $block, $out);
-
-                       $this->env->imports[$importId] = $result === false ?
-                               array(false, "@import " . $this->compileValue($importPath).";") :
-                               $result;
-
-                       break;
-               case "import_mixin":
-                       list(,$importId) = $prop;
-                       $import = $this->env->imports[$importId];
-                       if ($import[0] === false) {
-                               if (isset($import[1])) {
-                                       $out->lines[] = $import[1];
-                               }
-                       } else {
-                               list(, $bottom, $parser, $importDir) = $import;
-                               $this->compileImportedProps($bottom, $block, $out, $parser, $importDir);
-                       }
-
-                       break;
-               default:
-                       $this->throwError("unknown op: {$prop[0]}\n");
-               }
-       }
-
-
-       /**
-        * Compiles a primitive value into a CSS property value.
-        *
-        * Values in lessphp are typed by being wrapped in arrays, their format is
-        * typically:
-        *
-        *     array(type, contents [, additional_contents]*)
-        *
-        * The input is expected to be reduced. This function will not work on
-        * things like expressions and variables.
-        */
-       public function compileValue($value) {
-               switch ($value[0]) {
-               case 'list':
-                       // [1] - delimiter
-                       // [2] - array of values
-                       return implode($value[1], array_map(array($this, 'compileValue'), $value[2]));
-               case 'raw_color':
-                       if (!empty($this->formatter->compressColors)) {
-                               return $this->compileValue($this->coerceColor($value));
-                       }
-                       return $value[1];
-               case 'keyword':
-                       // [1] - the keyword
-                       return $value[1];
-               case 'number':
-                       list(, $num, $unit) = $value;
-                       // [1] - the number
-                       // [2] - the unit
-                       if ($this->numberPrecision !== null) {
-                               $num = round($num, $this->numberPrecision);
-                       }
-                       return $num . $unit;
-               case 'string':
-                       // [1] - contents of string (includes quotes)
-                       list(, $delim, $content) = $value;
-                       foreach ($content as &$part) {
-                               if (is_array($part)) {
-                                       $part = $this->compileValue($part);
-                               }
-                       }
-                       return $delim . implode($content) . $delim;
-               case 'color':
-                       // [1] - red component (either number or a %)
-                       // [2] - green component
-                       // [3] - blue component
-                       // [4] - optional alpha component
-                       list(, $r, $g, $b) = $value;
-                       $r = round($r);
-                       $g = round($g);
-                       $b = round($b);
-
-                       if (count($value) == 5 && $value[4] != 1) { // rgba
-                               return 'rgba('.$r.','.$g.','.$b.','.$value[4].')';
-                       }
-
-                       $h = sprintf("#%02x%02x%02x", $r, $g, $b);
-
-                       if (!empty($this->formatter->compressColors)) {
-                               // Converting hex color to short notation (e.g. #003399 to #039)
-                               if ($h[1] === $h[2] && $h[3] === $h[4] && $h[5] === $h[6]) {
-                                       $h = '#' . $h[1] . $h[3] . $h[5];
-                               }
-                       }
-
-                       return $h;
-
-               case 'function':
-                       list(, $name, $args) = $value;
-                       return $name.'('.$this->compileValue($args).')';
-               default: // assumed to be unit
-                       $this->throwError("unknown value type: $value[0]");
-               }
-       }
-
-       protected function lib_pow($args) {
-               list($base, $exp) = $this->assertArgs($args, 2, "pow");
-               return pow($this->assertNumber($base), $this->assertNumber($exp));
-       }
-
-       protected function lib_pi() {
-               return pi();
-       }
-
-       protected function lib_mod($args) {
-               list($a, $b) = $this->assertArgs($args, 2, "mod");
-               return $this->assertNumber($a) % $this->assertNumber($b);
-       }
-
-       protected function lib_tan($num) {
-               return tan($this->assertNumber($num));
-       }
-
-       protected function lib_sin($num) {
-               return sin($this->assertNumber($num));
-       }
-
-       protected function lib_cos($num) {
-               return cos($this->assertNumber($num));
-       }
-
-       protected function lib_atan($num) {
-               $num = atan($this->assertNumber($num));
-               return array("number", $num, "rad");
-       }
-
-       protected function lib_asin($num) {
-               $num = asin($this->assertNumber($num));
-               return array("number", $num, "rad");
-       }
-
-       protected function lib_acos($num) {
-               $num = acos($this->assertNumber($num));
-               return array("number", $num, "rad");
-       }
-
-       protected function lib_sqrt($num) {
-               return sqrt($this->assertNumber($num));
-       }
-
-       protected function lib_extract($value) {
-               list($list, $idx) = $this->assertArgs($value, 2, "extract");
-               $idx = $this->assertNumber($idx);
-               // 1 indexed
-               if ($list[0] == "list" && isset($list[2][$idx - 1])) {
-                       return $list[2][$idx - 1];
-               }
-       }
-
-       protected function lib_isnumber($value) {
-               return $this->toBool($value[0] == "number");
-       }
-
-       protected function lib_isstring($value) {
-               return $this->toBool($value[0] == "string");
-       }
-
-       protected function lib_iscolor($value) {
-               return $this->toBool($this->coerceColor($value));
-       }
-
-       protected function lib_iskeyword($value) {
-               return $this->toBool($value[0] == "keyword");
-       }
-
-       protected function lib_ispixel($value) {
-               return $this->toBool($value[0] == "number" && $value[2] == "px");
-       }
-
-       protected function lib_ispercentage($value) {
-               return $this->toBool($value[0] == "number" && $value[2] == "%");
-       }
-
-       protected function lib_isem($value) {
-               return $this->toBool($value[0] == "number" && $value[2] == "em");
-       }
-
-       protected function lib_isrem($value) {
-               return $this->toBool($value[0] == "number" && $value[2] == "rem");
-       }
-
-       protected function lib_rgbahex($color) {
-               $color = $this->coerceColor($color);
-               if (is_null($color))
-                       $this->throwError("color expected for rgbahex");
-
-               return sprintf("#%02x%02x%02x%02x",
-                       isset($color[4]) ? $color[4]*255 : 255,
-                       $color[1],$color[2], $color[3]);
-       }
-
-       protected function lib_argb($color){
-               return $this->lib_rgbahex($color);
-       }
-
-       /**
-        * Given an url, decide whether to output a regular link or the base64-encoded contents of the file
-        *
-        * @param  array  $value either an argument list (two strings) or a single string
-        * @return string        formatted url(), either as a link or base64-encoded
-        */
-       protected function lib_data_uri($value) {
-               $mime = ($value[0] === 'list') ? $value[2][0][2] : null;
-               $url = ($value[0] === 'list') ? $value[2][1][2][0] : $value[2][0];
-
-               $fullpath = $this->findImport($url);
-
-               if($fullpath && ($fsize = filesize($fullpath)) !== false) {
-                       // IE8 can't handle data uris larger than 32KB
-                       if($fsize/1024 < 32) {
-                               if(is_null($mime)) {
-                                       if(class_exists('finfo')) { // php 5.3+
-                                               $finfo = new finfo(FILEINFO_MIME);
-                                               $mime = explode('; ', $finfo->file($fullpath));
-                                               $mime = $mime[0];
-                                       } elseif(function_exists('mime_content_type')) { // PHP 5.2
-                                               $mime = mime_content_type($fullpath);
-                                       }
-                               }
-
-                               if(!is_null($mime)) // fallback if the MIME type is still unknown
-                                       $url = sprintf('data:%s;base64,%s', $mime, base64_encode(file_get_contents($fullpath)));
-                       }
-               }
-
-               return 'url("'.$url.'")';
-       }
-
-       // utility func to unquote a string
-       protected function lib_e($arg) {
-               switch ($arg[0]) {
-                       case "list":
-                               $items = $arg[2];
-                               if (isset($items[0])) {
-                                       return $this->lib_e($items[0]);
-                               }
-                               $this->throwError("unrecognised input");
-                       case "string":
-                               $arg[1] = "";
-                               return $arg;
-                       case "keyword":
-                               return $arg;
-                       default:
-                               return array("keyword", $this->compileValue($arg));
-               }
-       }
-
-       protected function lib__sprintf($args) {
-               if ($args[0] != "list") return $args;
-               $values = $args[2];
-               $string = array_shift($values);
-               $template = $this->compileValue($this->lib_e($string));
-
-               $i = 0;
-               if (preg_match_all('/%[dsa]/', $template, $m)) {
-                       foreach ($m[0] as $match) {
-                               $val = isset($values[$i]) ?
-                                       $this->reduce($values[$i]) : array('keyword', '');
-
-                               // lessjs compat, renders fully expanded color, not raw color
-                               if ($color = $this->coerceColor($val)) {
-                                       $val = $color;
-                               }
-
-                               $i++;
-                               $rep = $this->compileValue($this->lib_e($val));
-                               $template = preg_replace('/'.self::preg_quote($match).'/',
-                                       $rep, $template, 1);
-                       }
-               }
-
-               $d = $string[0] == "string" ? $string[1] : '"';
-               return array("string", $d, array($template));
-       }
-
-       protected function lib_floor($arg) {
-               $value = $this->assertNumber($arg);
-               return array("number", floor($value), $arg[2]);
-       }
-
-       protected function lib_ceil($arg) {
-               $value = $this->assertNumber($arg);
-               return array("number", ceil($value), $arg[2]);
-       }
-
-       protected function lib_round($arg) {
-               if($arg[0] != "list") {
-                       $value = $this->assertNumber($arg);
-                       return array("number", round($value), $arg[2]);
-               } else {
-                       $value = $this->assertNumber($arg[2][0]);
-                       $precision = $this->assertNumber($arg[2][1]);
-                       return array("number", round($value, $precision), $arg[2][0][2]);
-               }
-       }
-
-       protected function lib_unit($arg) {
-               if ($arg[0] == "list") {
-                       list($number, $newUnit) = $arg[2];
-                       return array("number", $this->assertNumber($number),
-                               $this->compileValue($this->lib_e($newUnit)));
-               } else {
-                       return array("number", $this->assertNumber($arg), "");
-               }
-       }
-
-       /**
-        * Helper function to get arguments for color manipulation functions.
-        * takes a list that contains a color like thing and a percentage
-        */
-       public function colorArgs($args) {
-               if ($args[0] != 'list' || count($args[2]) < 2) {
-                       return array(array('color', 0, 0, 0), 0);
-               }
-               list($color, $delta) = $args[2];
-               $color = $this->assertColor($color);
-               $delta = floatval($delta[1]);
-
-               return array($color, $delta);
-       }
-
-       protected function lib_darken($args) {
-               list($color, $delta) = $this->colorArgs($args);
-
-               $hsl = $this->toHSL($color);
-               $hsl[3] = $this->clamp($hsl[3] - $delta, 100);
-               return $this->toRGB($hsl);
-       }
-
-       protected function lib_lighten($args) {
-               list($color, $delta) = $this->colorArgs($args);
-
-               $hsl = $this->toHSL($color);
-               $hsl[3] = $this->clamp($hsl[3] + $delta, 100);
-               return $this->toRGB($hsl);
-       }
-
-       protected function lib_saturate($args) {
-               list($color, $delta) = $this->colorArgs($args);
-
-               $hsl = $this->toHSL($color);
-               $hsl[2] = $this->clamp($hsl[2] + $delta, 100);
-               return $this->toRGB($hsl);
-       }
-
-       protected function lib_desaturate($args) {
-               list($color, $delta) = $this->colorArgs($args);
-
-               $hsl = $this->toHSL($color);
-               $hsl[2] = $this->clamp($hsl[2] - $delta, 100);
-               return $this->toRGB($hsl);
-       }
-
-       protected function lib_spin($args) {
-               list($color, $delta) = $this->colorArgs($args);
-
-               $hsl = $this->toHSL($color);
-
-               $hsl[1] = $hsl[1] + $delta % 360;
-               if ($hsl[1] < 0) $hsl[1] += 360;
-
-               return $this->toRGB($hsl);
-       }
-
-       protected function lib_fadeout($args) {
-               list($color, $delta) = $this->colorArgs($args);
-               $color[4] = $this->clamp((isset($color[4]) ? $color[4] : 1) - $delta/100);
-               return $color;
-       }
-
-       protected function lib_fadein($args) {
-               list($color, $delta) = $this->colorArgs($args);
-               $color[4] = $this->clamp((isset($color[4]) ? $color[4] : 1) + $delta/100);
-               return $color;
-       }
-
-       protected function lib_hue($color) {
-               $hsl = $this->toHSL($this->assertColor($color));
-               return round($hsl[1]);
-       }
-
-       protected function lib_saturation($color) {
-               $hsl = $this->toHSL($this->assertColor($color));
-               return round($hsl[2]);
-       }
-
-       protected function lib_lightness($color) {
-               $hsl = $this->toHSL($this->assertColor($color));
-               return round($hsl[3]);
-       }
-
-       // get the alpha of a color
-       // defaults to 1 for non-colors or colors without an alpha
-       protected function lib_alpha($value) {
-               if (!is_null($color = $this->coerceColor($value))) {
-                       return isset($color[4]) ? $color[4] : 1;
-               }
-       }
-
-       // set the alpha of the color
-       protected function lib_fade($args) {
-               list($color, $alpha) = $this->colorArgs($args);
-               $color[4] = $this->clamp($alpha / 100.0);
-               return $color;
-       }
-
-       protected function lib_percentage($arg) {
-               $num = $this->assertNumber($arg);
-               return array("number", $num*100, "%");
-       }
-
-       // mixes two colors by weight
-       // mix(@color1, @color2, [@weight: 50%]);
-       // http://sass-lang.com/docs/yardoc/Sass/Script/Functions.html#mix-instance_method
-       protected function lib_mix($args) {
-               if ($args[0] != "list" || count($args[2]) < 2)
-                       $this->throwError("mix expects (color1, color2, weight)");
-
-               list($first, $second) = $args[2];
-               $first = $this->assertColor($first);
-               $second = $this->assertColor($second);
-
-               $first_a = $this->lib_alpha($first);
-               $second_a = $this->lib_alpha($second);
-
-               if (isset($args[2][2])) {
-                       $weight = $args[2][2][1] / 100.0;
-               } else {
-                       $weight = 0.5;
-               }
-
-               $w = $weight * 2 - 1;
-               $a = $first_a - $second_a;
-
-               $w1 = (($w * $a == -1 ? $w : ($w + $a)/(1 + $w * $a)) + 1) / 2.0;
-               $w2 = 1.0 - $w1;
-
-               $new = array('color',
-                       $w1 * $first[1] + $w2 * $second[1],
-                       $w1 * $first[2] + $w2 * $second[2],
-                       $w1 * $first[3] + $w2 * $second[3],
-               );
-
-               if ($first_a != 1.0 || $second_a != 1.0) {
-                       $new[] = $first_a * $weight + $second_a * ($weight - 1);
-               }
-
-               return $this->fixColor($new);
-       }
-
-       protected function lib_contrast($args) {
-           $darkColor  = array('color', 0, 0, 0);
-           $lightColor = array('color', 255, 255, 255);
-           $threshold  = 0.43;
-
-           if ( $args[0] == 'list' ) {
-               $inputColor = ( isset($args[2][0]) ) ? $this->assertColor($args[2][0])  : $lightColor;
-               $darkColor  = ( isset($args[2][1]) ) ? $this->assertColor($args[2][1])  : $darkColor;
-               $lightColor = ( isset($args[2][2]) ) ? $this->assertColor($args[2][2])  : $lightColor;
-               $threshold  = ( isset($args[2][3]) ) ? $this->assertNumber($args[2][3]) : $threshold;
-           }
-           else {
-               $inputColor  = $this->assertColor($args);
-           }
-
-           $inputColor = $this->coerceColor($inputColor);
-           $darkColor  = $this->coerceColor($darkColor);
-           $lightColor = $this->coerceColor($lightColor);
-
-           //Figure out which is actually light and dark!
-           if ( $this->lib_luma($darkColor) > $this->lib_luma($lightColor) ) {
-               $t  = $lightColor;
-               $lightColor = $darkColor;
-               $darkColor  = $t;
-           }
-
-           $inputColor_alpha = $this->lib_alpha($inputColor);
-           if ( ( $this->lib_luma($inputColor) * $inputColor_alpha) < $threshold) {
-               return $lightColor;
-           }
-           return $darkColor;
-       }
-
-       protected function lib_luma($color) {
-           $color = $this->coerceColor($color);
-           return (0.2126 * $color[0] / 255) + (0.7152 * $color[1] / 255) + (0.0722 * $color[2] / 255);
-       }
-
-
-       public function assertColor($value, $error = "expected color value") {
-               $color = $this->coerceColor($value);
-               if (is_null($color)) $this->throwError($error);
-               return $color;
-       }
-
-       public function assertNumber($value, $error = "expecting number") {
-               if ($value[0] == "number") return $value[1];
-               $this->throwError($error);
-       }
-
-       public function assertArgs($value, $expectedArgs, $name="") {
-               if ($expectedArgs == 1) {
-                       return $value;
-               } else {
-                       if ($value[0] !== "list" || $value[1] != ",") $this->throwError("expecting list");
-                       $values = $value[2];
-                       $numValues = count($values);
-                       if ($expectedArgs != $numValues) {
-                               if ($name) {
-                                       $name = $name . ": ";
-                               }
-
-                               $this->throwError("${name}expecting $expectedArgs arguments, got $numValues");
-                       }
-
-                       return $values;
-               }
-       }
-
-       protected function toHSL($color) {
-               if ($color[0] == 'hsl') return $color;
-
-               $r = $color[1] / 255;
-               $g = $color[2] / 255;
-               $b = $color[3] / 255;
-
-               $min = min($r, $g, $b);
-               $max = max($r, $g, $b);
-
-               $L = ($min + $max) / 2;
-               if ($min == $max) {
-                       $S = $H = 0;
-               } else {
-                       if ($L < 0.5)
-                               $S = ($max - $min)/($max + $min);
-                       else
-                               $S = ($max - $min)/(2.0 - $max - $min);
-
-                       if ($r == $max) $H = ($g - $b)/($max - $min);
-                       elseif ($g == $max) $H = 2.0 + ($b - $r)/($max - $min);
-                       elseif ($b == $max) $H = 4.0 + ($r - $g)/($max - $min);
-
-               }
-
-               $out = array('hsl',
-                       ($H < 0 ? $H + 6 : $H)*60,
-                       $S*100,
-                       $L*100,
-               );
-
-               if (count($color) > 4) $out[] = $color[4]; // copy alpha
-               return $out;
-       }
-
-       protected function toRGB_helper($comp, $temp1, $temp2) {
-               if ($comp < 0) $comp += 1.0;
-               elseif ($comp > 1) $comp -= 1.0;
-
-               if (6 * $comp < 1) return $temp1 + ($temp2 - $temp1) * 6 * $comp;
-               if (2 * $comp < 1) return $temp2;
-               if (3 * $comp < 2) return $temp1 + ($temp2 - $temp1)*((2/3) - $comp) * 6;
-
-               return $temp1;
-       }
-
-       /**
-        * Converts a hsl array into a color value in rgb.
-        * Expects H to be in range of 0 to 360, S and L in 0 to 100
-        */
-       protected function toRGB($color) {
-               if ($color[0] == 'color') return $color;
-
-               $H = $color[1] / 360;
-               $S = $color[2] / 100;
-               $L = $color[3] / 100;
-
-               if ($S == 0) {
-                       $r = $g = $b = $L;
-               } else {
-                       $temp2 = $L < 0.5 ?
-                               $L*(1.0 + $S) :
-                               $L + $S - $L * $S;
-
-                       $temp1 = 2.0 * $L - $temp2;
-
-                       $r = $this->toRGB_helper($H + 1/3, $temp1, $temp2);
-                       $g = $this->toRGB_helper($H, $temp1, $temp2);
-                       $b = $this->toRGB_helper($H - 1/3, $temp1, $temp2);
-               }
-
-               // $out = array('color', round($r*255), round($g*255), round($b*255));
-               $out = array('color', $r*255, $g*255, $b*255);
-               if (count($color) > 4) $out[] = $color[4]; // copy alpha
-               return $out;
-       }
-
-       protected function clamp($v, $max = 1, $min = 0) {
-               return min($max, max($min, $v));
-       }
-
-       /**
-        * Convert the rgb, rgba, hsl color literals of function type
-        * as returned by the parser into values of color type.
-        */
-       protected function funcToColor($func) {
-               $fname = $func[1];
-               if ($func[2][0] != 'list') return false; // need a list of arguments
-               $rawComponents = $func[2][2];
-
-               if ($fname == 'hsl' || $fname == 'hsla') {
-                       $hsl = array('hsl');
-                       $i = 0;
-                       foreach ($rawComponents as $c) {
-                               $val = $this->reduce($c);
-                               $val = isset($val[1]) ? floatval($val[1]) : 0;
-
-                               if ($i == 0) $clamp = 360;
-                               elseif ($i < 3) $clamp = 100;
-                               else $clamp = 1;
-
-                               $hsl[] = $this->clamp($val, $clamp);
-                               $i++;
-                       }
-
-                       while (count($hsl) < 4) $hsl[] = 0;
-                       return $this->toRGB($hsl);
-
-               } elseif ($fname == 'rgb' || $fname == 'rgba') {
-                       $components = array();
-                       $i = 1;
-                       foreach ($rawComponents as $c) {
-                               $c = $this->reduce($c);
-                               if ($i < 4) {
-                                       if ($c[0] == "number" && $c[2] == "%") {
-                                               $components[] = 255 * ($c[1] / 100);
-                                       } else {
-                                               $components[] = floatval($c[1]);
-                                       }
-                               } elseif ($i == 4) {
-                                       if ($c[0] == "number" && $c[2] == "%") {
-                                               $components[] = 1.0 * ($c[1] / 100);
-                                       } else {
-                                               $components[] = floatval($c[1]);
-                                       }
-                               } else break;
-
-                               $i++;
-                       }
-                       while (count($components) < 3) $components[] = 0;
-                       array_unshift($components, 'color');
-                       return $this->fixColor($components);
-               }
-
-               return false;
-       }
-
-       protected function reduce($value, $forExpression = false) {
-               switch ($value[0]) {
-               case "interpolate":
-                       $reduced = $this->reduce($value[1]);
-                       $var = $this->compileValue($reduced);
-                       $res = $this->reduce(array("variable", $this->vPrefix . $var));
-
-                       if ($res[0] == "raw_color") {
-                               $res = $this->coerceColor($res);
-                       }
-
-                       if (empty($value[2])) $res = $this->lib_e($res);
-
-                       return $res;
-               case "variable":
-                       $key = $value[1];
-                       if (is_array($key)) {
-                               $key = $this->reduce($key);
-                               $key = $this->vPrefix . $this->compileValue($this->lib_e($key));
-                       }
-
-                       $seen =& $this->env->seenNames;
-
-                       if (!empty($seen[$key])) {
-                               $this->throwError("infinite loop detected: $key");
-                       }
-
-                       $seen[$key] = true;
-                       $out = $this->reduce($this->get($key));
-                       $seen[$key] = false;
-                       return $out;
-               case "list":
-                       foreach ($value[2] as &$item) {
-                               $item = $this->reduce($item, $forExpression);
-                       }
-                       return $value;
-               case "expression":
-                       return $this->evaluate($value);
-               case "string":
-                       foreach ($value[2] as &$part) {
-                               if (is_array($part)) {
-                                       $strip = $part[0] == "variable";
-                                       $part = $this->reduce($part);
-                                       if ($strip) $part = $this->lib_e($part);
-                               }
-                       }
-                       return $value;
-               case "escape":
-                       list(,$inner) = $value;
-                       return $this->lib_e($this->reduce($inner));
-               case "function":
-                       $color = $this->funcToColor($value);
-                       if ($color) return $color;
-
-                       list(, $name, $args) = $value;
-                       if ($name == "%") $name = "_sprintf";
-
-                       $f = isset($this->libFunctions[$name]) ?
-                               $this->libFunctions[$name] : array($this, 'lib_'.str_replace('-', '_', $name));
-
-                       if (is_callable($f)) {
-                               if ($args[0] == 'list')
-                                       $args = self::compressList($args[2], $args[1]);
-
-                               $ret = call_user_func($f, $this->reduce($args, true), $this);
-
-                               if (is_null($ret)) {
-                                       return array("string", "", array(
-                                               $name, "(", $args, ")"
-                                       ));
-                               }
-
-                               // convert to a typed value if the result is a php primitive
-                               if (is_numeric($ret)) $ret = array('number', $ret, "");
-                               elseif (!is_array($ret)) $ret = array('keyword', $ret);
-
-                               return $ret;
-                       }
-
-                       // plain function, reduce args
-                       $value[2] = $this->reduce($value[2]);
-                       return $value;
-               case "unary":
-                       list(, $op, $exp) = $value;
-                       $exp = $this->reduce($exp);
-
-                       if ($exp[0] == "number") {
-                               switch ($op) {
-                               case "+":
-                                       return $exp;
-                               case "-":
-                                       $exp[1] *= -1;
-                                       return $exp;
-                               }
-                       }
-                       return array("string", "", array($op, $exp));
-               }
-
-               if ($forExpression) {
-                       switch ($value[0]) {
-                       case "keyword":
-                               if ($color = $this->coerceColor($value)) {
-                                       return $color;
-                               }
-                               break;
-                       case "raw_color":
-                               return $this->coerceColor($value);
-                       }
-               }
-
-               return $value;
-       }
-
-
-       // coerce a value for use in color operation
-       protected function coerceColor($value) {
-               switch($value[0]) {
-                       case 'color': return $value;
-                       case 'raw_color':
-                               $c = array("color", 0, 0, 0);
-                               $colorStr = substr($value[1], 1);
-                               $num = hexdec($colorStr);
-                               $width = strlen($colorStr) == 3 ? 16 : 256;
-
-                               for ($i = 3; $i > 0; $i--) { // 3 2 1
-                                       $t = $num % $width;
-                                       $num /= $width;
-
-                                       $c[$i] = $t * (256/$width) + $t * floor(16/$width);
-                               }
-
-                               return $c;
-                       case 'keyword':
-                               $name = $value[1];
-                               if (isset(self::$cssColors[$name])) {
-                                       $rgba = explode(',', self::$cssColors[$name]);
-
-                                       if(isset($rgba[3]))
-                                               return array('color', $rgba[0], $rgba[1], $rgba[2], $rgba[3]);
-
-                                       return array('color', $rgba[0], $rgba[1], $rgba[2]);
-                               }
-                               return null;
-               }
-       }
-
-       // make something string like into a string
-       protected function coerceString($value) {
-               switch ($value[0]) {
-               case "string":
-                       return $value;
-               case "keyword":
-                       return array("string", "", array($value[1]));
-               }
-               return null;
-       }
-
-       // turn list of length 1 into value type
-       protected function flattenList($value) {
-               if ($value[0] == "list" && count($value[2]) == 1) {
-                       return $this->flattenList($value[2][0]);
-               }
-               return $value;
-       }
-
-       public function toBool($a) {
-               if ($a) return self::$TRUE;
-               else return self::$FALSE;
-       }
-
-       // evaluate an expression
-       protected function evaluate($exp) {
-               list(, $op, $left, $right, $whiteBefore, $whiteAfter) = $exp;
-
-               $left = $this->reduce($left, true);
-               $right = $this->reduce($right, true);
-
-               if ($leftColor = $this->coerceColor($left)) {
-                       $left = $leftColor;
-               }
-
-               if ($rightColor = $this->coerceColor($right)) {
-                       $right = $rightColor;
-               }
-
-               $ltype = $left[0];
-               $rtype = $right[0];
-
-               // operators that work on all types
-               if ($op == "and") {
-                       return $this->toBool($left == self::$TRUE && $right == self::$TRUE);
-               }
-
-               if ($op == "=") {
-                       return $this->toBool($this->eq($left, $right) );
-               }
-
-               if ($op == "+" && !is_null($str = $this->stringConcatenate($left, $right))) {
-                       return $str;
-               }
-
-               // type based operators
-               $fname = "op_${ltype}_${rtype}";
-               if (is_callable(array($this, $fname))) {
-                       $out = $this->$fname($op, $left, $right);
-                       if (!is_null($out)) return $out;
-               }
-
-               // make the expression look it did before being parsed
-               $paddedOp = $op;
-               if ($whiteBefore) $paddedOp = " " . $paddedOp;
-               if ($whiteAfter) $paddedOp .= " ";
-
-               return array("string", "", array($left, $paddedOp, $right));
-       }
-
-       protected function stringConcatenate($left, $right) {
-               if ($strLeft = $this->coerceString($left)) {
-                       if ($right[0] == "string") {
-                               $right[1] = "";
-                       }
-                       $strLeft[2][] = $right;
-                       return $strLeft;
-               }
-
-               if ($strRight = $this->coerceString($right)) {
-                       array_unshift($strRight[2], $left);
-                       return $strRight;
-               }
-       }
-
-
-       // make sure a color's components don't go out of bounds
-       protected function fixColor($c) {
-               foreach (range(1, 3) as $i) {
-                       if ($c[$i] < 0) $c[$i] = 0;
-                       if ($c[$i] > 255) $c[$i] = 255;
-               }
-
-               return $c;
-       }
-
-       protected function op_number_color($op, $lft, $rgt) {
-               if ($op == '+' || $op == '*') {
-                       return $this->op_color_number($op, $rgt, $lft);
-               }
-       }
-
-       protected function op_color_number($op, $lft, $rgt) {
-               if ($rgt[0] == '%') $rgt[1] /= 100;
-
-               return $this->op_color_color($op, $lft,
-                       array_fill(1, count($lft) - 1, $rgt[1]));
-       }
-
-       protected function op_color_color($op, $left, $right) {
-               $out = array('color');
-               $max = count($left) > count($right) ? count($left) : count($right);
-               foreach (range(1, $max - 1) as $i) {
-                       $lval = isset($left[$i]) ? $left[$i] : 0;
-                       $rval = isset($right[$i]) ? $right[$i] : 0;
-                       switch ($op) {
-                       case '+':
-                               $out[] = $lval + $rval;
-                               break;
-                       case '-':
-                               $out[] = $lval - $rval;
-                               break;
-                       case '*':
-                               $out[] = $lval * $rval;
-                               break;
-                       case '%':
-                               $out[] = $lval % $rval;
-                               break;
-                       case '/':
-                               if ($rval == 0) $this->throwError("evaluate error: can't divide by zero");
-                               $out[] = $lval / $rval;
-                               break;
-                       default:
-                               $this->throwError('evaluate error: color op number failed on op '.$op);
-                       }
-               }
-               return $this->fixColor($out);
-       }
-
-       function lib_red($color){
-               $color = $this->coerceColor($color);
-               if (is_null($color)) {
-                       $this->throwError('color expected for red()');
-               }
-
-               return $color[1];
-       }
-
-       function lib_green($color){
-               $color = $this->coerceColor($color);
-               if (is_null($color)) {
-                       $this->throwError('color expected for green()');
-               }
-
-               return $color[2];
-       }
-
-       function lib_blue($color){
-               $color = $this->coerceColor($color);
-               if (is_null($color)) {
-                       $this->throwError('color expected for blue()');
-               }
-
-               return $color[3];
-       }
-
-
-       // operator on two numbers
-       protected function op_number_number($op, $left, $right) {
-               $unit = empty($left[2]) ? $right[2] : $left[2];
-
-               $value = 0;
-               switch ($op) {
-               case '+':
-                       $value = $left[1] + $right[1];
-                       break;
-               case '*':
-                       $value = $left[1] * $right[1];
-                       break;
-               case '-':
-                       $value = $left[1] - $right[1];
-                       break;
-               case '%':
-                       $value = $left[1] % $right[1];
-                       break;
-               case '/':
-                       if ($right[1] == 0) $this->throwError('parse error: divide by zero');
-                       $value = $left[1] / $right[1];
-                       break;
-               case '<':
-                       return $this->toBool($left[1] < $right[1]);
-               case '>':
-                       return $this->toBool($left[1] > $right[1]);
-               case '>=':
-                       return $this->toBool($left[1] >= $right[1]);
-               case '=<':
-                       return $this->toBool($left[1] <= $right[1]);
-               default:
-                       $this->throwError('parse error: unknown number operator: '.$op);
-               }
-
-               return array("number", $value, $unit);
-       }
-
-
-       /* environment functions */
-
-       protected function makeOutputBlock($type, $selectors = null) {
-               $b = new stdclass;
-               $b->lines = array();
-               $b->children = array();
-               $b->selectors = $selectors;
-               $b->type = $type;
-               $b->parent = $this->scope;
-               return $b;
-       }
-
-       // the state of execution
-       protected function pushEnv($block = null) {
-               $e = new stdclass;
-               $e->parent = $this->env;
-               $e->store = array();
-               $e->block = $block;
-
-               $this->env = $e;
-               return $e;
-       }
-
-       // pop something off the stack
-       protected function popEnv() {
-               $old = $this->env;
-               $this->env = $this->env->parent;
-               return $old;
-       }
-
-       // set something in the current env
-       protected function set($name, $value) {
-               $this->env->store[$name] = $value;
-       }
-
-
-       // get the highest occurrence entry for a name
-       protected function get($name) {
-               $current = $this->env;
-
-               $isArguments = $name == $this->vPrefix . 'arguments';
-               while ($current) {
-                       if ($isArguments && isset($current->arguments)) {
-                               return array('list', ' ', $current->arguments);
-                       }
-
-                       if (isset($current->store[$name]))
-                               return $current->store[$name];
-                       else {
-                               $current = isset($current->storeParent) ?
-                                       $current->storeParent : $current->parent;
-                       }
-               }
-
-               $this->throwError("variable $name is undefined");
-       }
-
-       // inject array of unparsed strings into environment as variables
-       protected function injectVariables($args) {
-               $this->pushEnv();
-               $parser = new lessc_parser($this, __METHOD__);
-               foreach ($args as $name => $strValue) {
-                       if ($name{0} != '@') $name = '@'.$name;
-                       $parser->count = 0;
-                       $parser->buffer = (string)$strValue;
-                       if (!$parser->propertyValue($value)) {
-                               throw new Exception("failed to parse passed in variable $name: $strValue");
-                       }
-
-                       $this->set($name, $value);
-               }
-       }
-
-       /**
-        * Initialize any static state, can initialize parser for a file
-        * $opts isn't used yet
-        */
-       public function __construct($fname = null) {
-               if ($fname !== null) {
-                       // used for deprecated parse method
-                       $this->_parseFile = $fname;
-               }
-       }
-
-       public function compile($string, $name = null) {
-               $locale = setlocale(LC_NUMERIC, 0);
-               setlocale(LC_NUMERIC, "C");
-
-               $this->parser = $this->makeParser($name);
-               $root = $this->parser->parse($string);
-
-               $this->env = null;
-               $this->scope = null;
-
-               $this->formatter = $this->newFormatter();
-
-               if (!empty($this->registeredVars)) {
-                       $this->injectVariables($this->registeredVars);
-               }
-
-               $this->sourceParser = $this->parser; // used for error messages
-               $this->compileBlock($root);
-
-               ob_start();
-               $this->formatter->block($this->scope);
-               $out = ob_get_clean();
-               setlocale(LC_NUMERIC, $locale);
-               return $out;
-       }
-
-       public function compileFile($fname, $outFname = null) {
-               if (!is_readable($fname)) {
-                       throw new Exception('load error: failed to find '.$fname);
-               }
-
-               $pi = pathinfo($fname);
-
-               $oldImport = $this->importDir;
-
-               $this->importDir = (array)$this->importDir;
-               $this->importDir[] = $pi['dirname'].'/';
-
-               $this->addParsedFile($fname);
-
-               $out = $this->compile(file_get_contents($fname), $fname);
-
-               $this->importDir = $oldImport;
-
-               if ($outFname !== null) {
-                       return file_put_contents($outFname, $out);
-               }
-
-               return $out;
-       }
-
-       // compile only if changed input has changed or output doesn't exist
-       public function checkedCompile($in, $out) {
-               if (!is_file($out) || filemtime($in) > filemtime($out)) {
-                       $this->compileFile($in, $out);
-                       return true;
-               }
-               return false;
-       }
-
-       /**
-        * Execute lessphp on a .less file or a lessphp cache structure
-        *
-        * The lessphp cache structure contains information about a specific
-        * less file having been parsed. It can be used as a hint for future
-        * calls to determine whether or not a rebuild is required.
-        *
-        * The cache structure contains two important keys that may be used
-        * externally:
-        *
-        * compiled: The final compiled CSS
-        * updated: The time (in seconds) the CSS was last compiled
-        *
-        * The cache structure is a plain-ol' PHP associative array and can
-        * be serialized and unserialized without a hitch.
-        *
-        * @param mixed $in Input
-        * @param bool $force Force rebuild?
-        * @return array lessphp cache structure
-        */
-       public function cachedCompile($in, $force = false) {
-               // assume no root
-               $root = null;
-
-               if (is_string($in)) {
-                       $root = $in;
-               } elseif (is_array($in) and isset($in['root'])) {
-                       if ($force or ! isset($in['files'])) {
-                               // If we are forcing a recompile or if for some reason the
-                               // structure does not contain any file information we should
-                               // specify the root to trigger a rebuild.
-                               $root = $in['root'];
-                       } elseif (isset($in['files']) and is_array($in['files'])) {
-                               foreach ($in['files'] as $fname => $ftime ) {
-                                       if (!file_exists($fname) or filemtime($fname) > $ftime) {
-                                               // One of the files we knew about previously has changed
-                                               // so we should look at our incoming root again.
-                                               $root = $in['root'];
-                                               break;
-                                       }
-                               }
-                       }
-               } else {
-                       // TODO: Throw an exception? We got neither a string nor something
-                       // that looks like a compatible lessphp cache structure.
-                       return null;
-               }
-
-               if ($root !== null) {
-                       // If we have a root value which means we should rebuild.
-                       $out = array();
-                       $out['root'] = $root;
-                       $out['compiled'] = $this->compileFile($root);
-                       $out['files'] = $this->allParsedFiles();
-                       $out['updated'] = time();
-                       return $out;
-               } else {
-                       // No changes, pass back the structure
-                       // we were given initially.
-                       return $in;
-               }
-
-       }
-
-       // parse and compile buffer
-       // This is deprecated
-       public function parse($str = null, $initialVariables = null) {
-               if (is_array($str)) {
-                       $initialVariables = $str;
-                       $str = null;
-               }
-
-               $oldVars = $this->registeredVars;
-               if ($initialVariables !== null) {
-                       $this->setVariables($initialVariables);
-               }
-
-               if ($str == null) {
-                       if (empty($this->_parseFile)) {
-                               throw new exception("nothing to parse");
-                       }
-
-                       $out = $this->compileFile($this->_parseFile);
-               } else {
-                       $out = $this->compile($str);
-               }
-
-               $this->registeredVars = $oldVars;
-               return $out;
-       }
-
-       protected function makeParser($name) {
-               $parser = new lessc_parser($this, $name);
-               $parser->writeComments = $this->preserveComments;
-
-               return $parser;
-       }
-
-       public function setFormatter($name) {
-               $this->formatterName = $name;
-       }
-
-       protected function newFormatter() {
-               $className = "lessc_formatter_lessjs";
-               if (!empty($this->formatterName)) {
-                       if (!is_string($this->formatterName))
-                               return $this->formatterName;
-                       $className = "lessc_formatter_$this->formatterName";
-               }
-
-               return new $className;
-       }
-
-       public function setPreserveComments($preserve) {
-               $this->preserveComments = $preserve;
-       }
-
-       public function registerFunction($name, $func) {
-               $this->libFunctions[$name] = $func;
-       }
-
-       public function unregisterFunction($name) {
-               unset($this->libFunctions[$name]);
-       }
-
-       public function setVariables($variables) {
-               $this->registeredVars = array_merge($this->registeredVars, $variables);
-       }
-
-       public function unsetVariable($name) {
-               unset($this->registeredVars[$name]);
-       }
-
-       public function setImportDir($dirs) {
-               $this->importDir = (array)$dirs;
-       }
-
-       public function addImportDir($dir) {
-               $this->importDir = (array)$this->importDir;
-               $this->importDir[] = $dir;
-       }
-
-       public function allParsedFiles() {
-               return $this->allParsedFiles;
-       }
-
-       public function addParsedFile($file) {
-               $this->allParsedFiles[realpath($file)] = filemtime($file);
-       }
-
-       /**
-        * Uses the current value of $this->count to show line and line number
-        */
-       public function throwError($msg = null) {
-               if ($this->sourceLoc >= 0) {
-                       $this->sourceParser->throwError($msg, $this->sourceLoc);
-               }
-               throw new exception($msg);
-       }
-
-       // compile file $in to file $out if $in is newer than $out
-       // returns true when it compiles, false otherwise
-       public static function ccompile($in, $out, $less = null) {
-               if ($less === null) {
-                       $less = new self;
-               }
-               return $less->checkedCompile($in, $out);
-       }
-
-       public static function cexecute($in, $force = false, $less = null) {
-               if ($less === null) {
-                       $less = new self;
-               }
-               return $less->cachedCompile($in, $force);
-       }
-
-       static protected $cssColors = array(
-               'aliceblue' => '240,248,255',
-               'antiquewhite' => '250,235,215',
-               'aqua' => '0,255,255',
-               'aquamarine' => '127,255,212',
-               'azure' => '240,255,255',
-               'beige' => '245,245,220',
-               'bisque' => '255,228,196',
-               'black' => '0,0,0',
-               'blanchedalmond' => '255,235,205',
-               'blue' => '0,0,255',
-               'blueviolet' => '138,43,226',
-               'brown' => '165,42,42',
-               'burlywood' => '222,184,135',
-               'cadetblue' => '95,158,160',
-               'chartreuse' => '127,255,0',
-               'chocolate' => '210,105,30',
-               'coral' => '255,127,80',
-               'cornflowerblue' => '100,149,237',
-               'cornsilk' => '255,248,220',
-               'crimson' => '220,20,60',
-               'cyan' => '0,255,255',
-               'darkblue' => '0,0,139',
-               'darkcyan' => '0,139,139',
-               'darkgoldenrod' => '184,134,11',
-               'darkgray' => '169,169,169',
-               'darkgreen' => '0,100,0',
-               'darkgrey' => '169,169,169',
-               'darkkhaki' => '189,183,107',
-               'darkmagenta' => '139,0,139',
-               'darkolivegreen' => '85,107,47',
-               'darkorange' => '255,140,0',
-               'darkorchid' => '153,50,204',
-               'darkred' => '139,0,0',
-               'darksalmon' => '233,150,122',
-               'darkseagreen' => '143,188,143',
-               'darkslateblue' => '72,61,139',
-               'darkslategray' => '47,79,79',
-               'darkslategrey' => '47,79,79',
-               'darkturquoise' => '0,206,209',
-               'darkviolet' => '148,0,211',
-               'deeppink' => '255,20,147',
-               'deepskyblue' => '0,191,255',
-               'dimgray' => '105,105,105',
-               'dimgrey' => '105,105,105',
-               'dodgerblue' => '30,144,255',
-               'firebrick' => '178,34,34',
-               'floralwhite' => '255,250,240',
-               'forestgreen' => '34,139,34',
-               'fuchsia' => '255,0,255',
-               'gainsboro' => '220,220,220',
-               'ghostwhite' => '248,248,255',
-               'gold' => '255,215,0',
-               'goldenrod' => '218,165,32',
-               'gray' => '128,128,128',
-               'green' => '0,128,0',
-               'greenyellow' => '173,255,47',
-               'grey' => '128,128,128',
-               'honeydew' => '240,255,240',
-               'hotpink' => '255,105,180',
-               'indianred' => '205,92,92',
-               'indigo' => '75,0,130',
-               'ivory' => '255,255,240',
-               'khaki' => '240,230,140',
-               'lavender' => '230,230,250',
-               'lavenderblush' => '255,240,245',
-               'lawngreen' => '124,252,0',
-               'lemonchiffon' => '255,250,205',
-               'lightblue' => '173,216,230',
-               'lightcoral' => '240,128,128',
-               'lightcyan' => '224,255,255',
-               'lightgoldenrodyellow' => '250,250,210',
-               'lightgray' => '211,211,211',
-               'lightgreen' => '144,238,144',
-               'lightgrey' => '211,211,211',
-               'lightpink' => '255,182,193',
-               'lightsalmon' => '255,160,122',
-               'lightseagreen' => '32,178,170',
-               'lightskyblue' => '135,206,250',
-               'lightslategray' => '119,136,153',
-               'lightslategrey' => '119,136,153',
-               'lightsteelblue' => '176,196,222',
-               'lightyellow' => '255,255,224',
-               'lime' => '0,255,0',
-               'limegreen' => '50,205,50',
-               'linen' => '250,240,230',
-               'magenta' => '255,0,255',
-               'maroon' => '128,0,0',
-               'mediumaquamarine' => '102,205,170',
-               'mediumblue' => '0,0,205',
-               'mediumorchid' => '186,85,211',
-               'mediumpurple' => '147,112,219',
-               'mediumseagreen' => '60,179,113',
-               'mediumslateblue' => '123,104,238',
-               'mediumspringgreen' => '0,250,154',
-               'mediumturquoise' => '72,209,204',
-               'mediumvioletred' => '199,21,133',
-               'midnightblue' => '25,25,112',
-               'mintcream' => '245,255,250',
-               'mistyrose' => '255,228,225',
-               'moccasin' => '255,228,181',
-               'navajowhite' => '255,222,173',
-               'navy' => '0,0,128',
-               'oldlace' => '253,245,230',
-               'olive' => '128,128,0',
-               'olivedrab' => '107,142,35',
-               'orange' => '255,165,0',
-               'orangered' => '255,69,0',
-               'orchid' => '218,112,214',
-               'palegoldenrod' => '238,232,170',
-               'palegreen' => '152,251,152',
-               'paleturquoise' => '175,238,238',
-               'palevioletred' => '219,112,147',
-               'papayawhip' => '255,239,213',
-               'peachpuff' => '255,218,185',
-               'peru' => '205,133,63',
-               'pink' => '255,192,203',
-               'plum' => '221,160,221',
-               'powderblue' => '176,224,230',
-               'purple' => '128,0,128',
-               'red' => '255,0,0',
-               'rosybrown' => '188,143,143',
-               'royalblue' => '65,105,225',
-               'saddlebrown' => '139,69,19',
-               'salmon' => '250,128,114',
-               'sandybrown' => '244,164,96',
-               'seagreen' => '46,139,87',
-               'seashell' => '255,245,238',
-               'sienna' => '160,82,45',
-               'silver' => '192,192,192',
-               'skyblue' => '135,206,235',
-               'slateblue' => '106,90,205',
-               'slategray' => '112,128,144',
-               'slategrey' => '112,128,144',
-               'snow' => '255,250,250',
-               'springgreen' => '0,255,127',
-               'steelblue' => '70,130,180',
-               'tan' => '210,180,140',
-               'teal' => '0,128,128',
-               'thistle' => '216,191,216',
-               'tomato' => '255,99,71',
-               'transparent' => '0,0,0,0',
-               'turquoise' => '64,224,208',
-               'violet' => '238,130,238',
-               'wheat' => '245,222,179',
-               'white' => '255,255,255',
-               'whitesmoke' => '245,245,245',
-               'yellow' => '255,255,0',
-               'yellowgreen' => '154,205,50'
-       );
-}
-
-// responsible for taking a string of LESS code and converting it into a
-// syntax tree
-class lessc_parser {
-       static protected $nextBlockId = 0; // used to uniquely identify blocks
-
-       static protected $precedence = array(
-               '=<' => 0,
-               '>=' => 0,
-               '=' => 0,
-               '<' => 0,
-               '>' => 0,
-
-               '+' => 1,
-               '-' => 1,
-               '*' => 2,
-               '/' => 2,
-               '%' => 2,
-       );
-
-       static protected $whitePattern;
-       static protected $commentMulti;
-
-       static protected $commentSingle = "//";
-       static protected $commentMultiLeft = "/*";
-       static protected $commentMultiRight = "*/";
-
-       // regex string to match any of the operators
-       static protected $operatorString;
-
-       // these properties will supress division unless it's inside parenthases
-       static protected $supressDivisionProps =
-               array('/border-radius$/i', '/^font$/i');
-
-       protected $blockDirectives = array("font-face", "keyframes", "page", "-moz-document", "viewport", "-moz-viewport", "-o-viewport", "-ms-viewport");
-       protected $lineDirectives = array("charset");
-
-       /**
-        * if we are in parens we can be more liberal with whitespace around
-        * operators because it must evaluate to a single value and thus is less
-        * ambiguous.
-        *
-        * Consider:
-        *     property1: 10 -5; // is two numbers, 10 and -5
-        *     property2: (10 -5); // should evaluate to 5
-        */
-       protected $inParens = false;
-
-       // caches preg escaped literals
-       static protected $literalCache = array();
-
-       public function __construct($lessc, $sourceName = null) {
-               $this->eatWhiteDefault = true;
-               // reference to less needed for vPrefix, mPrefix, and parentSelector
-               $this->lessc = $lessc;
-
-               $this->sourceName = $sourceName; // name used for error messages
-
-               $this->writeComments = false;
-
-               if (!self::$operatorString) {
-                       self::$operatorString =
-                               '('.implode('|', array_map(array('lessc', 'preg_quote'),
-                                       array_keys(self::$precedence))).')';
-
-                       $commentSingle = lessc::preg_quote(self::$commentSingle);
-                       $commentMultiLeft = lessc::preg_quote(self::$commentMultiLeft);
-                       $commentMultiRight = lessc::preg_quote(self::$commentMultiRight);
-
-                       self::$commentMulti = $commentMultiLeft.'.*?'.$commentMultiRight;
-                       self::$whitePattern = '/'.$commentSingle.'[^\n]*\s*|('.self::$commentMulti.')\s*|\s+/Ais';
-               }
-       }
-
-       public function parse($buffer) {
-               $this->count = 0;
-               $this->line = 1;
-
-               $this->env = null; // block stack
-               $this->buffer = $this->writeComments ? $buffer : $this->removeComments($buffer);
-               $this->pushSpecialBlock("root");
-               $this->eatWhiteDefault = true;
-               $this->seenComments = array();
-
-               // trim whitespace on head
-               // if (preg_match('/^\s+/', $this->buffer, $m)) {
-               //      $this->line += substr_count($m[0], "\n");
-               //      $this->buffer = ltrim($this->buffer);
-               // }
-               $this->whitespace();
-
-               // parse the entire file
-               while (false !== $this->parseChunk());
-
-               if ($this->count != strlen($this->buffer))
-                       $this->throwError();
-
-               // TODO report where the block was opened
-               if ( !property_exists($this->env, 'parent') || !is_null($this->env->parent) )
-                       throw new exception('parse error: unclosed block');
-
-               return $this->env;
-       }
-
-       /**
-        * Parse a single chunk off the head of the buffer and append it to the
-        * current parse environment.
-        * Returns false when the buffer is empty, or when there is an error.
-        *
-        * This function is called repeatedly until the entire document is
-        * parsed.
-        *
-        * This parser is most similar to a recursive descent parser. Single
-        * functions represent discrete grammatical rules for the language, and
-        * they are able to capture the text that represents those rules.
-        *
-        * Consider the function lessc::keyword(). (all parse functions are
-        * structured the same)
-        *
-        * The function takes a single reference argument. When calling the
-        * function it will attempt to match a keyword on the head of the buffer.
-        * If it is successful, it will place the keyword in the referenced
-        * argument, advance the position in the buffer, and return true. If it
-        * fails then it won't advance the buffer and it will return false.
-        *
-        * All of these parse functions are powered by lessc::match(), which behaves
-        * the same way, but takes a literal regular expression. Sometimes it is
-        * more convenient to use match instead of creating a new function.
-        *
-        * Because of the format of the functions, to parse an entire string of
-        * grammatical rules, you can chain them together using &&.
-        *
-        * But, if some of the rules in the chain succeed before one fails, then
-        * the buffer position will be left at an invalid state. In order to
-        * avoid this, lessc::seek() is used to remember and set buffer positions.
-        *
-        * Before parsing a chain, use $s = $this->seek() to remember the current
-        * position into $s. Then if a chain fails, use $this->seek($s) to
-        * go back where we started.
-        */
-       protected function parseChunk() {
-               if (empty($this->buffer)) return false;
-               $s = $this->seek();
-
-               if ($this->whitespace()) {
-                       return true;
-               }
-
-               // setting a property
-               if ($this->keyword($key) && $this->assign() &&
-                       $this->propertyValue($value, $key) && $this->end())
-               {
-                       $this->append(array('assign', $key, $value), $s);
-                       return true;
-               } else {
-                       $this->seek($s);
-               }
-
-
-               // look for special css blocks
-               if ($this->literal('@', false)) {
-                       $this->count--;
-
-                       // media
-                       if ($this->literal('@media')) {
-                               if (($this->mediaQueryList($mediaQueries) || true)
-                                       && $this->literal('{'))
-                               {
-                                       $media = $this->pushSpecialBlock("media");
-                                       $media->queries = is_null($mediaQueries) ? array() : $mediaQueries;
-                                       return true;
-                               } else {
-                                       $this->seek($s);
-                                       return false;
-                               }
-                       }
-
-                       if ($this->literal("@", false) && $this->keyword($dirName)) {
-                               if ($this->isDirective($dirName, $this->blockDirectives)) {
-                                       if (($this->openString("{", $dirValue, null, array(";")) || true) &&
-                                               $this->literal("{"))
-                                       {
-                                               $dir = $this->pushSpecialBlock("directive");
-                                               $dir->name = $dirName;
-                                               if (isset($dirValue)) $dir->value = $dirValue;
-                                               return true;
-                                       }
-                               } elseif ($this->isDirective($dirName, $this->lineDirectives)) {
-                                       if ($this->propertyValue($dirValue) && $this->end()) {
-                                               $this->append(array("directive", $dirName, $dirValue));
-                                               return true;
-                                       }
-                               }
-                       }
-
-                       $this->seek($s);
-               }
-
-               // setting a variable
-               if ($this->variable($var) && $this->assign() &&
-                       $this->propertyValue($value) && $this->end())
-               {
-                       $this->append(array('assign', $var, $value), $s);
-                       return true;
-               } else {
-                       $this->seek($s);
-               }
-
-               if ($this->import($importValue)) {
-                       $this->append($importValue, $s);
-                       return true;
-               }
-
-               // opening parametric mixin
-               if ($this->tag($tag, true) && $this->argumentDef($args, $isVararg) &&
-                       ($this->guards($guards) || true) &&
-                       $this->literal('{'))
-               {
-                       $block = $this->pushBlock($this->fixTags(array($tag)));
-                       $block->args = $args;
-                       $block->isVararg = $isVararg;
-                       if (!empty($guards)) $block->guards = $guards;
-                       return true;
-               } else {
-                       $this->seek($s);
-               }
-
-               // opening a simple block
-               if ($this->tags($tags) && $this->literal('{', false)) {
-                       $tags = $this->fixTags($tags);
-                       $this->pushBlock($tags);
-                       return true;
-               } else {
-                       $this->seek($s);
-               }
-
-               // closing a block
-               if ($this->literal('}', false)) {
-                       try {
-                               $block = $this->pop();
-                       } catch (exception $e) {
-                               $this->seek($s);
-                               $this->throwError($e->getMessage());
-                       }
-
-                       $hidden = false;
-                       if (is_null($block->type)) {
-                               $hidden = true;
-                               if (!isset($block->args)) {
-                                       foreach ($block->tags as $tag) {
-                                               if (!is_string($tag) || $tag{0} != $this->lessc->mPrefix) {
-                                                       $hidden = false;
-                                                       break;
-                                               }
-                                       }
-                               }
-
-                               foreach ($block->tags as $tag) {
-                                       if (is_string($tag)) {
-                                               $this->env->children[$tag][] = $block;
-                                       }
-                               }
-                       }
-
-                       if (!$hidden) {
-                               $this->append(array('block', $block), $s);
-                       }
-
-                       // this is done here so comments aren't bundled into he block that
-                       // was just closed
-                       $this->whitespace();
-                       return true;
-               }
-
-               // mixin
-               if ($this->mixinTags($tags) &&
-                       ($this->argumentDef($argv, $isVararg) || true) &&
-                       ($this->keyword($suffix) || true) && $this->end())
-               {
-                       $tags = $this->fixTags($tags);
-                       $this->append(array('mixin', $tags, $argv, $suffix), $s);
-                       return true;
-               } else {
-                       $this->seek($s);
-               }
-
-               // spare ;
-               if ($this->literal(';')) return true;
-
-               return false; // got nothing, throw error
-       }
-
-       protected function isDirective($dirname, $directives) {
-               // TODO: cache pattern in parser
-               $pattern = implode("|",
-                       array_map(array("lessc", "preg_quote"), $directives));
-               $pattern = '/^(-[a-z-]+-)?(' . $pattern . ')$/i';
-
-               return preg_match($pattern, $dirname);
-       }
-
-       protected function fixTags($tags) {
-               // move @ tags out of variable namespace
-               foreach ($tags as &$tag) {
-                       if ($tag{0} == $this->lessc->vPrefix)
-                               $tag[0] = $this->lessc->mPrefix;
-               }
-               return $tags;
-       }
-
-       // a list of expressions
-       protected function expressionList(&$exps) {
-               $values = array();
-
-               while ($this->expression($exp)) {
-                       $values[] = $exp;
-               }
-
-               if (count($values) == 0) return false;
-
-               $exps = lessc::compressList($values, ' ');
-               return true;
-       }
-
-       /**
-        * Attempt to consume an expression.
-        * @link http://en.wikipedia.org/wiki/Operator-precedence_parser#Pseudo-code
-        */
-       protected function expression(&$out) {
-               if ($this->value($lhs)) {
-                       $out = $this->expHelper($lhs, 0);
-
-                       // look for / shorthand
-                       if (!empty($this->env->supressedDivision)) {
-                               unset($this->env->supressedDivision);
-                               $s = $this->seek();
-                               if ($this->literal("/") && $this->value($rhs)) {
-                                       $out = array("list", "",
-                                               array($out, array("keyword", "/"), $rhs));
-                               } else {
-                                       $this->seek($s);
-                               }
-                       }
-
-                       return true;
-               }
-               return false;
-       }
-
-       /**
-        * recursively parse infix equation with $lhs at precedence $minP
-        */
-       protected function expHelper($lhs, $minP) {
-               $this->inExp = true;
-               $ss = $this->seek();
-
-               while (true) {
-                       $whiteBefore = isset($this->buffer[$this->count - 1]) &&
-                               ctype_space($this->buffer[$this->count - 1]);
-
-                       // If there is whitespace before the operator, then we require
-                       // whitespace after the operator for it to be an expression
-                       $needWhite = $whiteBefore && !$this->inParens;
-
-                       if ($this->match(self::$operatorString.($needWhite ? '\s' : ''), $m) && self::$precedence[$m[1]] >= $minP) {
-                               if (!$this->inParens && isset($this->env->currentProperty) && $m[1] == "/" && empty($this->env->supressedDivision)) {
-                                       foreach (self::$supressDivisionProps as $pattern) {
-                                               if (preg_match($pattern, $this->env->currentProperty)) {
-                                                       $this->env->supressedDivision = true;
-                                                       break 2;
-                                               }
-                                       }
-                               }
-
-
-                               $whiteAfter = isset($this->buffer[$this->count - 1]) &&
-                                       ctype_space($this->buffer[$this->count - 1]);
-
-                               if (!$this->value($rhs)) break;
-
-                               // peek for next operator to see what to do with rhs
-                               if ($this->peek(self::$operatorString, $next) && self::$precedence[$next[1]] > self::$precedence[$m[1]]) {
-                                       $rhs = $this->expHelper($rhs, self::$precedence[$next[1]]);
-                               }
-
-                               $lhs = array('expression', $m[1], $lhs, $rhs, $whiteBefore, $whiteAfter);
-                               $ss = $this->seek();
-
-                               continue;
-                       }
-
-                       break;
-               }
-
-               $this->seek($ss);
-
-               return $lhs;
-       }
-
-       // consume a list of values for a property
-       public function propertyValue(&$value, $keyName = null) {
-               $values = array();
-
-               if ($keyName !== null) $this->env->currentProperty = $keyName;
-
-               $s = null;
-               while ($this->expressionList($v)) {
-                       $values[] = $v;
-                       $s = $this->seek();
-                       if (!$this->literal(',')) break;
-               }
-
-               if ($s) $this->seek($s);
-
-               if ($keyName !== null) unset($this->env->currentProperty);
-
-               if (count($values) == 0) return false;
-
-               $value = lessc::compressList($values, ', ');
-               return true;
-       }
-
-       protected function parenValue(&$out) {
-               $s = $this->seek();
-
-               // speed shortcut
-               if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] != "(") {
-                       return false;
-               }
-
-               $inParens = $this->inParens;
-               if ($this->literal("(") &&
-                       ($this->inParens = true) && $this->expression($exp) &&
-                       $this->literal(")"))
-               {
-                       $out = $exp;
-                       $this->inParens = $inParens;
-                       return true;
-               } else {
-                       $this->inParens = $inParens;
-                       $this->seek($s);
-               }
-
-               return false;
-       }
-
-       // a single value
-       protected function value(&$value) {
-               $s = $this->seek();
-
-               // speed shortcut
-               if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] == "-") {
-                       // negation
-                       if ($this->literal("-", false) &&
-                               (($this->variable($inner) && $inner = array("variable", $inner)) ||
-                               $this->unit($inner) ||
-                               $this->parenValue($inner)))
-                       {
-                               $value = array("unary", "-", $inner);
-                               return true;
-                       } else {
-                               $this->seek($s);
-                       }
-               }
-
-               if ($this->parenValue($value)) return true;
-               if ($this->unit($value)) return true;
-               if ($this->color($value)) return true;
-               if ($this->func($value)) return true;
-               if ($this->string($value)) return true;
-
-               if ($this->keyword($word)) {
-                       $value = array('keyword', $word);
-                       return true;
-               }
-
-               // try a variable
-               if ($this->variable($var)) {
-                       $value = array('variable', $var);
-                       return true;
-               }
-
-               // unquote string (should this work on any type?
-               if ($this->literal("~") && $this->string($str)) {
-                       $value = array("escape", $str);
-                       return true;
-               } else {
-                       $this->seek($s);
-               }
-
-               // css hack: \0
-               if ($this->literal('\\') && $this->match('([0-9]+)', $m)) {
-                       $value = array('keyword', '\\'.$m[1]);
-                       return true;
-               } else {
-                       $this->seek($s);
-               }
-
-               return false;
-       }
-
-       // an import statement
-       protected function import(&$out) {
-               if (!$this->literal('@import')) return false;
-
-               // @import "something.css" media;
-               // @import url("something.css") media;
-               // @import url(something.css) media;
-
-               if ($this->propertyValue($value)) {
-                       $out = array("import", $value);
-                       return true;
-               }
-       }
-
-       protected function mediaQueryList(&$out) {
-               if ($this->genericList($list, "mediaQuery", ",", false)) {
-                       $out = $list[2];
-                       return true;
-               }
-               return false;
-       }
-
-       protected function mediaQuery(&$out) {
-               $s = $this->seek();
-
-               $expressions = null;
-               $parts = array();
-
-               if (($this->literal("only") && ($only = true) || $this->literal("not") && ($not = true) || true) && $this->keyword($mediaType)) {
-                       $prop = array("mediaType");
-                       if (isset($only)) $prop[] = "only";
-                       if (isset($not)) $prop[] = "not";
-                       $prop[] = $mediaType;
-                       $parts[] = $prop;
-               } else {
-                       $this->seek($s);
-               }
-
-
-               if (!empty($mediaType) && !$this->literal("and")) {
-                       // ~
-               } else {
-                       $this->genericList($expressions, "mediaExpression", "and", false);
-                       if (is_array($expressions)) $parts = array_merge($parts, $expressions[2]);
-               }
-
-               if (count($parts) == 0) {
-                       $this->seek($s);
-                       return false;
-               }
-
-               $out = $parts;
-               return true;
-       }
-
-       protected function mediaExpression(&$out) {
-               $s = $this->seek();
-               $value = null;
-               if ($this->literal("(") &&
-                       $this->keyword($feature) &&
-                       ($this->literal(":") && $this->expression($value) || true) &&
-                       $this->literal(")"))
-               {
-                       $out = array("mediaExp", $feature);
-                       if ($value) $out[] = $value;
-                       return true;
-               } elseif ($this->variable($variable)) {
-                       $out = array('variable', $variable);
-                       return true;
-               }
-
-               $this->seek($s);
-               return false;
-       }
-
-       // an unbounded string stopped by $end
-       protected function openString($end, &$out, $nestingOpen=null, $rejectStrs = null) {
-               $oldWhite = $this->eatWhiteDefault;
-               $this->eatWhiteDefault = false;
-
-               $stop = array("'", '"', "@{", $end);
-               $stop = array_map(array("lessc", "preg_quote"), $stop);
-               // $stop[] = self::$commentMulti;
-
-               if (!is_null($rejectStrs)) {
-                       $stop = array_merge($stop, $rejectStrs);
-               }
-
-               $patt = '(.*?)('.implode("|", $stop).')';
-
-               $nestingLevel = 0;
-
-               $content = array();
-               while ($this->match($patt, $m, false)) {
-                       if (!empty($m[1])) {
-                               $content[] = $m[1];
-                               if ($nestingOpen) {
-                                       $nestingLevel += substr_count($m[1], $nestingOpen);
-                               }
-                       }
-
-                       $tok = $m[2];
-
-                       $this->count-= strlen($tok);
-                       if ($tok == $end) {
-                               if ($nestingLevel == 0) {
-                                       break;
-                               } else {
-                                       $nestingLevel--;
-                               }
-                       }
-
-                       if (($tok == "'" || $tok == '"') && $this->string($str)) {
-                               $content[] = $str;
-                               continue;
-                       }
-
-                       if ($tok == "@{" && $this->interpolation($inter)) {
-                               $content[] = $inter;
-                               continue;
-                       }
-
-                       if (!empty($rejectStrs) && in_array($tok, $rejectStrs)) {
-                               break;
-                       }
-
-                       $content[] = $tok;
-                       $this->count+= strlen($tok);
-               }
-
-               $this->eatWhiteDefault = $oldWhite;
-
-               if (count($content) == 0) return false;
-
-               // trim the end
-               if (is_string(end($content))) {
-                       $content[count($content) - 1] = rtrim(end($content));
-               }
-
-               $out = array("string", "", $content);
-               return true;
-       }
-
-       protected function string(&$out) {
-               $s = $this->seek();
-               if ($this->literal('"', false)) {
-                       $delim = '"';
-               } elseif ($this->literal("'", false)) {
-                       $delim = "'";
-               } else {
-                       return false;
-               }
-
-               $content = array();
-
-               // look for either ending delim , escape, or string interpolation
-               $patt = '([^\n]*?)(@\{|\\\\|' .
-                       lessc::preg_quote($delim).')';
-
-               $oldWhite = $this->eatWhiteDefault;
-               $this->eatWhiteDefault = false;
-
-               while ($this->match($patt, $m, false)) {
-                       $content[] = $m[1];
-                       if ($m[2] == "@{") {
-                               $this->count -= strlen($m[2]);
-                               if ($this->interpolation($inter, false)) {
-                                       $content[] = $inter;
-                               } else {
-                                       $this->count += strlen($m[2]);
-                                       $content[] = "@{"; // ignore it
-                               }
-                       } elseif ($m[2] == '\\') {
-                               $content[] = $m[2];
-                               if ($this->literal($delim, false)) {
-                                       $content[] = $delim;
-                               }
-                       } else {
-                               $this->count -= strlen($delim);
-                               break; // delim
-                       }
-               }
-
-               $this->eatWhiteDefault = $oldWhite;
-
-               if ($this->literal($delim)) {
-                       $out = array("string", $delim, $content);
-                       return true;
-               }
-
-               $this->seek($s);
-               return false;
-       }
-
-       protected function interpolation(&$out) {
-               $oldWhite = $this->eatWhiteDefault;
-               $this->eatWhiteDefault = true;
-
-               $s = $this->seek();
-               if ($this->literal("@{") &&
-                       $this->openString("}", $interp, null, array("'", '"', ";")) &&
-                       $this->literal("}", false))
-               {
-                       $out = array("interpolate", $interp);
-                       $this->eatWhiteDefault = $oldWhite;
-                       if ($this->eatWhiteDefault) $this->whitespace();
-                       return true;
-               }
-
-               $this->eatWhiteDefault = $oldWhite;
-               $this->seek($s);
-               return false;
-       }
-
-       protected function unit(&$unit) {
-               // speed shortcut
-               if (isset($this->buffer[$this->count])) {
-                       $char = $this->buffer[$this->count];
-                       if (!ctype_digit($char) && $char != ".") return false;
-               }
-
-               if ($this->match('([0-9]+(?:\.[0-9]*)?|\.[0-9]+)([%a-zA-Z]+)?', $m)) {
-                       $unit = array("number", $m[1], empty($m[2]) ? "" : $m[2]);
-                       return true;
-               }
-               return false;
-       }
-
-       // a # color
-       protected function color(&$out) {
-               if ($this->match('(#(?:[0-9a-f]{8}|[0-9a-f]{6}|[0-9a-f]{3}))', $m)) {
-                       if (strlen($m[1]) > 7) {
-                               $out = array("string", "", array($m[1]));
-                       } else {
-                               $out = array("raw_color", $m[1]);
-                       }
-                       return true;
-               }
-
-               return false;
-       }
-
-       // consume an argument definition list surrounded by ()
-       // each argument is a variable name with optional value
-       // or at the end a ... or a variable named followed by ...
-       // arguments are separated by , unless a ; is in the list, then ; is the
-       // delimiter.
-       protected function argumentDef(&$args, &$isVararg) {
-               $s = $this->seek();
-               if (!$this->literal('(')) return false;
-
-               $values = array();
-               $delim = ",";
-               $method = "expressionList";
-
-               $isVararg = false;
-               while (true) {
-                       if ($this->literal("...")) {
-                               $isVararg = true;
-                               break;
-                       }
-
-                       if ($this->$method($value)) {
-                               if ($value[0] == "variable") {
-                                       $arg = array("arg", $value[1]);
-                                       $ss = $this->seek();
-
-                                       if ($this->assign() && $this->$method($rhs)) {
-                                               $arg[] = $rhs;
-                                       } else {
-                                               $this->seek($ss);
-                                               if ($this->literal("...")) {
-                                                       $arg[0] = "rest";
-                                                       $isVararg = true;
-                                               }
-                                       }
-
-                                       $values[] = $arg;
-                                       if ($isVararg) break;
-                                       continue;
-                               } else {
-                                       $values[] = array("lit", $value);
-                               }
-                       }
-
-
-                       if (!$this->literal($delim)) {
-                               if ($delim == "," && $this->literal(";")) {
-                                       // found new delim, convert existing args
-                                       $delim = ";";
-                                       $method = "propertyValue";
-
-                                       // transform arg list
-                                       if (isset($values[1])) { // 2 items
-                                               $newList = array();
-                                               foreach ($values as $i => $arg) {
-                                                       switch($arg[0]) {
-                                                       case "arg":
-                                                               if ($i) {
-                                                                       $this->throwError("Cannot mix ; and , as delimiter types");
-                                                               }
-                                                               $newList[] = $arg[2];
-                                                               break;
-                                                       case "lit":
-                                                               $newList[] = $arg[1];
-                                                               break;
-                                                       case "rest":
-                                                               $this->throwError("Unexpected rest before semicolon");
-                                                       }
-                                               }
-
-                                               $newList = array("list", ", ", $newList);
-
-                                               switch ($values[0][0]) {
-                                               case "arg":
-                                                       $newArg = array("arg", $values[0][1], $newList);
-                                                       break;
-                                               case "lit":
-                                                       $newArg = array("lit", $newList);
-                                                       break;
-                                               }
-
-                                       } elseif ($values) { // 1 item
-                                               $newArg = $values[0];
-                                       }
-
-                                       if ($newArg) {
-                                               $values = array($newArg);
-                                       }
-                               } else {
-                                       break;
-                               }
-                       }
-               }
-
-               if (!$this->literal(')')) {
-                       $this->seek($s);
-                       return false;
-               }
-
-               $args = $values;
-
-               return true;
-       }
-
-       // consume a list of tags
-       // this accepts a hanging delimiter
-       protected function tags(&$tags, $simple = false, $delim = ',') {
-               $tags = array();
-               while ($this->tag($tt, $simple)) {
-                       $tags[] = $tt;
-                       if (!$this->literal($delim)) break;
-               }
-               if (count($tags) == 0) return false;
-
-               return true;
-       }
-
-       // list of tags of specifying mixin path
-       // optionally separated by > (lazy, accepts extra >)
-       protected function mixinTags(&$tags) {
-               $tags = array();
-               while ($this->tag($tt, true)) {
-                       $tags[] = $tt;
-                       $this->literal(">");
-               }
-
-               if (count($tags) == 0) return false;
-
-               return true;
-       }
-
-       // a bracketed value (contained within in a tag definition)
-       protected function tagBracket(&$parts, &$hasExpression) {
-               // speed shortcut
-               if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] != "[") {
-                       return false;
-               }
-
-               $s = $this->seek();
-
-               $hasInterpolation = false;
-
-               if ($this->literal("[", false)) {
-                       $attrParts = array("[");
-                       // keyword, string, operator
-                       while (true) {
-                               if ($this->literal("]", false)) {
-                                       $this->count--;
-                                       break; // get out early
-                               }
-
-                               if ($this->match('\s+', $m)) {
-                                       $attrParts[] = " ";
-                                       continue;
-                               }
-                               if ($this->string($str)) {
-                                       // escape parent selector, (yuck)
-                                       foreach ($str[2] as &$chunk) {
-                                               $chunk = str_replace($this->lessc->parentSelector, "$&$", $chunk);
-                                       }
-
-                                       $attrParts[] = $str;
-                                       $hasInterpolation = true;
-                                       continue;
-                               }
-
-                               if ($this->keyword($word)) {
-                                       $attrParts[] = $word;
-                                       continue;
-                               }
-
-                               if ($this->interpolation($inter, false)) {
-                                       $attrParts[] = $inter;
-                                       $hasInterpolation = true;
-                                       continue;
-                               }
-
-                               // operator, handles attr namespace too
-                               if ($this->match('[|-~\$\*\^=]+', $m)) {
-                                       $attrParts[] = $m[0];
-                                       continue;
-                               }
-
-                               break;
-                       }
-
-                       if ($this->literal("]", false)) {
-                               $attrParts[] = "]";
-                               foreach ($attrParts as $part) {
-                                       $parts[] = $part;
-                               }
-                               $hasExpression = $hasExpression || $hasInterpolation;
-                               return true;
-                       }
-                       $this->seek($s);
-               }
-
-               $this->seek($s);
-               return false;
-       }
-
-       // a space separated list of selectors
-       protected function tag(&$tag, $simple = false) {
-               if ($simple)
-                       $chars = '^@,:;{}\][>\(\) "\'';
-               else
-                       $chars = '^@,;{}["\'';
-
-               $s = $this->seek();
-
-               $hasExpression = false;
-               $parts = array();
-               while ($this->tagBracket($parts, $hasExpression));
-
-               $oldWhite = $this->eatWhiteDefault;
-               $this->eatWhiteDefault = false;
-
-               while (true) {
-                       if ($this->match('(['.$chars.'0-9]['.$chars.']*)', $m)) {
-                               $parts[] = $m[1];
-                               if ($simple) break;
-
-                               while ($this->tagBracket($parts, $hasExpression));
-                               continue;
-                       }
-
-                       if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] == "@") {
-                               if ($this->interpolation($interp)) {
-                                       $hasExpression = true;
-                                       $interp[2] = true; // don't unescape
-                                       $parts[] = $interp;
-                                       continue;
-                               }
-
-                               if ($this->literal("@")) {
-                                       $parts[] = "@";
-                                       continue;
-                               }
-                       }
-
-                       if ($this->unit($unit)) { // for keyframes
-                               $parts[] = $unit[1];
-                               $parts[] = $unit[2];
-                               continue;
-                       }
-
-                       break;
-               }
-
-               $this->eatWhiteDefault = $oldWhite;
-               if (!$parts) {
-                       $this->seek($s);
-                       return false;
-               }
-
-               if ($hasExpression) {
-                       $tag = array("exp", array("string", "", $parts));
-               } else {
-                       $tag = trim(implode($parts));
-               }
-
-               $this->whitespace();
-               return true;
-       }
-
-       // a css function
-       protected function func(&$func) {
-               $s = $this->seek();
-
-               if ($this->match('(%|[\w\-_][\w\-_:\.]+|[\w_])', $m) && $this->literal('(')) {
-                       $fname = $m[1];
-
-                       $sPreArgs = $this->seek();
-
-                       $args = array();
-                       while (true) {
-                               $ss = $this->seek();
-                               // this ugly nonsense is for ie filter properties
-                               if ($this->keyword($name) && $this->literal('=') && $this->expressionList($value)) {
-                                       $args[] = array("string", "", array($name, "=", $value));
-                               } else {
-                                       $this->seek($ss);
-                                       if ($this->expressionList($value)) {
-                                               $args[] = $value;
-                                       }
-                               }
-
-                               if (!$this->literal(',')) break;
-                       }
-                       $args = array('list', ',', $args);
-
-                       if ($this->literal(')')) {
-                               $func = array('function', $fname, $args);
-                               return true;
-                       } elseif ($fname == 'url') {
-                               // couldn't parse and in url? treat as string
-                               $this->seek($sPreArgs);
-                               if ($this->openString(")", $string) && $this->literal(")")) {
-                                       $func = array('function', $fname, $string);
-                                       return true;
-                               }
-                       }
-               }
-
-               $this->seek($s);
-               return false;
-       }
-
-       // consume a less variable
-       protected function variable(&$name) {
-               $s = $this->seek();
-               if ($this->literal($this->lessc->vPrefix, false) &&
-                       ($this->variable($sub) || $this->keyword($name)))
-               {
-                       if (!empty($sub)) {
-                               $name = array('variable', $sub);
-                       } else {
-                               $name = $this->lessc->vPrefix.$name;
-                       }
-                       return true;
-               }
-
-               $name = null;
-               $this->seek($s);
-               return false;
-       }
-
-       /**
-        * Consume an assignment operator
-        * Can optionally take a name that will be set to the current property name
-        */
-       protected function assign($name = null) {
-               if ($name) $this->currentProperty = $name;
-               return $this->literal(':') || $this->literal('=');
-       }
-
-       // consume a keyword
-       protected function keyword(&$word) {
-               if ($this->match('([\w_\-\*!"][\w\-_"]*)', $m)) {
-                       $word = $m[1];
-                       return true;
-               }
-               return false;
-       }
-
-       // consume an end of statement delimiter
-       protected function end() {
-               if ($this->literal(';', false)) {
-                       return true;
-               } elseif ($this->count == strlen($this->buffer) || $this->buffer[$this->count] == '}') {
-                       // if there is end of file or a closing block next then we don't need a ;
-                       return true;
-               }
-               return false;
-       }
-
-       protected function guards(&$guards) {
-               $s = $this->seek();
-
-               if (!$this->literal("when")) {
-                       $this->seek($s);
-                       return false;
-               }
-
-               $guards = array();
-
-               while ($this->guardGroup($g)) {
-                       $guards[] = $g;
-                       if (!$this->literal(",")) break;
-               }
-
-               if (count($guards) == 0) {
-                       $guards = null;
-                       $this->seek($s);
-                       return false;
-               }
-
-               return true;
-       }
-
-       // a bunch of guards that are and'd together
-       // TODO rename to guardGroup
-       protected function guardGroup(&$guardGroup) {
-               $s = $this->seek();
-               $guardGroup = array();
-               while ($this->guard($guard)) {
-                       $guardGroup[] = $guard;
-                       if (!$this->literal("and")) break;
-               }
-
-               if (count($guardGroup) == 0) {
-                       $guardGroup = null;
-                       $this->seek($s);
-                       return false;
-               }
-
-               return true;
-       }
-
-       protected function guard(&$guard) {
-               $s = $this->seek();
-               $negate = $this->literal("not");
-
-               if ($this->literal("(") && $this->expression($exp) && $this->literal(")")) {
-                       $guard = $exp;
-                       if ($negate) $guard = array("negate", $guard);
-                       return true;
-               }
-
-               $this->seek($s);
-               return false;
-       }
-
-       /* raw parsing functions */
-
-       protected function literal($what, $eatWhitespace = null) {
-               if ($eatWhitespace === null) $eatWhitespace = $this->eatWhiteDefault;
-
-               // shortcut on single letter
-               if (!isset($what[1]) && isset($this->buffer[$this->count])) {
-                       if ($this->buffer[$this->count] == $what) {
-                               if (!$eatWhitespace) {
-                                       $this->count++;
-                                       return true;
-                               }
-                               // goes below...
-                       } else {
-                               return false;
-                       }
-               }
-
-               if (!isset(self::$literalCache[$what])) {
-                       self::$literalCache[$what] = lessc::preg_quote($what);
-               }
-
-               return $this->match(self::$literalCache[$what], $m, $eatWhitespace);
-       }
-
-       protected function genericList(&$out, $parseItem, $delim="", $flatten=true) {
-               $s = $this->seek();
-               $items = array();
-               while ($this->$parseItem($value)) {
-                       $items[] = $value;
-                       if ($delim) {
-                               if (!$this->literal($delim)) break;
-                       }
-               }
-
-               if (count($items) == 0) {
-                       $this->seek($s);
-                       return false;
-               }
-
-               if ($flatten && count($items) == 1) {
-                       $out = $items[0];
-               } else {
-                       $out = array("list", $delim, $items);
-               }
-
-               return true;
-       }
-
-
-       // advance counter to next occurrence of $what
-       // $until - don't include $what in advance
-       // $allowNewline, if string, will be used as valid char set
-       protected function to($what, &$out, $until = false, $allowNewline = false) {
-               if (is_string($allowNewline)) {
-                       $validChars = $allowNewline;
-               } else {
-                       $validChars = $allowNewline ? "." : "[^\n]";
-               }
-               if (!$this->match('('.$validChars.'*?)'.lessc::preg_quote($what), $m, !$until)) return false;
-               if ($until) $this->count -= strlen($what); // give back $what
-               $out = $m[1];
-               return true;
-       }
-
-       // try to match something on head of buffer
-       protected function match($regex, &$out, $eatWhitespace = null) {
-               if ($eatWhitespace === null) $eatWhitespace = $this->eatWhiteDefault;
-
-               $r = '/'.$regex.($eatWhitespace && !$this->writeComments ? '\s*' : '').'/Ais';
-               if (preg_match($r, $this->buffer, $out, null, $this->count)) {
-                       $this->count += strlen($out[0]);
-                       if ($eatWhitespace && $this->writeComments) $this->whitespace();
-                       return true;
-               }
-               return false;
-       }
-
-       // match some whitespace
-       protected function whitespace() {
-               if ($this->writeComments) {
-                       $gotWhite = false;
-                       while (preg_match(self::$whitePattern, $this->buffer, $m, null, $this->count)) {
-                               if (isset($m[1]) && empty($this->seenComments[$this->count])) {
-                                       $this->append(array("comment", $m[1]));
-                                       $this->seenComments[$this->count] = true;
-                               }
-                               $this->count += strlen($m[0]);
-                               $gotWhite = true;
-                       }
-                       return $gotWhite;
-               } else {
-                       $this->match("", $m);
-                       return strlen($m[0]) > 0;
-               }
-       }
-
-       // match something without consuming it
-       protected function peek($regex, &$out = null, $from=null) {
-               if (is_null($from)) $from = $this->count;
-               $r = '/'.$regex.'/Ais';
-               $result = preg_match($r, $this->buffer, $out, null, $from);
-
-               return $result;
-       }
-
-       // seek to a spot in the buffer or return where we are on no argument
-       protected function seek($where = null) {
-               if ($where === null) return $this->count;
-               else $this->count = $where;
-               return true;
-       }
-
-       /* misc functions */
-
-       public function throwError($msg = "parse error", $count = null) {
-               $count = is_null($count) ? $this->count : $count;
-
-               $line = $this->line +
-                       substr_count(substr($this->buffer, 0, $count), "\n");
-
-               if (!empty($this->sourceName)) {
-                       $loc = "$this->sourceName on line $line";
-               } else {
-                       $loc = "line: $line";
-               }
-
-               // TODO this depends on $this->count
-               if ($this->peek("(.*?)(\n|$)", $m, $count)) {
-                       throw new exception("$msg: failed at `$m[1]` $loc");
-               } else {
-                       throw new exception("$msg: $loc");
-               }
-       }
-
-       protected function pushBlock($selectors=null, $type=null) {
-               $b = new stdclass;
-               $b->parent = $this->env;
-
-               $b->type = $type;
-               $b->id = self::$nextBlockId++;
-
-               $b->isVararg = false; // TODO: kill me from here
-               $b->tags = $selectors;
-
-               $b->props = array();
-               $b->children = array();
-
-               $this->env = $b;
-               return $b;
-       }
-
-       // push a block that doesn't multiply tags
-       protected function pushSpecialBlock($type) {
-               return $this->pushBlock(null, $type);
-       }
-
-       // append a property to the current block
-       protected function append($prop, $pos = null) {
-               if ($pos !== null) $prop[-1] = $pos;
-               $this->env->props[] = $prop;
-       }
-
-       // pop something off the stack
-       protected function pop() {
-               $old = $this->env;
-               $this->env = $this->env->parent;
-               return $old;
-       }
-
-       // remove comments from $text
-       // todo: make it work for all functions, not just url
-       protected function removeComments($text) {
-               $look = array(
-                       'url(', '//', '/*', '"', "'"
-               );
-
-               $out = '';
-               $min = null;
-               while (true) {
-                       // find the next item
-                       foreach ($look as $token) {
-                               $pos = strpos($text, $token);
-                               if ($pos !== false) {
-                                       if (!isset($min) || $pos < $min[1]) $min = array($token, $pos);
-                               }
-                       }
-
-                       if (is_null($min)) break;
-
-                       $count = $min[1];
-                       $skip = 0;
-                       $newlines = 0;
-                       switch ($min[0]) {
-                       case 'url(':
-                               if (preg_match('/url\(.*?\)/', $text, $m, 0, $count))
-                                       $count += strlen($m[0]) - strlen($min[0]);
-                               break;
-                       case '"':
-                       case "'":
-                               if (preg_match('/'.$min[0].'.*?(?<!\\\\)'.$min[0].'/', $text, $m, 0, $count))
-                                       $count += strlen($m[0]) - 1;
-                               break;
-                       case '//':
-                               $skip = strpos($text, "\n", $count);
-                               if ($skip === false) $skip = strlen($text) - $count;
-                               else $skip -= $count;
-                               break;
-                       case '/*':
-                               if (preg_match('/\/\*.*?\*\//s', $text, $m, 0, $count)) {
-                                       $skip = strlen($m[0]);
-                                       $newlines = substr_count($m[0], "\n");
-                               }
-                               break;
-                       }
-
-                       if ($skip == 0) $count += strlen($min[0]);
-
-                       $out .= substr($text, 0, $count).str_repeat("\n", $newlines);
-                       $text = substr($text, $count + $skip);
-
-                       $min = null;
-               }
-
-               return $out.$text;
-       }
-
-}
-
-class lessc_formatter_classic {
-       public $indentChar = "  ";
-
-       public $break = "\n";
-       public $open = " {";
-       public $close = "}";
-       public $selectorSeparator = ", ";
-       public $assignSeparator = ":";
-
-       public $openSingle = " { ";
-       public $closeSingle = " }";
-
-       public $disableSingle = false;
-       public $breakSelectors = false;
-
-       public $compressColors = false;
-
-       public function __construct() {
-               $this->indentLevel = 0;
-       }
-
-       public function indentStr($n = 0) {
-               return str_repeat($this->indentChar, max($this->indentLevel + $n, 0));
-       }
-
-       public function property($name, $value) {
-               return $name . $this->assignSeparator . $value . ";";
-       }
-
-       protected function isEmpty($block) {
-               if (empty($block->lines)) {
-                       foreach ($block->children as $child) {
-                               if (!$this->isEmpty($child)) return false;
-                       }
-
-                       return true;
-               }
-               return false;
-       }
-
-       public function block($block) {
-               if ($this->isEmpty($block)) return;
-
-               $inner = $pre = $this->indentStr();
-
-               $isSingle = !$this->disableSingle &&
-                       is_null($block->type) && count($block->lines) == 1;
-
-               if (!empty($block->selectors)) {
-                       $this->indentLevel++;
-
-                       if ($this->breakSelectors) {
-                               $selectorSeparator = $this->selectorSeparator . $this->break . $pre;
-                       } else {
-                               $selectorSeparator = $this->selectorSeparator;
-                       }
-
-                       echo $pre .
-                               implode($selectorSeparator, $block->selectors);
-                       if ($isSingle) {
-                               echo $this->openSingle;
-                               $inner = "";
-                       } else {
-                               echo $this->open . $this->break;
-                               $inner = $this->indentStr();
-                       }
-
-               }
-
-               if (!empty($block->lines)) {
-                       $glue = $this->break.$inner;
-                       echo $inner . implode($glue, $block->lines);
-                       if (!$isSingle && !empty($block->children)) {
-                               echo $this->break;
-                       }
-               }
-
-               foreach ($block->children as $child) {
-                       $this->block($child);
-               }
-
-               if (!empty($block->selectors)) {
-                       if (!$isSingle && empty($block->children)) echo $this->break;
-
-                       if ($isSingle) {
-                               echo $this->closeSingle . $this->break;
-                       } else {
-                               echo $pre . $this->close . $this->break;
-                       }
-
-                       $this->indentLevel--;
-               }
-       }
-}
-
-class lessc_formatter_compressed extends lessc_formatter_classic {
-       public $disableSingle = true;
-       public $open = "{";
-       public $selectorSeparator = ",";
-       public $assignSeparator = ":";
-       public $break = "";
-       public $compressColors = true;
-
-       public function indentStr($n = 0) {
-               return "";
-       }
-}
-
-class lessc_formatter_lessjs extends lessc_formatter_classic {
-       public $disableSingle = true;
-       public $breakSelectors = true;
-       public $assignSeparator = ": ";
-       public $selectorSeparator = ",";
-}
-
-
index c7f5e5a..e03cf1c 100644 (file)
@@ -542,22 +542,28 @@ class LogEventsList extends ContextSource {
                        $pager->mLimit = $lim;
                }
 
-               $logBody = null;
+               $knownEmptyResult = false;
                // Check if we can avoid the DB query all together
                if ( $page !== '' && !$param['useMaster'] ) {
                        $title = ( $page instanceof Title ) ? $page : Title::newFromText( $page );
                        if ( $title ) {
                                $member = $title->getNamespace() . ':' . $title->getDBkey();
                                if ( !BloomCache::get( 'main' )->check( wfWikiId(), 'TitleHasLogs', $member ) ) {
-                                       $logBody = '';
+                                       $knownEmptyResult = true;
                                }
                        } else {
-                               $logBody = '';
+                               $knownEmptyResult = true;
                        }
                }
 
                // Fetch the log rows and build the HTML if needed
-               $logBody = ( $logBody === null ) ? $pager->getBody() : $logBody;
+               if ( $knownEmptyResult ) {
+                       $logBody = '';
+                       $numRows = 0;
+               } else {
+                       $logBody = $pager->getBody();
+                       $numRows = $pager->getNumRows();
+               }
 
                $s = '';
 
@@ -588,7 +594,7 @@ class LogEventsList extends ContextSource {
                                $context->msg( 'logempty' )->parse() );
                }
 
-               if ( $pager->getNumRows() > $pager->mLimit ) { # Show "Full log" link
+               if ( $numRows > $pager->mLimit ) { # Show "Full log" link
                        $urlParam = array();
                        if ( $page instanceof Title ) {
                                $urlParam['page'] = $page->getPrefixedDBkey();
@@ -635,7 +641,7 @@ class LogEventsList extends ContextSource {
                        }
                }
 
-               return $pager->getNumRows();
+               return $numRows;
        }
 
        /**
index e81b37d..0292af8 100644 (file)
@@ -142,7 +142,7 @@ class BitmapHandler extends TransformationalImageHandler {
                        $env['MAGICK_TMPDIR'] = $wgImageMagickTempDir;
                }
 
-               $rotation = $this->getRotation( $image );
+               $rotation = isset( $params['disableRotation'] ) ? 0 : $this->getRotation( $image );
                list( $width, $height ) = $this->extractPreRotationDimensions( $params, $rotation );
 
                $cmd = call_user_func_array( 'wfEscapeShellArg', array_merge(
@@ -223,7 +223,7 @@ class BitmapHandler extends TransformationalImageHandler {
                                }
                        }
 
-                       $rotation = $this->getRotation( $image );
+                       $rotation = isset( $params['disableRotation'] ) ? 0 : $this->getRotation( $image );
                        list( $width, $height ) = $this->extractPreRotationDimensions( $params, $rotation );
 
                        $im->setImageBackgroundColor( new ImagickPixel( 'white' ) );
@@ -344,7 +344,7 @@ class BitmapHandler extends TransformationalImageHandler {
 
                $src_image = call_user_func( $loader, $params['srcPath'] );
 
-               $rotation = function_exists( 'imagerotate' ) ? $this->getRotation( $image ) : 0;
+               $rotation = function_exists( 'imagerotate' ) && !isset( $params['disableRotation'] )  ? $this->getRotation( $image ) : 0;
                list( $width, $height ) = $this->extractPreRotationDimensions( $params, $rotation );
                $dst_image = imagecreatetruecolor( $width, $height );
 
index 4356953..07d7618 100644 (file)
@@ -1624,6 +1624,7 @@ class FormatMetadata extends ContextSource {
                        if ( $this->singleLang ) {
                                $this->resolveMultilangMetadata( $extendedMetadata );
                        }
+                       $this->discardMultipleValues( $extendedMetadata );
                        // Make sure the metadata won't break the API when an XML format is used.
                        // This is an API-specific function so it would be cleaner to call it from
                        // outside fetchExtendedMetadata, but this way we don't need to redo the
@@ -1776,6 +1777,32 @@ class FormatMetadata extends ContextSource {
                return null;
        }
 
+       /**
+        * Turns an XMP-style multivalue array into a single value by dropping all but the first value.
+        * If the value is not a multivalue array (or a multivalue array inside a multilang array), it is returned unchanged.
+        * See mediawiki.org/wiki/Manual:File_metadata_handling#Multi-language_array_format
+        * @param mixed $value
+        * @return mixed The value, or the first value if there were multiple ones
+        * @since 1.25
+        */
+       protected function resolveMultivalueValue( $value ) {
+               if ( !is_array( $value ) ) {
+                       return $value;
+               } elseif ( isset( $value['_type'] ) && $value['_type'] === 'lang' ) { // if this is a multilang array, process fields separately
+                       $newValue = array();
+                       foreach ( $value as $k => $v ) {
+                               $newValue[$k] = $this->resolveMultivalueValue( $v );
+                       }
+                       return $newValue;
+               } else { // _type is 'ul' or 'ol' or missing in which case it defaults to 'ul'
+                       list( $k, $v ) = each( $value );
+                       if ( $k === '_type' ) {
+                               $v = current( $value );
+                       }
+                       return $v;
+               }
+       }
+
        /**
         * Takes an array returned by the getExtendedMetadata* functions,
         * and resolves multi-language values in it.
@@ -1793,6 +1820,29 @@ class FormatMetadata extends ContextSource {
                }
        }
 
+       /**
+        * Takes an array returned by the getExtendedMetadata* functions,
+        * and turns all fields into single-valued ones by dropping extra values.
+        * @param array $metadata
+        * @since 1.25
+        */
+       protected function discardMultipleValues( &$metadata ) {
+               if ( !is_array( $metadata ) ) {
+                       return;
+               }
+               foreach ( $metadata as $key => &$field ) {
+                       if ( $key === 'Software' || $key === 'Contact' ) {
+                               // we skip some fields which have composite values. They are not particularly interesting
+                               // and you can get them via the metadata / commonmetadata APIs anyway.
+                               continue;
+                       }
+                       if ( isset( $field['value'] ) ) {
+                               $field['value'] = $this->resolveMultivalueValue( $field['value'] );
+                       }
+               }
+
+       }
+
        /**
         * Makes sure the given array is a valid API response fragment
         * (can be transformed into XML)
index 3e3be3d..b3ae296 100644 (file)
@@ -216,6 +216,12 @@ abstract class TransformationalImageHandler extends ImageHandler {
                # Transform functions and binaries need a FS source file
                $thumbnailSource = $this->getThumbnailSource( $image, $params );
 
+               // If the source isn't the original, disable EXIF rotation because it's already been applied
+               if ( $scalerParams['srcWidth'] != $thumbnailSource['width']
+                       || $scalerParams['srcHeight'] != $thumbnailSource['height'] ) {
+                       $scalerParams['disableRotation'] = true;
+               }
+
                $scalerParams['srcPath'] = $thumbnailSource['path'];
                $scalerParams['srcWidth'] = $thumbnailSource['width'];
                $scalerParams['srcHeight'] = $thumbnailSource['height'];
index 1978c3e..0a23446 100644 (file)
@@ -173,13 +173,14 @@ abstract class BagOStuff {
 
        /**
         * @param string $key
-        * @param int $timeout [optional]
+        * @param int $timeout Lock wait timeout [optional]
+        * @param int $expiry Lock expiry [optional]
         * @return bool Success
         */
-       public function lock( $key, $timeout = 6 ) {
+       public function lock( $key, $timeout = 6, $expiry = 6 ) {
                $this->clearLastError();
                $timestamp = microtime( true ); // starting UNIX timestamp
-               if ( $this->add( "{$key}:lock", 1, $timeout ) ) {
+               if ( $this->add( "{$key}:lock", 1, $expiry ) ) {
                        return true;
                } elseif ( $this->getLastError() ) {
                        return false;
@@ -198,11 +199,11 @@ abstract class BagOStuff {
                        }
                        usleep( $sleep ); // back off
                        $this->clearLastError();
-                       $locked = $this->add( "{$key}:lock", 1, $timeout );
+                       $locked = $this->add( "{$key}:lock", 1, $expiry );
                        if ( $this->getLastError() ) {
                                return false;
                        }
-               } while ( !$locked );
+               } while ( !$locked && ( microtime( true ) - $timestamp ) < $timeout );
 
                return $locked;
        }
index 330d2b5..939a715 100644 (file)
@@ -65,23 +65,6 @@ class MemcachedPhpBagOStuff extends MemcachedBagOStuff {
                return $this->client->get_multi( array_map( $callback, $keys ) );
        }
 
-       /**
-        * @param string $key
-        * @param int $timeout
-        * @return bool
-        */
-       public function lock( $key, $timeout = 0 ) {
-               return $this->client->lock( $this->encodeKey( $key ), $timeout );
-       }
-
-       /**
-        * @param string $key
-        * @return mixed
-        */
-       public function unlock( $key ) {
-               return $this->client->unlock( $this->encodeKey( $key ) );
-       }
-
        /**
         * @param string $key
         * @param int $value
index 6a69137..c2a4a27 100644 (file)
@@ -136,12 +136,13 @@ class MultiWriteBagOStuff extends BagOStuff {
        /**
         * @param string $key
         * @param int $timeout
+        * @param int $expiry
         * @return bool
         */
-       public function lock( $key, $timeout = 0 ) {
+       public function lock( $key, $timeout = 6, $expiry = 6 ) {
                // Lock only the first cache, to avoid deadlocks
                if ( isset( $this->caches[0] ) ) {
-                       return $this->caches[0]->lock( $key, $timeout );
+                       return $this->caches[0]->lock( $key, $timeout, $expiry );
                } else {
                        return true;
                }
index d310836..07eb340 100644 (file)
@@ -534,12 +534,12 @@ class Parser {
                        // Add on template profiling data
                        $dataByFunc = $this->mProfiler->getFunctionStats();
                        uasort( $dataByFunc, function( $a, $b ) {
-                               return $a['elapsed'] < $b['elapsed']; // descending order
+                               return $a['real'] < $b['real']; // descending order
                        } );
-                       $profileReport = "Top template expansion time report (%,ms,calls,template)\n";
+                       $profileReport = "Transclusion expansion time report (%,ms,calls,template)\n";
                        foreach ( array_slice( $dataByFunc, 0, 10 ) as $item ) {
-                               $profileReport .= sprintf( "%6.2f%% %3.6f %6d - %s\n",
-                                       $item['percent'], $item['elapsed'], $item['calls'],
+                               $profileReport .= sprintf( "%6.2f%% %8.3f %6d - %s\n",
+                                       $item['%real'], $item['real'], $item['calls'],
                                        htmlspecialchars($item['name'] ) );
                        }
                        $text .= "\n<!-- \n$profileReport-->\n";
index 5e7e391..da20f94 100644 (file)
@@ -67,7 +67,8 @@ class PoolWorkArticleView extends PoolCounterWork {
                $this->parserOptions = $parserOptions;
                $this->content = $content;
                $this->cacheKey = ParserCache::singleton()->getKey( $page, $parserOptions );
-               parent::__construct( 'ArticleView', $this->cacheKey . ':revid:' . $revid );
+               $keyPrefix = $this->cacheKey ?: wfMemcKey( 'articleview', 'missingcachekey' );
+               parent::__construct( 'ArticleView', $keyPrefix . ':revid:' . $revid );
        }
 
        /**
index 078b66b..9650ff5 100644 (file)
@@ -33,10 +33,21 @@ abstract class Profiler {
        protected $profileID = false;
        /** @var bool Whether MediaWiki is in a SkinTemplate output context */
        protected $templated = false;
+       /** @var array All of the params passed from $wgProfiler */
+       protected $params = array();
 
        /** @var TransactionProfiler */
        protected $trxProfiler;
 
+       /**
+        * @var array Mapping of output type to class name
+        */
+       private static $outputTypes = array(
+               'db' => 'ProfilerOutputDb',
+               'text' => 'ProfilerOutputText',
+               'udp' => 'ProfilerOutputUdp',
+       );
+
        // @codingStandardsIgnoreStart PSR2.Classes.PropertyDeclaration.Underscore
        /** @var Profiler Do not call this outside Profiler and ProfileSection */
        public static $__instance = null;
@@ -49,6 +60,7 @@ abstract class Profiler {
                if ( isset( $params['profileID'] ) ) {
                        $this->profileID = $params['profileID'];
                }
+               $this->params = $params;
                $this->trxProfiler = new TransactionProfiler();
        }
 
@@ -60,10 +72,10 @@ abstract class Profiler {
                if ( self::$__instance === null ) {
                        global $wgProfiler;
                        if ( is_array( $wgProfiler ) ) {
-                               if ( !isset( $wgProfiler['class'] ) ) {
+                               $class = isset( $wgProfiler['class'] ) ? $wgProfiler['class'] : 'ProfilerStub';
+                               $factor = isset( $wgProfiler['sampling'] ) ? $wgProfiler['sampling'] : 1;
+                               if ( PHP_SAPI === 'cli' || mt_rand( 0, $factor - 1 ) != 0 ) {
                                        $class = 'ProfilerStub';
-                               } else {
-                                       $class = $wgProfiler['class'];
                                }
                                self::$__instance = new $class( $wgProfiler );
                        } else {
@@ -73,6 +85,21 @@ abstract class Profiler {
                return self::$__instance;
        }
 
+       /**
+        * Replace the current profiler with $profiler if no non-stub profiler is set
+        *
+        * @param Profiler $profiler
+        * @throws MWException
+        * @since 1.25
+        */
+       final public static function replaceStubInstance( Profiler $profiler ) {
+               if ( self::$__instance && !( self::$__instance instanceof ProfilerStub ) ) {
+                       throw new MWException( 'Could not replace non-stub profiler instance.' );
+               } else {
+                       self::$__instance = $profiler;
+               }
+       }
+
        /**
         * Return whether this a stub profiler
         *
@@ -112,6 +139,23 @@ abstract class Profiler {
         */
        abstract public function profileOut( $functionname );
 
+       /**
+        * Mark the start of a custom profiling frame (e.g. DB queries).
+        * The frame ends when the result of this method falls out of scope.
+        *
+        * @param string $section
+        * @return ScopedCallback|null
+        * @since 1.25
+        */
+       abstract public function scopedProfileIn( $section );
+
+       /**
+        * @param ScopedCallback $section
+        */
+       public function scopedProfileOut( ScopedCallback &$section ) {
+               $section = null;
+       }
+
        /**
         * @return TransactionProfiler
         * @since 1.25
@@ -127,8 +171,50 @@ abstract class Profiler {
 
        /**
         * Log the data to some store or even the page output
+        *
+        * @throws MWException
+        * @since 1.25
+        */
+       public function logData() {
+               $output = isset( $this->params['output'] ) ? $this->params['output'] : null;
+
+               if ( !$output || $this->isStub() ) {
+                       // return early when no output classes defined or we're a stub
+                       return;
+               }
+
+               if ( !is_array( $output ) ) {
+                       $output = array( $output );
+               }
+
+               foreach ( $output as $outType ) {
+                       if ( !isset( self::$outputTypes[$outType] ) ) {
+                               throw new MWException( "'$outType' is an invalid output type" );
+                       }
+                       $class = self::$outputTypes[$outType];
+
+                       /** @var ProfilerOutput $profileOut */
+                       $profileOut = new $class( $this, $this->params );
+                       if ( $profileOut->canUse() ) {
+                               $profileOut->log( $this->getFunctionStats() );
+                       }
+               }
+       }
+
+       /**
+        * Get the content type sent out to the client.
+        * Used for profilers that output instead of store data.
+        * @return string
+        * @since 1.25
         */
-       abstract public function logData();
+       public function getContentType() {
+               foreach ( headers_list() as $header ) {
+                       if ( preg_match( '#^content-type: (\w+/\w+);?#i', $header, $m ) ) {
+                               return $m[1];
+                       }
+               }
+               return null;
+       }
 
        /**
         * Mark this call as templated or not
@@ -140,80 +226,44 @@ abstract class Profiler {
        }
 
        /**
-        * Returns a profiling output to be stored in debug file
+        * Was this call as templated or not
         *
-        * @return string
+        * @return bool
         */
-       abstract public function getOutput();
+       public function getTemplated() {
+               return $this->templated;
+       }
 
        /**
-        * Get data for the debugging toolbar.
+        * Get the aggregated inclusive profiling data for each method
         *
-        * @return array
-        */
-       abstract public function getRawData();
-
-       /**
-        * Get the initial time of the request, based either on $wgRequestTime or
-        * $wgRUstart. Will return null if not able to find data.
+        * The percent time for each time is based on the current "total" time
+        * used is based on all methods so far. This method can therefore be
+        * called several times in between several profiling calls without the
+        * delays in usage of the profiler skewing the results. A "-total" entry
+        * is always included in the results.
         *
-        * @param string|bool $metric Metric to use, with the following possibilities:
-        *   - user: User CPU time (without system calls)
-        *   - cpu: Total CPU time (user and system calls)
-        *   - wall (or any other string): elapsed time
-        *   - false (default): will fall back to default metric
-        * @return float|null
-        */
-       protected function getTime( $metric = 'wall' ) {
-               if ( $metric === 'cpu' || $metric === 'user' ) {
-                       $ru = wfGetRusage();
-                       if ( !$ru ) {
-                               return 0;
-                       }
-                       $time = $ru['ru_utime.tv_sec'] + $ru['ru_utime.tv_usec'] / 1e6;
-                       if ( $metric === 'cpu' ) {
-                               # This is the time of system calls, added to the user time
-                               # it gives the total CPU time
-                               $time += $ru['ru_stime.tv_sec'] + $ru['ru_stime.tv_usec'] / 1e6;
-                       }
-                       return $time;
-               } else {
-                       return microtime( true );
-               }
-       }
+        * When a call chain involves a method invoked within itself, any
+        * entries for the cyclic invocation should be be demarked with "@".
+        * This makes filtering them out easier and follows the xhprof style.
+        *
+        * @return array List of method entries arrays, each having:
+        *   - name    : method name
+        *   - calls   : the number of invoking calls
+        *   - real    : real time ellapsed (ms)
+        *   - %real   : percent real time
+        *   - cpu     : CPU time ellapsed (ms)
+        *   - %cpu    : percent CPU time
+        *   - memory  : memory used (bytes)
+        *   - %memory : percent memory used
+        * @since 1.25
+        */
+       abstract public function getFunctionStats();
 
        /**
-        * Get the initial time of the request, based either on $wgRequestTime or
-        * $wgRUstart. Will return null if not able to find data.
+        * Returns a profiling output to be stored in debug file
         *
-        * @param string|bool $metric Metric to use, with the following possibilities:
-        *   - user: User CPU time (without system calls)
-        *   - cpu: Total CPU time (user and system calls)
-        *   - wall (or any other string): elapsed time
-        *   - false (default): will fall back to default metric
-        * @return float|null
-        */
-       protected function getInitialTime( $metric = 'wall' ) {
-               global $wgRequestTime, $wgRUstart;
-
-               if ( $metric === 'cpu' || $metric === 'user' ) {
-                       if ( !count( $wgRUstart ) ) {
-                               return null;
-                       }
-
-                       $time = $wgRUstart['ru_utime.tv_sec'] + $wgRUstart['ru_utime.tv_usec'] / 1e6;
-                       if ( $metric === 'cpu' ) {
-                               # This is the time of system calls, added to the user time
-                               # it gives the total CPU time
-                               $time += $wgRUstart['ru_stime.tv_sec'] + $wgRUstart['ru_stime.tv_usec'] / 1e6;
-                       }
-                       return $time;
-               } else {
-                       if ( empty( $wgRequestTime ) ) {
-                               return null;
-                       } else {
-                               return $wgRequestTime;
-                       }
-               }
-       }
+        * @return string
+        */
+       abstract public function getOutput();
 }
diff --git a/includes/profiler/ProfilerSimpleDB.php b/includes/profiler/ProfilerSimpleDB.php
deleted file mode 100644 (file)
index 911b926..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-<?php
-/**
- * Profiler storing information in the DB.
- *
- * 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 Profiler
- */
-
-/**
- * $wgProfiler['class'] = 'ProfilerSimpleDB';
- *
- * @ingroup Profiler
- */
-class ProfilerSimpleDB extends ProfilerStandard {
-       /**
-        * Log the whole profiling data into the database.
-        */
-       public function logData() {
-               global $wgProfilePerHost;
-
-               # Do not log anything if database is readonly (bug 5375)
-               if ( wfReadOnly() ) {
-                       return;
-               }
-
-               if ( $wgProfilePerHost ) {
-                       $pfhost = wfHostname();
-               } else {
-                       $pfhost = '';
-               }
-
-               try {
-                       $this->collateData();
-
-                       $dbw = wfGetDB( DB_MASTER );
-                       $useTrx = ( $dbw->getType() === 'sqlite' ); // much faster
-                       if ( $useTrx ) {
-                               $dbw->startAtomic( __METHOD__ );
-                       }
-                       foreach ( $this->collated as $name => $data ) {
-                               $eventCount = $data['count'];
-                               $timeSum = (float)( $data['real'] * 1000 );
-                               $memorySum = (float)$data['memory'];
-                               $name = substr( $name, 0, 255 );
-
-                               // Kludge
-                               $timeSum = $timeSum >= 0 ? $timeSum : 0;
-                               $memorySum = $memorySum >= 0 ? $memorySum : 0;
-
-                               $dbw->update( 'profiling',
-                                       array(
-                                               "pf_count=pf_count+{$eventCount}",
-                                               "pf_time=pf_time+{$timeSum}",
-                                               "pf_memory=pf_memory+{$memorySum}",
-                                       ),
-                                       array(
-                                               'pf_name' => $name,
-                                               'pf_server' => $pfhost,
-                                       ),
-                                       __METHOD__ );
-
-                               $rc = $dbw->affectedRows();
-                               if ( $rc == 0 ) {
-                                       $dbw->insert( 'profiling',
-                                               array(
-                                                       'pf_name' => $name,
-                                                       'pf_count' => $eventCount,
-                                                       'pf_time' => $timeSum,
-                                                       'pf_memory' => $memorySum,
-                                                       'pf_server' => $pfhost
-                                               ),
-                                               __METHOD__,
-                                               array( 'IGNORE' )
-                                       );
-                               }
-                               // When we upgrade to mysql 4.1, the insert+update
-                               // can be merged into just a insert with this construct added:
-                               //     "ON DUPLICATE KEY UPDATE ".
-                               //     "pf_count=pf_count + VALUES(pf_count), ".
-                               //     "pf_time=pf_time + VALUES(pf_time)";
-                       }
-                       if ( $useTrx ) {
-                               $dbw->endAtomic( __METHOD__ );
-                       }
-               } catch ( DBError $e ) {
-               }
-       }
-}
diff --git a/includes/profiler/ProfilerSimpleText.php b/includes/profiler/ProfilerSimpleText.php
deleted file mode 100644 (file)
index 264845e..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-<?php
-/**
- * Profiler showing output in page source.
- *
- * 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 Profiler
- */
-
-/**
- * The least sophisticated profiler output class possible, view your source! :)
- *
- * Put the following 2 lines in StartProfiler.php:
- *
- * $wgProfiler['class'] = 'ProfilerSimpleText';
- * $wgProfiler['visible'] = true;
- *
- * @ingroup Profiler
- */
-class ProfilerSimpleText extends ProfilerStandard {
-       public $visible = false; /* Show as <PRE> or <!-- ? */
-
-       public function __construct( $profileConfig ) {
-               if ( isset( $profileConfig['visible'] ) && $profileConfig['visible'] ) {
-                       $this->visible = true;
-               }
-               parent::__construct( $profileConfig );
-       }
-
-       public function logData() {
-               $out = '';
-               if ( $this->templated ) {
-                       $this->close();
-                       $totalReal = isset( $this->collated['-total'] )
-                               ? $this->collated['-total']['real']
-                               : 0; // profiling mismatch error?
-
-                       uasort( $this->collated, function( $a, $b ) {
-                               // sort descending by time elapsed
-                               return $a['real'] < $b['real'];
-                       } );
-
-                       array_walk( $this->collated,
-                               function( $item, $key ) use ( &$out, $totalReal ) {
-                                       $perc = $totalReal ? $item['real'] / $totalReal * 100 : 0;
-                                       $out .= sprintf( "%6.2f%% %3.6f %6d - %s\n",
-                                               $perc, $item['real'], $item['count'], $key );
-                               }
-                       );
-
-                       $contentType = $this->getContentType();
-                       if ( PHP_SAPI === 'cli' ) {
-                               print "<!--\n{$out}\n-->\n";
-                       } elseif ( $contentType === 'text/html' ) {
-                               if ( $this->visible ) {
-                                       print "<pre>{$out}</pre>";
-                               } else {
-                                       print "<!--\n{$out}\n-->\n";
-                               }
-                       } elseif ( $contentType === 'text/javascript' ) {
-                               print "\n/*\n${$out}*/\n";
-                       } elseif ( $contentType === 'text/css' ) {
-                               print "\n/*\n{$out}*/\n";
-                       }
-               }
-       }
-}
diff --git a/includes/profiler/ProfilerSimpleUDP.php b/includes/profiler/ProfilerSimpleUDP.php
deleted file mode 100644 (file)
index ad16a18..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-<?php
-/**
- * Profiler sending messages over UDP.
- *
- * 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 Profiler
- */
-
-/**
- * ProfilerSimpleUDP class, that sends out messages for 'udpprofile' daemon
- * (the one from
- *  http://git.wikimedia.org/tree/operations%2Fsoftware.git/master/udpprofile)
- * @ingroup Profiler
- */
-class ProfilerSimpleUDP extends ProfilerStandard {
-       public function logData() {
-               global $wgUDPProfilerHost, $wgUDPProfilerPort, $wgUDPProfilerFormatString;
-
-               $this->close();
-
-               if ( !function_exists( 'socket_create' ) ) {
-                       # Sockets are not enabled
-                       return;
-               }
-
-               $sock = socket_create( AF_INET, SOCK_DGRAM, SOL_UDP );
-               $plength = 0;
-               $packet = "";
-               foreach ( $this->collated as $entry => $pfdata ) {
-                       if ( !isset( $pfdata['count'] )
-                               || !isset( $pfdata['cpu'] )
-                               || !isset( $pfdata['cpu_sq'] )
-                               || !isset( $pfdata['real'] )
-                               || !isset( $pfdata['real_sq'] ) ) {
-                               continue;
-                       }
-                       $pfline = sprintf( $wgUDPProfilerFormatString, $this->getProfileID(), $pfdata['count'],
-                               $pfdata['cpu'], $pfdata['cpu_sq'], $pfdata['real'], $pfdata['real_sq'], $entry,
-                               $pfdata['memory'] );
-                       $length = strlen( $pfline );
-                       /* printf("<!-- $pfline -->"); */
-                       if ( $length + $plength > 1400 ) {
-                               socket_sendto( $sock, $packet, $plength, 0, $wgUDPProfilerHost, $wgUDPProfilerPort );
-                               $packet = "";
-                               $plength = 0;
-                       }
-                       $packet .= $pfline;
-                       $plength += $length;
-               }
-               socket_sendto( $sock, $packet, $plength, 0x100, $wgUDPProfilerHost, $wgUDPProfilerPort );
-       }
-}
index b873806..ab5e3ab 100644 (file)
@@ -227,6 +227,15 @@ class ProfilerStandard extends Profiler {
                }
        }
 
+       public function scopedProfileIn( $section ) {
+               $this->profileIn( $section );
+
+               $that = $this;
+               return new ScopedCallback( function() use ( $that, $section ) {
+                       $that->profileOut( $section );
+               } );
+       }
+
        /**
         * Close opened profiling sections
         */
@@ -236,13 +245,6 @@ class ProfilerStandard extends Profiler {
                }
        }
 
-       /**
-        * Log the data to some store or even the page output
-        */
-       public function logData() {
-               /* Implement in subclasses */
-       }
-
        /**
         * Returns a profiling output to be stored in debug file
         *
@@ -330,6 +332,17 @@ class ProfilerStandard extends Profiler {
                        trim( sprintf( "%7.3f", $delta * 1000.0 ) ), $space, $fname );
        }
 
+       /**
+        * Return the collated data, collating first if need be
+        * @return array
+        */
+       public function getCollatedData() {
+               if ( !$this->collateDone ) {
+                       $this->collateData();
+               }
+               return $this->collated;
+       }
+
        /**
         * Populate collated
         */
@@ -439,10 +452,7 @@ class ProfilerStandard extends Profiler {
                return $prof;
        }
 
-       /**
-        * @return array
-        */
-       public function getRawData() {
+       public function getFunctionStats() {
                // This method is called before shutdown in the footer method on Skins.
                // If some outer methods have not yet called wfProfileOut(), work around
                // that by clearing anything in the work stack to just the "-total" entry.
@@ -463,28 +473,29 @@ class ProfilerStandard extends Profiler {
                        $this->collateDone = false;
                }
 
-               $total = isset( $this->collated['-total'] )
+               $totalCpu = isset( $this->collated['-total'] )
+                       ? $this->collated['-total']['cpu']
+                       : 0;
+               $totalReal = isset( $this->collated['-total'] )
                        ? $this->collated['-total']['real']
                        : 0;
+               $totalMem = isset( $this->collated['-total'] )
+                       ? $this->collated['-total']['memory']
+                       : 0;
 
                $profile = array();
                foreach ( $this->collated as $fname => $data ) {
-                       $periods = array();
-                       foreach ( $data['periods'] as $period ) {
-                               $period['start'] *= 1000;
-                               $period['end'] *= 1000;
-                               $periods[] = $period;
-                       }
                        $profile[] = array(
                                'name' => $fname,
                                'calls' => $data['count'],
-                               'elapsed' => $data['real'] * 1000,
-                               'percent' => $total ? 100 * $data['real'] / $total : 0,
+                               'real' => $data['real'] * 1000,
+                               '%real' => $totalReal ? 100 * $data['real'] / $totalReal : 0,
+                               'cpu' => $data['cpu'] * 1000,
+                               '%cpu' => $totalCpu ? 100 * $data['cpu'] / $totalCpu : 0,
                                'memory' => $data['memory'],
+                               '%memory' => $totalMem ? 100 * $data['memory'] / $totalMem : 0,
                                'min' => $data['min_real'] * 1000,
-                               'max' => $data['max_real'] * 1000,
-                               'overhead' => $data['overhead'],
-                               'periods' => $periods
+                               'max' => $data['max_real'] * 1000
                        );
                }
 
@@ -522,17 +533,67 @@ class ProfilerStandard extends Profiler {
        }
 
        /**
-        * Get the content type sent out to the client.
-        * Used for profilers that output instead of store data.
-        * @return string
+        * Get the initial time of the request, based either on $wgRequestTime or
+        * $wgRUstart. Will return null if not able to find data.
+        *
+        * @param string|bool $metric Metric to use, with the following possibilities:
+        *   - user: User CPU time (without system calls)
+        *   - cpu: Total CPU time (user and system calls)
+        *   - wall (or any other string): elapsed time
+        *   - false (default): will fall back to default metric
+        * @return float|null
+        */
+       protected function getTime( $metric = 'wall' ) {
+               if ( $metric === 'cpu' || $metric === 'user' ) {
+                       $ru = wfGetRusage();
+                       if ( !$ru ) {
+                               return 0;
+                       }
+                       $time = $ru['ru_utime.tv_sec'] + $ru['ru_utime.tv_usec'] / 1e6;
+                       if ( $metric === 'cpu' ) {
+                               # This is the time of system calls, added to the user time
+                               # it gives the total CPU time
+                               $time += $ru['ru_stime.tv_sec'] + $ru['ru_stime.tv_usec'] / 1e6;
+                       }
+                       return $time;
+               } else {
+                       return microtime( true );
+               }
+       }
+
+       /**
+        * Get the initial time of the request, based either on $wgRequestTime or
+        * $wgRUstart. Will return null if not able to find data.
+        *
+        * @param string|bool $metric Metric to use, with the following possibilities:
+        *   - user: User CPU time (without system calls)
+        *   - cpu: Total CPU time (user and system calls)
+        *   - wall (or any other string): elapsed time
+        *   - false (default): will fall back to default metric
+        * @return float|null
         */
-       protected function getContentType() {
-               foreach ( headers_list() as $header ) {
-                       if ( preg_match( '#^content-type: (\w+/\w+);?#i', $header, $m ) ) {
-                               return $m[1];
+       protected function getInitialTime( $metric = 'wall' ) {
+               global $wgRequestTime, $wgRUstart;
+
+               if ( $metric === 'cpu' || $metric === 'user' ) {
+                       if ( !count( $wgRUstart ) ) {
+                               return null;
+                       }
+
+                       $time = $wgRUstart['ru_utime.tv_sec'] + $wgRUstart['ru_utime.tv_usec'] / 1e6;
+                       if ( $metric === 'cpu' ) {
+                               # This is the time of system calls, added to the user time
+                               # it gives the total CPU time
+                               $time += $wgRUstart['ru_stime.tv_sec'] + $wgRUstart['ru_stime.tv_usec'] / 1e6;
+                       }
+                       return $time;
+               } else {
+                       if ( empty( $wgRequestTime ) ) {
+                               return null;
+                       } else {
+                               return $wgRequestTime;
                        }
                }
-               return null;
        }
 
        /**
index 43e2193..6fc74ef 100644 (file)
@@ -37,20 +37,20 @@ class ProfilerStub extends Profiler {
        public function profileOut( $fn ) {
        }
 
-       public function getOutput() {
+       public function scopedProfileIn( $section ) {
+               return null;
        }
 
-       public function close() {
+       public function getFunctionStats() {
        }
 
-       public function logData() {
+       public function getOutput() {
        }
 
-       public function getCurrentSection() {
-               return '';
+       public function close() {
        }
 
-       public function getRawData() {
-               return array();
+       public function getCurrentSection() {
+               return '';
        }
 }
index 5e70aa9..cbd081d 100644 (file)
 /**
  * Profiler wrapper for XHProf extension.
  *
- * Mimics the output of ProfilerSimpleText, ProfilerSimpleUDP or
- * ProfilerSimpleTrace but using data collected via the XHProf PHP extension.
- * This Profiler also produces data compatable with the debugging toolbar.
+ * Mimics the output of ProfilerStandard using data collected via the XHProf
+ * PHP extension.
  *
- * To mimic ProfilerSimpleText results:
  * @code
  * $wgProfiler['class'] = 'ProfilerXhprof';
  * $wgProfiler['flags'] = XHPROF_FLAGS_NO_BUILTINS;
- * $wgProfiler['log'] = 'text';
+ * $wgProfiler['output'] = 'text';
  * $wgProfiler['visible'] = true;
  * @endcode
  *
- * To mimic ProfilerSimpleUDP results:
  * @code
  * $wgProfiler['class'] = 'ProfilerXhprof';
  * $wgProfiler['flags'] = XHPROF_FLAGS_CPU | XHPROF_FLAGS_MEMORY | XHPROF_FLAGS_NO_BUILTINS;
- * $wgProfiler['log'] = 'udpprofile';
- * @endcode
- *
- * Similar to ProfilerSimpleTrace results, report the most expensive path
- * through the application:
- * @code
- * $wgProfiler['class'] = 'ProfilerXhprof';
- * $wgProfiler['flags'] = XHPROF_FLAGS_NO_BUILTINS;
- * $wgProfiler['log'] = 'critpath';
- * $wgProfiler['visible'] = true;
+ * $wgProfiler['output'] = 'udp';
  * @endcode
  *
  * Rather than obeying wfProfileIn() and wfProfileOut() calls placed in the
  * To restrict the functions for which profiling data is collected, you can
  * use either a whitelist ($wgProfiler['include']) or a blacklist
  * ($wgProfiler['exclude']) containing an array of function names. The
- * blacklist funcitonality is built into HHVM and will completely exclude the
+ * blacklist functionality is built into HHVM and will completely exclude the
  * named functions from profiling collection. The whitelist is implemented by
- * Xhprof class and will filter the data collected by XHProf before reporting.
+ * Xhprof class which will filter the data collected by XHProf before reporting.
  * See documentation for the Xhprof class and the XHProf extension for
  * additional information.
  *
- * Data reported to debug toolbar via getRawData() can be restricted by
- * setting $wgProfiler['toolbarCutoff'] to a minumum cumulative wall clock
- * percentage. Functions in the call graph which contribute less than this
- * percentage to the total wall clock time measured will be excluded from the
- * data sent to debug toolbar. The default cutoff is 0.1 (1/1000th of the
- * total time measured).
- *
  * @author Bryan Davis <bd808@wikimedia.org>
  * @copyright © 2014 Bryan Davis and Wikimedia Foundation.
  * @ingroup Profiler
@@ -97,13 +78,6 @@ class ProfilerXhprof extends Profiler {
         */
        protected $visible;
 
-       /**
-        * Minimum wall time precentage for a fucntion to be reported to the debug
-        * toolbar via getRawData().
-        * @var float $toolbarCutoff
-        */
-       protected $toolbarCutoff;
-
        /**
         * @param array $params
         * @see Xhprof::__construct()
@@ -112,8 +86,7 @@ class ProfilerXhprof extends Profiler {
                $params = array_merge(
                        array(
                                'log' => 'text',
-                               'visible' => false,
-                               'toolbarCutoff' => 0.1,
+                               'visible' => false
                        ),
                        $params
                );
@@ -136,10 +109,6 @@ class ProfilerXhprof extends Profiler {
         * @param string $functionname
         */
        public function profileIn( $functionname ) {
-               global $wgDebugFunctionEntry;
-               if ( $wgDebugFunctionEntry ) {
-                       $this->debug( "Entering {$functionname}" );
-               }
        }
 
        /**
@@ -151,10 +120,23 @@ class ProfilerXhprof extends Profiler {
         * @param string $functionname
         */
        public function profileOut( $functionname ) {
-               global $wgDebugFunctionEntry;
-               if ( $wgDebugFunctionEntry ) {
-                       $this->debug( "Exiting {$functionname}" );
+       }
+
+       public function scopedProfileIn( $section ) {
+               static $exists = null;
+               // Only HHVM supports this, not the standard PECL extension
+               if ( $exists === null ) {
+                       $exists = function_exists( 'xhprof_frame_begin' );
                }
+
+               if ( $exists ) {
+                       xhprof_frame_begin( $section );
+                       return new ScopedCallback( function() use ( $section ) {
+                               xhprof_frame_end( $section );
+                       } );
+               }
+
+               return null;
        }
 
        /**
@@ -163,191 +145,27 @@ class ProfilerXhprof extends Profiler {
        public function close() {
        }
 
-       /**
-        * Get data for the debugging toolbar.
-        *
-        * @return array
-        * @see https://www.mediawiki.org/wiki/Debugging_toolbar
-        */
-       public function getRawData() {
+       public function getFunctionStats() {
                $metrics = $this->xhprof->getCompleteMetrics();
-               $endEpoch = $this->getTime();
                $profile = array();
 
                foreach ( $metrics as $fname => $stats ) {
-                       if ( $stats['wt']['percent'] < $this->toolbarCutoff ) {
-                               // Ignore functions which are not significant contributors
-                               // to the total elapsed time.
-                               continue;
-                       }
-
-                       $record = array(
+                       // Convert elapsed times from μs to ms to match ProfilerStandard
+                       $profile[] = array(
                                'name' => $fname,
                                'calls' => $stats['ct'],
-                               'elapsed' => $stats['wt']['total'] / 1000,
-                               'percent' => $stats['wt']['percent'],
+                               'real' => $stats['wt']['total'] / 1000,
+                               '%real' => $stats['wt']['percent'],
+                               'cpu' => isset( $stats['cpu'] ) ? $stats['cpu']['total'] / 1000 : 0,
+                               '%cpu' => isset( $stats['cpu'] ) ? $stats['cpu']['percent'] : 0,
                                'memory' => isset( $stats['mu'] ) ? $stats['mu']['total'] : 0,
+                               '%memory' => isset( $stats['mu'] ) ? $stats['mu']['percent'] : 0,
                                'min' => $stats['wt']['min'] / 1000,
-                               'max' => $stats['wt']['max'] / 1000,
-                               'overhead' => array_reduce(
-                                       $stats['subcalls'],
-                                       function( $carry, $item ) {
-                                               return $carry + $item['ct'];
-                                       },
-                                       0
-                               ),
-                               'periods' => array(),
-                       );
-
-                       // We are making up periods based on the call segments we have.
-                       // What we don't have is the start time for each call (which
-                       // actually may be a collection of multiple calls from the
-                       // caller). We will pretend that all the calls happen sequentially
-                       // and finish at the end of the end of the request.
-                       foreach ( $stats['calls'] as $call ) {
-                               $avgElapsed = $call['wt'] / 1000 / $call['ct'];
-                               $avgMem = isset( $call['mu'] ) ? $call['mu'] / $call['ct'] : 0;
-                               $start = $endEpoch - $avgElapsed;
-                               for ( $i = 0; $i < $call['ct']; $i++ ) {
-                                       $callStart = $start + ( $avgElapsed * $i );
-                                       $record['periods'][] = array(
-                                               'start' => $callStart,
-                                               'end' => $callStart + $avgElapsed,
-                                               'memory' => $avgMem,
-                                               'subcalls' => 0,
-                                       );
-                               }
-                       }
-
-                       $profile[] = $record;
-               }
-               return $profile;
-       }
-
-       /**
-        * Log the data to some store or even the page output.
-        */
-       public function logData() {
-               if ( $this->logType === 'text' ) {
-                       $this->logText();
-               } elseif ( $this->logType === 'udpprofile' ) {
-                       $this->logToUdpprofile();
-               } elseif ( $this->logType === 'critpath' ){
-                       $this->logCriticalPath();
-               } else {
-                       wfLogWarning(
-                               "Unknown ProfilerXhprof log type '{$this->logType}'"
-                       );
-               }
-       }
-
-       /**
-        * Write a brief profile report to stdout.
-        */
-       protected function logText() {
-               if ( $this->templated ) {
-                       $ct = $this->getContentType();
-                       if ( $ct === 'text/html' && $this->visible ) {
-                               $prefix = '<pre>';
-                               $suffix = '</pre>';
-                       } elseif ( $ct === 'text/javascript' || $ct === 'text/css' ) {
-                               $prefix = "\n/*";
-                               $suffix = "*/\n";
-                       } else {
-                               $prefix = "<!--";
-                               $suffix = "-->\n";
-                       }
-
-                       print $this->getSummaryReport( $prefix, $suffix );
-               }
-       }
-
-       /**
-        * Send collected information to a udpprofile daemon.
-        *
-        * @see http://git.wikimedia.org/tree/operations%2Fsoftware.git/master/udpprofile
-        * @see $wgUDPProfilerHost
-        * @see $wgUDPProfilerPort
-        * @see $wgUDPProfilerFormatString
-        */
-       protected function logToUdpprofile() {
-               global $wgUDPProfilerHost, $wgUDPProfilerPort;
-               global $wgUDPProfilerFormatString;
-
-               if ( !function_exists( 'socket_create' ) ) {
-                       return;
-               }
-
-               $metrics = $this->xhprof->getInclusiveMetrics();
-
-               $sock = socket_create( AF_INET, SOCK_DGRAM, SOL_UDP );
-               $buffer = '';
-               $bufferSize = 0;
-               foreach ( $metrics as $func => $data ) {
-                       if ( strpos( $func, '@' ) !== false ) {
-                               continue; // ignore cyclic re-entries to functions
-                       }
-                       $line = sprintf( $wgUDPProfilerFormatString,
-                               $this->getProfileID(),
-                               $data['ct'],
-                               isset( $data['cpu'] ) ? $data['cpu']['total'] : 0,
-                               isset( $data['cpu'] ) ? $data['cpu']['variance'] : 0,
-                               $data['wt']['total'] / 1000,
-                               $data['wt']['variance'],
-                               $func,
-                               isset( $data['mu'] ) ? $data['mu']['total'] : 0
+                               'max' => $stats['wt']['max'] / 1000
                        );
-                       $lineLength = strlen( $line );
-                       if ( $lineLength + $bufferSize > 1400 ) {
-                               // Line would exceed max packet size, send packet before
-                               // appending to buffer.
-                               socket_sendto( $sock, $buffer, $bufferSize, 0,
-                                       $wgUDPProfilerHost, $wgUDPProfilerPort
-                               );
-                               $buffer = '';
-                               $bufferSize = 0;
-                       }
-                       $buffer .= $line;
-                       $bufferSize += $lineLength;
                }
-               // Send final buffer
-               socket_sendto( $sock, $buffer, $bufferSize, 0x100 /* EOF */,
-                       $wgUDPProfilerHost, $wgUDPProfilerPort
-               );
-       }
-
-       /**
-        * Write a critical path report to stdout.
-        */
-       protected function logCriticalPath() {
-               if ( $this->templated ) {
-                       $ct = $this->getContentType();
-                       if ( $ct === 'text/html' && $this->visible ) {
-                               $prefix = '<pre>Critical path:';
-                               $suffix = '</pre>';
-                       } elseif ( $ct === 'text/javascript' || $ct === 'text/css' ) {
-                               $prefix = "\n/* Critical path:";
-                               $suffix = "*/\n";
-                       } else {
-                               $prefix = "<!-- Critical path:";
-                               $suffix = "-->\n";
-                       }
 
-                       print $this->getCriticalPathReport( $prefix, $suffix );
-               }
-       }
-
-       /**
-        * Get the content type of the current request.
-        * @return string
-        */
-       protected function getContentType() {
-               foreach ( headers_list() as $header ) {
-                       if ( preg_match( '#^content-type: (\w+/\w+);?#i', $header, $m ) ) {
-                               return $m[1];
-                       }
-               }
-               return 'application/octet-stream';
+               return $profile;
        }
 
        /**
@@ -432,46 +250,4 @@ class ProfilerXhprof extends Profiler {
                $out[] = $footer;
                return implode( "\n", $out );
        }
-
-       /**
-        * Get a brief report of the most costly code path by wall clock time.
-        *
-        * Each line of the report includes this data:
-        * - Total wall clock time spent in function in seconds
-        * - Function name
-        *
-        * @param string $header Header text to prepend to report
-        * @param string $footer Footer text to append to report
-        * @return string
-        */
-       protected function getCriticalPathReport( $header = '', $footer = '' ) {
-               $data = $this->xhprof->getCriticalPath();
-
-               $out = array( $header );
-               $out[] = sprintf( "%7s %9s %9s %6s %4s",
-                       'Time%', 'Time', 'Mem', 'Calls', 'Name'
-               );
-
-               $format = '%6.2f%% %9.6f %9d %6d %s%s';
-               $total = null;
-               $nest = 0;
-               foreach ( $data as $key => $stats ) {
-                       list( $parent, $child ) = Xhprof::splitKey( $key );
-                       if ( $total === null ) {
-                               $total = $stats;
-                       }
-                       $out[] = sprintf( $format,
-                               100 * $stats['wt'] / $total['wt'],
-                               $stats['wt'] / 1e6,
-                               isset( $stats['mu'] ) ? $stats['mu'] : 0,
-                               $stats['ct'],
-                               //$nest ? str_repeat( ' ', $nest - 1 ) . '┗ ' : '',
-                               $nest ? str_repeat( ' ', $nest - 1 ) . '└─' : '',
-                               $child
-                       );
-                       $nest++;
-               }
-               $out[] = $footer;
-               return implode( "\n", $out );
-       }
 }
index a418d30..89eebbe 100644 (file)
 /**
  * Custom PHP profiler for parser/DB type section names that xhprof/xdebug can't handle
  *
- * @TODO: refactor implementation by moving Profiler code to here when non-automatic
- * profiler support is dropped.
- *
  * @since 1.25
  */
 class SectionProfiler {
-       /** @var ProfilerStandard */
-       protected $profiler;
+       /** @var array List of resolved profile calls with start/end data */
+       protected $stack = array();
+       /** @var array Queue of open profile calls with start data */
+       protected $workStack = array();
+
+       /** @var array Map of (function name => aggregate data array) */
+       protected $collated = array();
+       /** @var bool */
+       protected $collateDone = false;
+       /** @var bool Whether to collect the full stack trace or just aggregates */
+       protected $collateOnly = true;
 
-       public function __construct() {
-               // This does *not* care about PHP request start time
-               $this->profiler = new ProfilerStandard( array( 'initTotal' => false ) );
+       /** @var array Cache of a standard broken collation entry */
+       protected $errorEntry;
+
+       /**
+        * @param array $params
+        */
+       public function __construct( array $params = array() ) {
+               $this->errorEntry = $this->getErrorEntry();
+               $this->collateOnly = empty( $params['trace'] );
        }
 
        /**
@@ -44,13 +56,12 @@ class SectionProfiler {
         * @return ScopedCallback
         */
        public function scopedProfileIn( $section ) {
-               $profiler = $this->profiler;
-               $sc = new ScopedCallback( function() use ( $profiler, $section ) {
-                       $profiler->profileOut( $section );
-               } );
-               $profiler->profileIn( $section );
+               $this->profileInInternal( $section );
 
-               return $sc;
+               $that = $this;
+               return new ScopedCallback( function() use ( $that, $section ) {
+                       $that->profileOutInternal( $section );
+               } );
        }
 
        /**
@@ -61,7 +72,7 @@ class SectionProfiler {
        }
 
        /**
-        * Get the raw and collated breakdown data for each method
+        * Get the aggregated inclusive profiling data for each method
         *
         * The percent time for each time is based on the current "total" time
         * used is based on all methods so far. This method can therefore be
@@ -70,39 +81,375 @@ class SectionProfiler {
         * is always included in the results.
         *
         * @return array List of method entries arrays, each having:
-        *   - name     : method name
-        *   - calls    : the number of method calls
-        *   - elapsed  : real time ellapsed (ms)
-        *   - percent  : percent real time
-        *   - memory   : memory used (bytes)
-        *   - min      : min real time of all calls (ms)
-        *   - max      : max real time of all calls (ms)
+        *   - name    : method name
+        *   - calls   : the number of invoking calls
+        *   - real    : real time ellapsed (ms)
+        *   - %real   : percent real time
+        *   - cpu     : real time ellapsed (ms)
+        *   - %cpu    : percent real time
+        *   - memory  : memory used (bytes)
+        *   - %memory : percent memory used
         */
        public function getFunctionStats() {
-               $data = $this->profiler->getRawData();
+               $this->collateData();
 
-               $memoryTotal = 0;
-               $elapsedTotal = 0;
-               foreach ( $data as $item ) {
-                       $memoryTotal += $item['memory'];
-                       $elapsedTotal += $item['elapsed'];
+               $totalCpu = 0.0;
+               $totalReal = 0.0;
+               $totalMem = 0;
+               foreach ( $this->collated as $fname => $data ) {
+                       $totalCpu += $data['cpu'];
+                       $totalReal += $data['real'];
+                       $totalMem += $data['memory'];
                }
 
-               foreach ( $data as &$item ) {
-                       $item['percent'] = $item['elapsed'] / $elapsedTotal * 100;
+               $profile = array();
+               foreach ( $this->collated as $fname => $data ) {
+                       $profile[] = array(
+                               'name' => $fname,
+                               'calls' => $data['count'],
+                               'real' => $data['real'] * 1000,
+                               '%real' => $totalReal ? 100 * $data['real'] / $totalReal : 0,
+                               'cpu' => $data['cpu'] * 1000,
+                               '%cpu' => $totalCpu ? 100 * $data['cpu'] / $totalCpu : 0,
+                               'memory' => $data['memory'],
+                               '%memory' => $totalMem ? 100 * $data['memory'] / $totalMem : 0,
+                       );
                }
-               unset( $item );
 
-               $data[] = array(
+               $profile[] = array(
                        'name' => '-total',
                        'calls' => 1,
-                       'elapsed' => $elapsedTotal,
-                       'percent' => 100,
-                       'memory' => $memoryTotal,
-                       'min' => $elapsedTotal,
-                       'max' => $elapsedTotal
+                       'real' => 1000 * $totalReal,
+                       '%real' => 100,
+                       'cpu' => 1000 * $totalCpu,
+                       '%cpu' => 100,
+                       'memory' => $totalMem,
+                       '%memory' => 100,
+               );
+
+               return $profile;
+       }
+
+       /**
+        * @return array Initial collation entry
+        */
+       protected function getZeroEntry() {
+               return array(
+                       'cpu'      => 0.0,
+                       'real'     => 0.0,
+                       'memory'   => 0,
+                       'count'    => 0
+               );
+       }
+
+       /**
+        * @return array Initial collation entry for errors
+        */
+       protected function getErrorEntry() {
+               $entry = $this->getZeroEntry();
+               $entry['count'] = 1;
+               return $entry;
+       }
+
+       /**
+        * Update the collation entry for a given method name
+        *
+        * @param string $name
+        * @param float $elapsedCpu
+        * @param float $elapsedReal
+        * @param int $memChange
+        */
+       protected function updateEntry( $name, $elapsedCpu, $elapsedReal, $memChange ) {
+               $entry =& $this->collated[$name];
+               if ( !is_array( $entry ) ) {
+                       $entry = $this->getZeroEntry();
+                       $this->collated[$name] =& $entry;
+               }
+               $entry['cpu'] += $elapsedCpu;
+               $entry['real'] += $elapsedReal;
+               $entry['memory'] += $memChange > 0 ? $memChange : 0;
+               $entry['count']++;
+       }
+
+       /**
+        * This method should not be called outside SectionProfiler
+        *
+        * @param string $functionname
+        */
+       public function profileInInternal( $functionname ) {
+               $this->workStack[] = array(
+                       $functionname,
+                       count( $this->workStack ),
+                       $this->getTime( 'time' ),
+                       $this->getTime( 'cpu' ),
+                       memory_get_usage()
                );
+       }
+
+       /**
+        * This method should not be called outside SectionProfiler
+        *
+        * @param string $functionname
+        */
+       public function profileOutInternal( $functionname ) {
+               $item = array_pop( $this->workStack );
+               if ( $item === null ) {
+                       $this->debugGroup( 'profileerror', "Profiling error: $functionname" );
+                       return;
+               }
+               list( $ofname, /* $ocount */, $ortime, $octime, $omem ) = $item;
+
+               if ( $functionname === 'close' ) {
+                       $message = "Profile section ended by close(): {$ofname}";
+                       $this->debugGroup( 'profileerror', $message );
+                       if ( $this->collateOnly ) {
+                               $this->collated[$message] = $this->errorEntry;
+                       } else {
+                               $this->stack[] = array( $message, 0, 0.0, 0.0, 0, 0.0, 0.0, 0 );
+                       }
+                       $functionname = $ofname;
+               } elseif ( $ofname !== $functionname ) {
+                       $message = "Profiling error: in({$ofname}), out($functionname)";
+                       $this->debugGroup( 'profileerror', $message );
+                       if ( $this->collateOnly ) {
+                               $this->collated[$message] = $this->errorEntry;
+                       } else {
+                               $this->stack[] = array( $message, 0, 0.0, 0.0, 0, 0.0, 0.0, 0 );
+                       }
+               }
+               $realTime = $this->getTime( 'wall' );
+               $cpuTime = $this->getTime( 'cpu' );
+               if ( $this->collateOnly ) {
+                       $elapsedcpu = $cpuTime - $octime;
+                       $elapsedreal = $realTime - $ortime;
+                       $memchange = memory_get_usage() - $omem;
+                       $this->updateEntry( $functionname, $elapsedcpu, $elapsedreal, $memchange );
+               } else {
+                       $this->stack[] = array_merge( $item,
+                               array( $realTime, $cpuTime,     memory_get_usage() ) );
+               }
+       }
 
-               return $data;
+       /**
+        * Returns a tree of function calls with their real times
+        * @return string
+        */
+       public function getCallTreeReport() {
+               if ( $this->collateOnly ) {
+                       throw new Exception( "Tree is only available for trace profiling." );
+               }
+               return implode( '', array_map(
+                       array( $this, 'getCallTreeLine' ), $this->remapCallTree( $this->stack )
+               ) );
+       }
+
+       /**
+        * Recursive function the format the current profiling array into a tree
+        *
+        * @param array $stack Profiling array
+        * @return array
+        */
+       protected function remapCallTree( array $stack ) {
+               if ( count( $stack ) < 2 ) {
+                       return $stack;
+               }
+               $outputs = array();
+               for ( $max = count( $stack ) - 1; $max > 0; ) {
+                       /* Find all items under this entry */
+                       $level = $stack[$max][1];
+                       $working = array();
+                       for ( $i = $max -1; $i >= 0; $i-- ) {
+                               if ( $stack[$i][1] > $level ) {
+                                       $working[] = $stack[$i];
+                               } else {
+                                       break;
+                               }
+                       }
+                       $working = $this->remapCallTree( array_reverse( $working ) );
+                       $output = array();
+                       foreach ( $working as $item ) {
+                               array_push( $output, $item );
+                       }
+                       array_unshift( $output, $stack[$max] );
+                       $max = $i;
+
+                       array_unshift( $outputs, $output );
+               }
+               $final = array();
+               foreach ( $outputs as $output ) {
+                       foreach ( $output as $item ) {
+                               $final[] = $item;
+                       }
+               }
+               return $final;
+       }
+
+       /**
+        * Callback to get a formatted line for the call tree
+        * @param array $entry
+        * @return string
+        */
+       protected function getCallTreeLine( $entry ) {
+               // $entry has (name, level, stime, scpu, smem, etime, ecpu, emem)
+               list( $fname, $level, $startreal, , , $endreal ) = $entry;
+               $delta = $endreal - $startreal;
+               $space = str_repeat( ' ', $level );
+               # The ugly double sprintf is to work around a PHP bug,
+               # which has been fixed in recent releases.
+               return sprintf( "%10s %s %s\n",
+                       trim( sprintf( "%7.3f", $delta * 1000.0 ) ), $space, $fname );
+       }
+
+       /**
+        * Populate collated data
+        */
+       protected function collateData() {
+               if ( $this->collateDone ) {
+                       return;
+               }
+               $this->collateDone = true;
+               // Close opened profiling sections
+               while ( count( $this->workStack ) ) {
+                       $this->profileOutInternal( 'close' );
+               }
+
+               if ( $this->collateOnly ) {
+                       return; // already collated as methods exited
+               }
+
+               $this->collated = array();
+
+               # Estimate profiling overhead
+               $profileCount = count( $this->stack );
+               $this->calculateOverhead( $profileCount );
+
+               # First, subtract the overhead!
+               $overheadTotal = $overheadMemory = $overheadInternal = array();
+               foreach ( $this->stack as $entry ) {
+                       // $entry is (name,pos,rtime0,cputime0,mem0,rtime1,cputime1,mem1)
+                       $fname = $entry[0];
+                       $elapsed = $entry[5] - $entry[2];
+                       $memchange = $entry[7] - $entry[4];
+
+                       if ( $fname === '-overhead-total' ) {
+                               $overheadTotal[] = $elapsed;
+                               $overheadMemory[] = max( 0, $memchange );
+                       } elseif ( $fname === '-overhead-internal' ) {
+                               $overheadInternal[] = $elapsed;
+                       }
+               }
+               $overheadTotal = $overheadTotal ?
+                       array_sum( $overheadTotal ) / count( $overheadInternal ) : 0;
+               $overheadMemory = $overheadMemory ?
+                       array_sum( $overheadMemory ) / count( $overheadInternal ) : 0;
+               $overheadInternal = $overheadInternal ?
+                       array_sum( $overheadInternal ) / count( $overheadInternal ) : 0;
+
+               # Collate
+               foreach ( $this->stack as $index => $entry ) {
+                       // $entry is (name,pos,rtime0,cputime0,mem0,rtime1,cputime1,mem1)
+                       $fname = $entry[0];
+                       $elapsedCpu = $entry[6] - $entry[3];
+                       $elapsedReal = $entry[5] - $entry[2];
+                       $memchange = $entry[7] - $entry[4];
+                       $subcalls = $this->calltreeCount( $this->stack, $index );
+
+                       if ( substr( $fname, 0, 9 ) !== '-overhead' ) {
+                               # Adjust for profiling overhead (except special values with elapsed=0
+                               if ( $elapsed ) {
+                                       $elapsed -= $overheadInternal;
+                                       $elapsed -= ( $subcalls * $overheadTotal );
+                                       $memchange -= ( $subcalls * $overheadMemory );
+                               }
+                       }
+
+                       $this->updateEntry( $fname, $elapsedCpu, $elapsedReal, $memchange );
+               }
+
+               $this->collated['-overhead-total']['count'] = $profileCount;
+               arsort( $this->collated, SORT_NUMERIC );
+       }
+
+       /**
+        * Dummy calls to calculate profiling overhead
+        *
+        * @param int $profileCount
+        */
+       protected function calculateOverhead( $profileCount ) {
+               $this->profileInInternal( '-overhead-total' );
+               for ( $i = 0; $i < $profileCount; $i++ ) {
+                       $this->profileInInternal( '-overhead-internal' );
+                       $this->profileOutInternal( '-overhead-internal' );
+               }
+               $this->profileOutInternal( '-overhead-total' );
+       }
+
+       /**
+        * Counts the number of profiled function calls sitting under
+        * the given point in the call graph. Not the most efficient algo.
+        *
+        * @param array $stack
+        * @param int $start
+        * @return int
+        */
+       protected function calltreeCount( $stack, $start ) {
+               $level = $stack[$start][1];
+               $count = 0;
+               for ( $i = $start -1; $i >= 0 && $stack[$i][1] > $level; $i-- ) {
+                       $count ++;
+               }
+               return $count;
+       }
+
+       /**
+        * Get the initial time of the request, based either on $wgRequestTime or
+        * $wgRUstart. Will return null if not able to find data.
+        *
+        * @param string|bool $metric Metric to use, with the following possibilities:
+        *   - user: User CPU time (without system calls)
+        *   - cpu: Total CPU time (user and system calls)
+        *   - wall (or any other string): elapsed time
+        *   - false (default): will fall back to default metric
+        * @return float|null
+        */
+       protected function getTime( $metric = 'wall' ) {
+               if ( $metric === 'cpu' || $metric === 'user' ) {
+                       $ru = wfGetRusage();
+                       if ( !$ru ) {
+                               return 0;
+                       }
+                       $time = $ru['ru_utime.tv_sec'] + $ru['ru_utime.tv_usec'] / 1e6;
+                       if ( $metric === 'cpu' ) {
+                               # This is the time of system calls, added to the user time
+                               # it gives the total CPU time
+                               $time += $ru['ru_stime.tv_sec'] + $ru['ru_stime.tv_usec'] / 1e6;
+                       }
+                       return $time;
+               } else {
+                       return microtime( true );
+               }
+       }
+
+       /**
+        * Add an entry in the debug log file
+        *
+        * @param string $s String to output
+        */
+       protected function debug( $s ) {
+               if ( function_exists( 'wfDebug' ) ) {
+                       wfDebug( $s );
+               }
+       }
+
+       /**
+        * Add an entry in the debug log group
+        *
+        * @param string $group Group to send the message to
+        * @param string $s String to output
+        */
+       protected function debugGroup( $group, $s ) {
+               if ( function_exists( 'wfDebugLog' ) ) {
+                       wfDebugLog( $group, $s );
+               }
        }
 }
index 7843ac1..886bc5a 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * Transaction profiling.
+ * Transaction profiling for contention
  *
  * 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
 /**
  * Helper class that detects high-contention DB queries via profiling calls
  *
- * This class is meant to work with a Profiler, as the later already knows
- * when methods start and finish (which may take place during transactions).
+ * This class is meant to work with a DatabaseBase object, which manages queries
  *
  * @since 1.24
  */
 class TransactionProfiler {
        /** @var float Seconds */
        protected $dbLockThreshold = 3.0;
-       /** @var array DB/server name => (active trx count, time, DBs involved) */
+       /** @var float Seconds */
+       protected $eventThreshold = .25;
+
+       /** @var array transaction ID => (write start time, list of DBs involved) */
        protected $dbTrxHoldingLocks = array();
-       /** @var array DB/server name => list of (function name, elapsed time) */
+       /** @var array transaction ID => list of (query name, start time, end time) */
        protected $dbTrxMethodTimes = array();
 
        /**
@@ -54,7 +56,7 @@ class TransactionProfiler {
                }
                $this->dbTrxHoldingLocks[$name] = array(
                        'start' => microtime( true ),
-                       'conns' => array(),
+                       'conns' => array(), // all connections involved
                );
                $this->dbTrxMethodTimes[$name] = array();
 
@@ -67,26 +69,41 @@ class TransactionProfiler {
        /**
         * Register the name and time of a method for slow DB trx detection
         *
-        * This method is only to be called by the Profiler class as methods finish
+        * This assumes that all queries are synchronous (non-overlapping)
         *
-        * @param string $method Function name
-        * @param float $realtime Wall time ellapsed
+        * @param string $query Function name
+        * @param float $sTime Starting UNIX wall time
+        * @param bool $isWrite Whether this is a write query
         */
-       public function recordFunctionCompletion( $method, $realtime ) {
+       public function recordQueryCompletion( $query, $sTime, $isWrite = false ) {
+               $eTime = microtime( true );
+               $elapsed = ( $eTime - $sTime );
+
                if ( !$this->dbTrxHoldingLocks ) {
                        // Short-circuit
                        return;
-               // @todo hardcoded check is a tad janky
-               } elseif ( !preg_match( '/^query-m: /', $method ) && $realtime < 1.0 ) {
-                       // Not a DB master query nor slow enough
+               } elseif ( !$isWrite && $elapsed < $this->eventThreshold ) {
+                       // Not an important query nor slow enough
                        return;
                }
 
-               $now = microtime( true );
                foreach ( $this->dbTrxHoldingLocks as $name => $info ) {
-                       // Hacky check to exclude entries from before the first TRX write
-                       if ( ( $now - $realtime ) >= $info['start'] ) {
-                               $this->dbTrxMethodTimes[$name][] = array( $method, $realtime );
+                       $lastQuery = end( $this->dbTrxMethodTimes[$name] );
+                       if ( $lastQuery ) {
+                               // Additional query in the trx...
+                               $lastEnd = $lastQuery[2];
+                               if ( $sTime >= $lastEnd ) { // sanity check
+                                       if ( ( $sTime - $lastEnd ) > $this->eventThreshold ) {
+                                               // Add an entry representing the time spent doing non-queries
+                                               $this->dbTrxMethodTimes[$name][] = array( '...delay...', $lastEnd, $sTime );
+                                       }
+                                       $this->dbTrxMethodTimes[$name][] = array( $query, $sTime, $eTime );
+                               }
+                       } else {
+                               // First query in the trx...
+                               if ( $sTime >= $info['start'] ) { // sanity check
+                                       $this->dbTrxMethodTimes[$name][] = array( $query, $sTime, $eTime );
+                               }
                        }
                }
        }
@@ -108,10 +125,20 @@ class TransactionProfiler {
                        wfDebugLog( 'DBPerformance', "Detected no transaction for '$name' - out of sync." );
                        return;
                }
+               // Fill in the last non-query period...
+               $lastQuery = end( $this->dbTrxMethodTimes[$name] );
+               if ( $lastQuery ) {
+                       $now = microtime( true );
+                       $lastEnd = $lastQuery[2];
+                       if ( ( $now - $lastEnd ) > $this->eventThreshold ) {
+                               $this->dbTrxMethodTimes[$name][] = array( '...delay...', $lastEnd, $now );
+                       }
+               }
+               // Check for any slow queries or non-query periods...
                $slow = false;
                foreach ( $this->dbTrxMethodTimes[$name] as $info ) {
-                       $realtime = $info[1];
-                       if ( $realtime >= $this->dbLockThreshold ) {
+                       $elapsed = ( $info[2] - $info[1] );
+                       if ( $elapsed >= $this->dbLockThreshold ) {
                                $slow = true;
                                break;
                        }
@@ -120,8 +147,8 @@ class TransactionProfiler {
                        $dbs = implode( ', ', array_keys( $this->dbTrxHoldingLocks[$name]['conns'] ) );
                        $msg = "Sub-optimal transaction on DB(s) [{$dbs}]:\n";
                        foreach ( $this->dbTrxMethodTimes[$name] as $i => $info ) {
-                               list( $method, $realtime ) = $info;
-                               $msg .= sprintf( "%d\t%.6f\t%s\n", $i, $realtime, $method );
+                               list( $query, $sTime, $end ) = $info;
+                               $msg .= sprintf( "%d\t%.6f\t%s\n", $i, ( $end - $sTime ), $query );
                        }
                        wfDebugLog( 'DBPerformance', $msg );
                }
diff --git a/includes/profiler/output/ProfilerOutput.php b/includes/profiler/output/ProfilerOutput.php
new file mode 100644 (file)
index 0000000..3473e0b
--- /dev/null
@@ -0,0 +1,57 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Profiler
+ */
+
+/**
+ * Base class for profiling output
+ *
+ * Since 1.25
+ */
+abstract class ProfilerOutput {
+       /** @var Profiler */
+       protected $collector;
+       /** @var array Configuration of $wgProfiler */
+       protected $params = array();
+
+       /**
+        * Constructor
+        * @param Profiler $collector The actual profiler
+        * @param array $params Configuration array, passed down from $wgProfiler
+        */
+       public function __construct( Profiler $collector, array $params ) {
+               $this->collector = $collector;
+               $this->params = $params;
+       }
+
+       /**
+        * Can this output type be used?
+        * @return bool
+        */
+       public function canUse() {
+               return true;
+       }
+
+       /**
+        * Log MediaWiki-style profiling data
+        *
+        * @param array $stats Result of Profiler::getFunctionStats()
+        */
+       abstract public function log( array $stats );
+}
diff --git a/includes/profiler/output/ProfilerOutputDb.php b/includes/profiler/output/ProfilerOutputDb.php
new file mode 100644 (file)
index 0000000..f988238
--- /dev/null
@@ -0,0 +1,85 @@
+<?php
+/**
+ * Profiler storing information in the DB.
+ *
+ * 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 Profiler
+ */
+
+/**
+ * Logs profiling data into the local DB
+ *
+ * @ingroup Profiler
+ * @since 1.25
+ */
+class ProfilerOutputDb extends ProfilerOutput {
+       public function canUse() {
+               # Do not log anything if database is readonly (bug 5375)
+               return !wfReadOnly();
+       }
+
+       public function log( array $stats ) {
+               global $wgProfilePerHost;
+
+               if ( $wgProfilePerHost ) {
+                       $pfhost = wfHostname();
+               } else {
+                       $pfhost = '';
+               }
+
+               try {
+                       $dbw = wfGetDB( DB_MASTER );
+                       $useTrx = ( $dbw->getType() === 'sqlite' ); // much faster
+                       if ( $useTrx ) {
+                               $dbw->startAtomic( __METHOD__ );
+                       }
+                       foreach ( $stats as $data ) {
+                               $name = $data['name'];
+                               $eventCount = $data['calls'];
+                               $timeSum = (float)$data['real'];
+                               $memorySum = (float)$data['memory'];
+                               $name = substr( $name, 0, 255 );
+
+                               // Kludge
+                               $timeSum = $timeSum >= 0 ? $timeSum : 0;
+                               $memorySum = $memorySum >= 0 ? $memorySum : 0;
+
+                               $dbw->upsert( 'profiling',
+                                       array(
+                                               'pf_name' => $name,
+                                               'pf_count' => $eventCount,
+                                               'pf_time' => $timeSum,
+                                               'pf_memory' => $memorySum,
+                                               'pf_server' => $pfhost
+                                       ),
+                                       array( array( 'pf_name', 'pf_server' ) ),
+                                       array(
+                                               "pf_count=pf_count+{$eventCount}",
+                                               "pf_time=pf_time+{$timeSum}",
+                                               "pf_memory=pf_memory+{$memorySum}",
+                                       ),
+                                       __METHOD__
+                               );
+                       }
+                       if ( $useTrx ) {
+                               $dbw->endAtomic( __METHOD__ );
+                       }
+               } catch ( DBError $e ) {
+               }
+       }
+}
diff --git a/includes/profiler/output/ProfilerOutputText.php b/includes/profiler/output/ProfilerOutputText.php
new file mode 100644 (file)
index 0000000..b24bbef
--- /dev/null
@@ -0,0 +1,79 @@
+<?php
+/**
+ * Profiler showing output in page source.
+ *
+ * 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 Profiler
+ */
+
+/**
+ * The least sophisticated profiler output class possible, view your source! :)
+ *
+ * @ingroup Profiler
+ * @since 1.25
+ */
+class ProfilerOutputText extends ProfilerOutput {
+       /** @var float Min real time display threshold */
+       protected $thresholdMs;
+
+       function __construct( Profiler $collector, array $params ) {
+               parent::__construct( $collector, $params );
+               $this->thresholdMs = isset( $params['thresholdMs'] )
+                       ? $params['thresholdMs']
+                       : .25;
+       }
+       public function log( array $stats ) {
+               if ( $this->collector->getTemplated() ) {
+                       $out = '';
+
+                       // Filter out really tiny entries
+                       $min = $this->thresholdMs;
+                       $stats = array_filter( $stats, function( $a ) use ( $min ) {
+                               return $a['real'] > $min;
+                       } );
+                       // Sort descending by time elapsed
+                       usort( $stats, function( $a, $b ) {
+                               return $a['real'] < $b['real'];
+                       } );
+
+                       array_walk( $stats,
+                               function ( $item ) use ( &$out ) {
+                                       $out .= sprintf( "%6.2f%% %3.3f %6d - %s\n",
+                                               $item['%real'], $item['real'], $item['calls'], $item['name'] );
+                               }
+                       );
+
+                       $contentType = $this->collector->getContentType();
+                       if ( PHP_SAPI === 'cli' ) {
+                               print "<!--\n{$out}\n-->\n";
+                       } elseif ( $contentType === 'text/html' ) {
+                               $visible = isset( $this->params['visible'] ) ?
+                                       $this->params['visible'] : false;
+                               if ( $visible ) {
+                                       print "<pre>{$out}</pre>";
+                               } else {
+                                       print "<!--\n{$out}\n-->\n";
+                               }
+                       } elseif ( $contentType === 'text/javascript' ) {
+                               print "\n/*\n${$out}*/\n";
+                       } elseif ( $contentType === 'text/css' ) {
+                               print "\n/*\n{$out}*/\n";
+                       }
+               }
+       }
+}
diff --git a/includes/profiler/output/ProfilerOutputUdp.php b/includes/profiler/output/ProfilerOutputUdp.php
new file mode 100644 (file)
index 0000000..a938861
--- /dev/null
@@ -0,0 +1,65 @@
+<?php
+/**
+ * Profiler sending messages over UDP.
+ *
+ * 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 Profiler
+ */
+
+/**
+ * ProfilerSimpleUDP class, that sends out messages for 'udpprofile' daemon
+ * (see http://git.wikimedia.org/tree/operations%2Fsoftware.git/master/udpprofile)
+ *
+ * @ingroup Profiler
+ * @since 1.25
+ */
+class ProfilerOutputUdp extends ProfilerOutput {
+       public function canUse() {
+               # Sockets are not enabled
+               return function_exists( 'socket_create' );
+       }
+
+       public function log( array $stats ) {
+               global $wgUDPProfilerHost, $wgUDPProfilerPort, $wgUDPProfilerFormatString;
+
+               $sock = socket_create( AF_INET, SOCK_DGRAM, SOL_UDP );
+               $plength = 0;
+               $packet = "";
+               foreach ( $stats as $pfdata ) {
+                       $pfline = sprintf( $wgUDPProfilerFormatString,
+                               $this->collector->getProfileID(),
+                               $pfdata['calls'],
+                               $pfdata['cpu'] / 1000, // ms => sec
+                               0.0, // sum of CPU^2 for each invocation (unused)
+                               $pfdata['real'] / 1000, // ms => sec
+                               0.0, // sum of real^2 for each invocation (unused)
+                               $pfdata['name'],
+                               $pfdata['memory']
+                       );
+                       $length = strlen( $pfline );
+                       if ( $length + $plength > 1400 ) {
+                               socket_sendto( $sock, $packet, $plength, 0, $wgUDPProfilerHost, $wgUDPProfilerPort );
+                               $packet = "";
+                               $plength = 0;
+                       }
+                       $packet .= $pfline;
+                       $plength += $length;
+               }
+               socket_sendto( $sock, $packet, $plength, 0x100, $wgUDPProfilerHost, $wgUDPProfilerPort );
+       }
+}
index e5d05be..fde22f1 100644 (file)
@@ -444,52 +444,3 @@ class SiteSQLStore implements SiteStore {
        }
 
 }
-
-/**
- * @deprecated since 1.21
- */
-class Sites extends SiteSQLStore {
-
-       /**
-        * Factory for creating new site objects.
-        *
-        * @since 1.21
-        * @deprecated since 1.21
-        *
-        * @param string|bool $globalId
-        *
-        * @return Site
-        */
-       public static function newSite( $globalId = false ) {
-               $site = new Site();
-
-               if ( $globalId !== false ) {
-                       $site->setGlobalId( $globalId );
-               }
-
-               return $site;
-       }
-
-       /**
-        * @deprecated since 1.21
-        * @return SiteStore
-        */
-       public static function singleton() {
-               static $singleton;
-
-               if ( $singleton === null ) {
-                       $singleton = new static();
-               }
-
-               return $singleton;
-       }
-
-       /**
-        * @deprecated since 1.21
-        * @param string $group
-        * @return SiteList
-        */
-       public function getSiteGroup( $group ) {
-               return $this->getSites()->getGroup( $group );
-       }
-}
index 107413e..59e2bba 100644 (file)
@@ -934,7 +934,7 @@ class ContribsPager extends ReverseChronologicalPager {
         * @return string
         */
        function getStartBody() {
-               return "<ul>\n";
+               return "<ul class=\"mw-contributions-list\">\n";
        }
 
        /**
index ad1f051..b3b3d48 100644 (file)
@@ -199,6 +199,8 @@ class SpecialNewpages extends IncludableSpecialPage {
 
        protected function form() {
                $out = $this->getOutput();
+               $out->addModules( 'mediawiki.userSuggest' );
+
                // Consume values
                $this->opts->consumeValue( 'offset' ); // don't carry offset, DWIW
                $namespace = $this->opts->consumeValue( 'namespace' );
index 2022d74..464f71a 100644 (file)
@@ -263,6 +263,26 @@ class SpecialRedirect extends FormSpecialPage {
                $form->setMethod( 'get' );
        }
 
+       /**
+        * Return an array of subpages beginning with $search that this special page will accept.
+        *
+        * @param string $search Prefix to search for
+        * @param int $limit Maximum number of results to return
+        * @return string[] Matching subpages
+        */
+       public function prefixSearchSubpages( $search, $limit = 10 ) {
+               return self::prefixSearchArray(
+                       $search,
+                       $limit,
+                       array(
+                               "file",
+                               "page",
+                               "revision",
+                               "user",
+                       )
+               );
+       }
+
        protected function getGroupName() {
                return 'redirects';
        }
index 7cf05bc..727f485 100644 (file)
@@ -81,19 +81,15 @@ class AutoloadGenerator {
         * @var string $inputPath Path to a php file to find classes within
         */
        public function readFile( $inputPath ) {
-               $path = realpath( $inputPath );
-               if ( !$path ) {
-                       throw new \Exception( "Invalid path: $inputPath" );
-               }
                $len = strlen( $this->basepath );
-               if ( substr( $path, 0, $len ) !== $this->basepath ) {
+               if ( substr( $inputPath, 0, $len ) !== $this->basepath ) {
                        throw new \Exception( "Path is not within basepath: $inputPath" );
                }
                $result = $this->collector->getClasses(
-                       file_get_contents( $path )
+                       file_get_contents( $inputPath )
                );
                if ( $result ) {
-                       $shortpath = substr( $path, $len );
+                       $shortpath = substr( $inputPath, $len );
                        $this->classes[$shortpath] = $result;
                }
        }
@@ -118,8 +114,11 @@ class AutoloadGenerator {
        /**
         * Write out all known classes to autoload.php in
         * the provided basedir
+        *
+        * @param string $commandName Value used in file comment to direct
+        *  developers towards the appropriate way to update the autoload.
         */
-       public function generateAutoload() {
+       public function generateAutoload( $commandName = 'AutoloadGenerator' ) {
                $content = array();
 
                // We need to generate a line each rather than exporting the
@@ -147,16 +146,24 @@ class AutoloadGenerator {
                // sort for stable output
                ksort( $content );
 
+               // extensions using this generator are appending to the existing
+               // autoload.
+               if ( $this->variableName === 'wgAutoloadClasses' ) {
+                       $op = '+=';
+               } else {
+                       $op = '=';
+               }
+
                $output = implode( "\n\t", $content );
                file_put_contents(
                        $this->basepath . '/autoload.php',
                        <<<EOD
 <?php
-// This file is generated, do not adjust manually
+// This file is generated by $commandName, do not adjust manually
 
 global \${$this->variableName};
 
-\${$this->variableName} = array(
+\${$this->variableName} {$op} array(
        {$output}
 );
 
index cb582a1..c535e51 100644 (file)
@@ -6,32 +6,33 @@
                        "Kuwaity26",
                        "Malekbr",
                        "아라",
-                       "Aħmedbaɛl"
+                       "Aħmedbaɛl",
+                       "GeekEmad"
                ]
        },
-       "tog-underline": "ضع خطا تحت الوصلات:",
-       "tog-hideminor": "أخف التعديلات الطفيفة في أحدث التغييرات",
-       "tog-hidepatrolled": "أخف التعديلات المراجعة في أحدث التغييرات",
-       "tog-newpageshidepatrolled": "أخف الصفحات المراجعة من قائمة الصفحات الجديدة",
-       "tog-extendwatchlist": "مدد قائمة المراقبة لعرض كل التغييرات، وليس الأحدث فقط",
+       "tog-underline": "Ħotʾ stʾar taħt errabtʾa:",
+       "tog-hideminor": "Khabbi ettabdilàt essʾgħàr m ettabdilàt lekhrànìn",
+       "tog-hidepatrolled": "Khabbi ettabdilàt elmagħsous għlihom m ettabdilàt lekhrànìn",
+       "tog-newpageshidepatrolled": "Khabbi elpàjàt elmagħsous għlihom m ellista mtagħ elpàjàt ejjdida",
+       "tog-extendwatchlist": "Wassegħ ellista mtagħ elgħassa bech twarri ettabdilàt elkoll, w mouch lekhrànìn kahaw",
        "tog-usenewrc": ")جمّع التعديلات حسب الصفحة في أحدث التغييرات وقائمة المراقبة (يتطلب جافاسكربت",
-       "tog-numberheadings": "رقم العناوين تلقائيا",
+       "tog-numberheadings": "Nwàmer otomatik l ettitrouàt mtagħ esseksyon",
        "tog-showtoolbar": "أظهر شريط التحرير (يتطلب جافاسكربت)",
        "tog-editondblclick": "عدل الصفحات عند الضغط المزدوج (جافاسكربت)",
        "tog-editsectiononrightclick": "فعل تعديل الأقسام بواسطة كبسة الفأرة اليمين على عناوين الأقسام (جافاسكريبت)",
-       "tog-watchcreations": "أضف الصفحات التي أنشئها والملفات التي أرفعها إلى قائمة مراقبتي.",
-       "tog-watchdefault": "أضف الصفحات والملفات التي أعدلها إلى قائمة مراقبتي",
-       "tog-watchmoves": "أضف الصفحات والملفات التي أنقلها إلى قائمة مراقبتي",
-       "tog-watchdeletion": "أضف الصفحات والملفات التي أحذفها إلى قائمة مراقبتي",
-       "tog-minordefault": "علم كل التعديلات طفيفة افتراضيا",
-       "tog-previewontop": "أظهر العرض المسبق قبل صندوق التحرير",
-       "tog-previewonfirst": "أظهر معاينة مع أول تعديل",
-       "tog-enotifwatchlistpages": "أرسل لي رسالة إلكترونية عندما تُغيّر صفحة أو ملف في قائمة مراقبتي",
-       "tog-enotifusertalkpages": "أرسل لي رسالة إلكترونية عندما تعدل صفحة نقاشي",
-       "tog-enotifminoredits": "أرسل لي رسالة إلكترونية عن التعديلات الطفيفة للصفحات والملفات أيضا",
-       "tog-enotifrevealaddr": "أظهر عنوان بريدي الإلكتروني في رسائل الإخطار",
-       "tog-shownumberswatching": "اعرض عدد المستخدمين المراقبين",
-       "tog-oldsig": "التوقيع الحالي:",
+       "tog-watchcreations": "Zid elpàjàt elli għmalt'hom w elfichyéàt elli tʾallagħt'hom l ellista mtagħ elgħassa mtagħi",
+       "tog-watchdefault": "Zid elpàjàt w elfichyéàt elli nbaddelhom l ellista mtagħ elgħassa mtagħi",
+       "tog-watchmoves": "Zid elpàjàt w elfichyéàt elli nhezzhom, l ellista mtagħ elgħassa mtagħi",
+       "tog-watchdeletion": "Zid elpàjàt w elfichyéàt elli nnaħħihom l ellista mtagħ elgħassa mtagħi",
+       "tog-minordefault": "Marki ettabdilàt essʾghàr elkoll par défo",
+       "tog-previewontop": "Warri tʾalla għla chsʾàr qbal ħokket ettabdil",
+       "tog-previewonfirst": "Warri tʾalla għla chsʾàr f ettabdila lawlàniya",
+       "tog-enotifwatchlistpages": "Abgħethli mail waqtelli pàj wella fichyé m ellista mtagħ elgħassa mtagħi tbaddel",
+       "tog-enotifusertalkpages": "Abgħethli mail watelli elpàj mtagħ leħdith mtagħi tetbaddel",
+       "tog-enotifminoredits": "Abgħethli mail zàda għattabdilàt essʾghàr mtagħ elpàjàt w elfichyéàt",
+       "tog-enotifrevealaddr": "Warri ladrisa mail mtagħi f elmailàt mtagħ ennotifikasyon",
+       "tog-shownumberswatching": "Warri għdad lutilizateuràt elgħassàsa",
+       "tog-oldsig": "Ettosʾħàħa elmawjouda:",
        "tog-fancysig": "عامل التوقيع كنص ويكي (بدون وصلة أوتوماتيكية)",
        "tog-uselivepreview": "استخدم الاستعراض السريع (جافاسكريبت) (تجريبي)",
        "tog-forceeditsummary": "نبهني عند إدخال ملخص تعديل فارغ",
@@ -45,8 +46,8 @@
        "tog-diffonly": "لا تعرض محتوى الصفحة أسفل الفروقات",
        "tog-showhiddencats": "أظهر التصنيفات المخفية",
        "tog-norollbackdiff": "أزل الفرق بعد القيام باسترجاع",
-       "underline-always": "دائما",
-       "underline-never": "أبدا",
+       "underline-always": "Dima",
+       "underline-never": "Jemla",
        "underline-default": "تبعا لإعدادات المتصفح",
        "editfont-style": "نمط خط منطقة التحرير:",
        "editfont-default": "تبعا لإعدادات المتصفح",
        "article": "صفحة محتوى",
        "newwindow": "(تفتح في نافذة جديدة)",
        "cancel": "ifsa5",
-       "moredotdotdot": "المزيد...",
+       "moredotdotdot": "Akther...",
        "mypage": "صفحتي",
        "mytalk": "نقاشي",
-       "anontalk": "النقاش لعنوان الأيبي هذا",
-       "navigation": "Navigui",
-       "and": "&#32;و",
-       "qbfind": "جد",
-       "qbbrowse": "ara",
-       "qbedit": "modifi el page (baddelha)",
-       "qbpageoptions": "هذه الصفحة",
-       "qbmyoptions": "صفحاتي",
-       "faq": "الأسئلة الأكثر تكرارا",
-       "faqpage": "Project:أسئلة متكررة",
-       "actions": "Aεmel",
-       "namespaces": "El espaces de noms",
-       "variants": "Anweε",
-       "errorpagetitle": "ghalath",
-       "returnto": "ارجع إلى $1.",
-       "tagline": "Fima ykhoss {{SITENAME}}",
-       "help": "Mouεawna",
+       "anontalk": "Tħaddeth mgħa ladrisa IP hadhi",
+       "navigation": "Ħawwes",
+       "and": "&#32;w",
+       "qbfind": "Lawwej",
+       "qbbrowse": "Navigi",
+       "qbedit": "Baddel",
+       "qbpageoptions": "Elpàj hadhi",
+       "qbmyoptions": "Pàjàti",
+       "faq": "FAQ",
+       "faqpage": "Project:FAQ",
+       "actions": "Aksyonàt",
+       "namespaces": "Blàsʾàt làsàmi",
+       "variants": "Tanwigħàt",
+       "errorpagetitle": "Għaltʾa",
+       "returnto": "Arjagħ l $1.",
+       "tagline": "Men {{SITENAME}}",
+       "help": "Mgħàwna",
        "search": "Lawwej",
        "searchbutton": "Lawwej",
-       "go": "اذهب",
-       "searcharticle": "اذهب",
-       "history": "teri5 el milaf",
-       "history_short": "Historique",
-       "updatedmarker": "تم تحديثها منذ زيارتي الأخيرة",
-       "printableversion": "Copie bech tetetbaε",
-       "permalink": "Lien deyem",
-       "print": "itthba3",
-       "view": "عرض",
+       "go": "Emchi",
+       "searcharticle": "Lawwej",
+       "history": "Listorik mtagħ elpàj",
+       "history_short": "Listorik",
+       "updatedmarker": "tbaddlet melli jit àkher marra",
+       "printableversion": "Kopi bech tatʾbaħħa",
+       "permalink": "Rabtʾa għla tʾoul",
+       "print": "Atʾbagħ",
+       "view": "Aqra",
        "edit": "Baddel",
-       "create": "أنشئ",
-       "editthispage": "modifi hal page",
-       "create-this-page": "أنشئ هذه الصفحة",
-       "delete": "احذف",
-       "deletethispage": "احذف هذه الصفحة",
-       "undelete_short": "استرجاع {{PLURAL:$1|تعديل واحد|تعديلين|$1 تعديلات|$1 تعديل|$1 تعديلا}}",
-       "viewdeleted_short": "عرض {{PLURAL:$1|تعديل محذوف|$1 تعديلات محذوفة}}",
-       "protect": "احم",
-       "protect_change": "غير",
-       "protectthispage": "احم هذه الصفحة",
-       "unprotect": "غير الحماية",
-       "unprotectthispage": "غير حماية هذه الصفحة",
-       "newpage": "صفحات جديدة",
-       "talkpage": "ناقش هذه الصفحة",
+       "create": "Agħmel",
+       "editthispage": "Baddel f elpàj hadhi",
+       "create-this-page": "Agħmel elpàj hadhi",
+       "delete": "Afsakh",
+       "deletethispage": "Afsakh elpàj hadhi",
+       "undelete_short": "Rajjagħ{{PLURAL:$1|tabdila waħda|$1 tabdila}}",
+       "viewdeleted_short": "Warri {{PLURAL:$1|tabdila waħda mafsoukha|$1 tabdila mafsoukha}}",
+       "protect": "Ħàmi",
+       "protect_change": "baddel",
+       "protectthispage": "Ħàmi għal pàj hadhi",
+       "unprotect": "Baddel elħimàya",
+       "unprotectthispage": "Baddel elħimàya mtagħ elpàj hadhi",
+       "newpage": "Pàj jdida",
+       "talkpage": "Tħaddeth għal pàj hadhi",
        "talkpagelinktext": "Ħdith",
-       "specialpage": "صفحة خاصة",
-       "personaltools": "Outils mteεek",
-       "articlepage": "عرض صفحة المحتوى",
+       "specialpage": "Sʾafħa spesyàl",
+       "personaltools": "Magħounek",
+       "articlepage": "Warri elpàj mtagħ elkontenu",
        "talk": "Ħdith",
-       "views": "Affichages",
-       "toolbox": "Outils",
-       "userpage": "عرض صفحة المستخدم",
-       "projectpage": "عرض صفحة المشروع",
-       "imagepage": "عرض صفحة الملف",
-       "mediawikipage": "عرض صفحة الرسالة",
-       "templatepage": "عرض صفحة القالب",
-       "viewhelppage": "عرض صفحة المساعدة",
-       "categorypage": "عرض صفحة التصنيف",
-       "viewtalkpage": "عرض النقاش",
-       "otherlanguages": "Bloughat okhra",
-       "redirectedfrom": "(تم التحويل من $1)",
-       "redirectpagesub": "صفحة تحويل",
-       "lastmodifiedat": "Ekher tabdil elhassafħa nhar $2, mεa $1.",
-       "viewcount": "{{PLURAL:$1|لم تعرض هذه الصفحة أبدا|تم عرض هذه الصفحة مرة واحدة|تم عرض هذه الصفحة مرتين|تم عرض هذه الصفحة $1 مرات|تم عرض هذه الصفحة $1 مرة}}.",
-       "protectedpage": "صÙ\81حة Ù\85Ø­Ù\85Ù\8aØ©",
-       "jumpto": "Emchi el:",
-       "jumptonavigation": "Navigation",
+       "views": "Mandhʾer",
+       "toolbox": "Magħoun",
+       "userpage": "Chour elpàj mtagħ lutilizateur",
+       "projectpage": "Chouf elpàj mtagħ leprojé",
+       "imagepage": "Chouf elpàj mtagħ elfichyé",
+       "mediawikipage": "Chouf elpàj mtagħ elmessàj",
+       "templatepage": "Chouf elpàj mtagħ elmodàl",
+       "viewhelppage": "Chouf elpàj mtagħ lemgħàwna",
+       "categorypage": "Chouf elpàj mtagħ elkatégori",
+       "viewtalkpage": "Chouf leħdith",
+       "otherlanguages": "B loughat okhra",
+       "redirectedfrom": "(Tħawwelt men $1)",
+       "redirectpagesub": "Pàj mtagħ taħwil",
+       "lastmodifiedat": "Elpàj hadhi tbaddlet àkher marra nhàr $1, mgħa $2.",
+       "viewcount": "Elpàj hadhi dakhloulha {{PLURAL:$1|marra waħda|$1 marra}}.",
+       "protectedpage": "Pàj protéjé",
+       "jumpto": "Emchi l:",
+       "jumptonavigation": "Ħawwes",
        "jumptosearch": "Lawwej",
-       "view-pool-error": "عذرا، الخوادم منهكة حاليا.\nيحاول مستخدمون كثر الوصول إلى هذه الصفحة.\nمن فضلك انتظر قليلا قبل أن تحاول الوصول إلى هذه الصفحة مجددا.\n\n$1",
-       "pool-timeout": "انتهاء الانتظار للقفل",
-       "pool-queuefull": "طابور الاقتراع ملئ",
-       "pool-errorunknown": "خطأ غير معروف",
-       "aboutsite": "Fima ykhoss {{SITENAME}}",
-       "aboutpage": "Project:Fima ykhoss",
+       "view-pool-error": "Pardon, esserveuràt tàgħba tawwa.\nBarcha għbàd yħebbou ychoufou nafs elpàj.\nYgħaychek estanna chway qbal ma tjarreb bech todkhel l elpàj hadhi marra okhra.\n\n\n$1",
+       "pool-timeout": "Waqt esstennya wfa",
+       "pool-queuefull": "Essʾaf mgħabbi",
+       "pool-errorunknown": "Ghaltʾa ma nagħrfouhàch",
+       "aboutsite": "Fima ykhosʾ {{SITENAME}}",
+       "aboutpage": "Project:Fima ykhosʾ",
        "copyright": "المحتوى متوفر تحت $1.",
-       "copyrightpage": "{{ns:project}}:حقوق النسخ",
-       "currentevents": "Laħdeth mtaε tawa",
-       "currentevents-url": "Project:Laħdeth mtaε tawa",
-       "disclaimers": "Ɛadam mas'ouliya",
-       "disclaimerpage": "Project:Ɛadam mas'ouliya bsifa εamma",
-       "edithelp": "مساعدة التحرير",
-       "mainpage": "Elpage principale",
-       "mainpage-description": "Elpage principale",
-       "policy-url": "Project:سياسة",
-       "portal": "Mojtamaε",
-       "portal-url": "Project:Mojtamaε",
-       "privacy": "Syeset elconfidentialité",
-       "privacypage": "Project:Syeset elconfidentialité",
+       "copyrightpage": "{{ns:project}}:Copyrights",
+       "currentevents": "Elli sʾàyer tawwa",
+       "currentevents-url": "Project:Elli sʾàyer tawwa",
+       "disclaimers": "Tambihàt",
+       "disclaimerpage": "Project:Tambihàt għàmma",
+       "edithelp": "Mgħàwna f elktiba",
+       "mainpage": "Elpàj Lawlàniya",
+       "mainpage-description": "Elpàj Lawlàniya",
+       "policy-url": "Project:Elpolitik",
+       "portal": "Dakhlet elmojtamagħ",
+       "portal-url": "Project:Mojtama",
+       "privacy": "Elpolitik mtagħ elkonfidonsyalité",
+       "privacypage": "Project:Elpolitik mtagħ elkonfidonsyalité",
        "badaccess": "خطأ في السماح",
        "badaccess-group0": "ليس من المسموح لك تنفيذ الفعل الذي طلبته.",
        "badaccess-groups": "الفعل الذي طلبته مقصور على المستخدمين في {{PLURAL:$2||مجموعة|واحدة من مجموعتي|واحدة من مجموعات}}: $1.",
        "retrievedfrom": "Tekhdhet men \"$1\"",
        "youhavenewmessages": "توجد لديك $1 ($2).",
        "youhavenewmessagesmulti": "لديك رسائل جديدة على $1",
-       "editsection": "Baddel essafħa",
+       "editsection": "Baddel essʾafħa",
        "editold": "Baddel",
-       "viewsourceold": "اعرض المصدر",
-       "editlink": "modifi el page (baddelha)",
+       "viewsourceold": "Warri essours",
+       "editlink": "baddel",
        "viewsourcelink": "Warri essource",
        "editsectionhint": "Baddel essection: $1",
        "toc": "Contenu",
-       "showtoc": "اعرض",
-       "hidetoc": "أخف",
-       "collapsible-collapse": "اطو",
-       "collapsible-expand": "وسع",
-       "thisisdeleted": "أأعرض أو أسترجع $1؟",
-       "viewdeleted": "أأعرض $1؟",
+       "showtoc": "Warri",
+       "hidetoc": "Khabbi",
+       "collapsible-collapse": "Tʾabbes",
+       "collapsible-expand": "Wassegħ",
+       "thisisdeleted": "Warri wella rajjagħ $1؟",
+       "viewdeleted": "Warri $1؟",
        "restorelink": "{{PLURAL:$1|$1 تعديل محذوف|تعديلا واحدا محذوفا|تعديلين محذوفين|$1 تعديلات محذوفة|$1 تعديلا محذوفا|$1 تعديلا محذوفا}}",
        "feedlinks": "التغذية:",
        "feed-invalid": "نوع اشتراك التلقيم غير صحيح.",
        "red-link-title": "$1 (Essafħa mouch mawjouda)",
        "sort-descending": "ترتيب تنازلي",
        "sort-ascending": "ترتيب تصاعدي",
-       "nstab-main": "Safħa",
+       "nstab-main": "Sʾafħa",
        "nstab-user": "صفحة مستخدم",
        "nstab-media": "صفحة وسيط",
-       "nstab-special": "Safħa spéciale",
+       "nstab-special": "Sʾafħa spesyàl",
        "nstab-project": "صفحة مشروع",
        "nstab-image": "Fichier",
-       "nstab-mediawiki": "رسالة",
+       "nstab-mediawiki": "Messàj",
        "nstab-template": "قالب",
        "nstab-help": "صفحة مساعدة",
        "nstab-category": "تصنيف",
        "showhideselectedversions": "أظهر/أخف المراجعات المختارة",
        "editundo": "Rajjaε",
        "diff-multi-manyusers": "({{PLURAL:$1||مراجعة واحدة متوسطة غير معروضة أجراها|مراجعتان متوسطتان غير معروضتان أجراهما|$1 مراجعات متوسطة غير معروضة أجراها|$1 مراجعة متوسطة غير معروضة أجراها}} أكثر من {{PLURAL:$2||مستخدم واحد|مستخدمين|$2 مستخدمين|$2 مستخدمًا|$2 مستخدم}}.)",
-       "searchresults": "Elrésultats mtaε elrecherche",
-       "searchresults-title": "Elrésultats mtaε elrecherche εla \"$1\"",
+       "searchresults": "Errézultʾa mtagħ ettalwij",
+       "searchresults-title": "Errézultʾa mtagħ ettalwij għla \"$1\"",
        "prevn": "{{PLURAL:$1|$1}} السابقة",
        "nextn": "{{PLURAL:$1|$1}} التالية",
        "prevn-title": "$1 {{PLURAL:$1|نتيجة|نتيجة}} سابقة",
        "linkstoimage": "{{PLURAL:$1||الصفحة التالية تصل|الصفحتان التاليتان تصلان|ال$1 صفحات التالية تصل|ال$1 صفحة التالية تصل}} إلى هذا الملف:",
        "nolinkstoimage": "لا توجد صفحات تصل لهذا الملف.",
        "sharedupload-desc-here": "هذا الملف من $1 ويمكن استخدامه بواسطة المشاريع الأخرى.\nالوصف على [$2 صفحة وصف الملف] هناك معروض بالأسفل.",
-       "randompage": "Safħa elli tji",
+       "randompage": "Sʾafħa elli tji",
        "statistics": "إحصاءات",
        "nbytes": "{{PLURAL:Octet weħed|Zouz octets|$1 octets|$1 en octet}}",
        "nmembers": "{{PLURAL:$1|لا أعضاء|عضو واحد|عضوان|$1 أعضاء|$1 عضوا|$1 عضو}}",
        "tooltip-ca-talk": "Discussion εal contenu mtaε essafħa",
        "tooltip-ca-edit": "Tannjem tbaddel essafħa hedhi. Aman enzel εal bouton mtaε elvue el msabqa qbal matsajjel.",
        "tooltip-ca-addsection": "ابدأ قسما جديدا",
-       "tooltip-ca-viewsource": "El safħa protégée.\nTnajjem tchouf essource mteεha.",
+       "tooltip-ca-viewsource": "Essʾafħa protéjé.\nTnajjem tchouf essours mtaħħa.",
        "tooltip-ca-history": "Copiet qdom mtaε essafħa hedhi",
        "tooltip-ca-protect": "احم هذه الصفحة",
        "tooltip-ca-delete": "احذف هذه الصفحة",
        "tooltip-n-portal": "Ɛ'almachrouε, chnowa tnajem taεmel, win talqa elli ħajtek bih",
        "tooltip-n-currentevents": " Alqa information εla aham laħdeth mtaε tawa",
        "tooltip-n-recentchanges": "Lista mtaε ajad ettabdilat f'elwiki",
-       "tooltip-n-randompage": "Ħell safħa elli tji",
+       "tooltip-n-randompage": "Ħell sʾafħa elli tji",
        "tooltip-n-help": "Mouεawna",
        "tooltip-t-whatlinkshere": "Lista mtaε safħat elwiki elkol elli twassel elhouni",
        "tooltip-t-recentchangeslinked": "Aham ettabldilet f'essafħat elli ywaslou l'essafħa hedhi",
index 75bfc34..856df15 100644 (file)
        "otherlanguages": "بلغات أخرى",
        "redirectedfrom": "(بالتحويل من $1)",
        "redirectpagesub": "صفحة تحويل",
+       "redirectto": "تحويل إلى",
        "lastmodifiedat": "آخر تعديل لهذه الصفحة كان يوم $1 الساعة $2.",
        "viewcount": "{{PLURAL:$1|لم تعرض هذه الصفحة أبدا|تم عرض هذه الصفحة مرة واحدة|تم عرض هذه الصفحة مرتين|تم عرض هذه الصفحة $1 مرات|تم عرض هذه الصفحة $1 مرة}}.",
        "protectedpage": "صفحة محمية",
        "search-result-category-size": "{{PLURAL:$1|لا أعضاء|عضو واحد|عضوان|$1 أعضاء|$1 عضوًا|$1 عضو}} ({{PLURAL:$2|لا تصانيف فرعية|تصنيف فرعي واحد|تصنيفان فرعيان|$2 تصنيفات فرعية|$2 تصنيفًا فرعيًا|$2 تصنيف فرعي}} و{{PLURAL:$3|لا ملفات|ملف واحد|ملفان|$3 ملفات|$3 ملفًا|$3 ملف}})",
        "search-redirect": "(تحويلة $1)",
        "search-section": "(قسم $1)",
+       "search-category": "(التصنيف $1)",
        "search-file-match": "(يطابق محتوى الملف)",
        "search-suggest": "أتقصد: $1",
        "search-interwiki-caption": "المشاريع الشقيقة",
        "pager-older-n": "{{PLURAL:$1|أقدم 1|أقدم $1}}",
        "suppress": "أوفرسايت",
        "querypage-disabled": "تم تعطيل هذه الصفحة الخاصة لأسباب تتعلق بالأداء.",
+       "apihelp": "مساعدة API",
        "booksources": "مصادر كتاب",
        "booksources-search-legend": "البحث عن مصادر الكتب",
        "booksources-isbn": "ردمك:",
        "import": "استيراد صفحات",
        "importinterwiki": "استيراد ترانسويكي",
        "import-interwiki-text": "اختر ويكي وعنوان الصفحة للاستيراد.\nتواريخ المراجعات وأسماء المحررين سيتم حفظها.\nكل أفعال الاستيراد عبر الويكي يتم تسجيلها في [[Special:Log/import|سجل الاستيراد]].",
+       "import-interwiki-sourcewiki": "الويكي المصدر:",
+       "import-interwiki-sourcepage": "الصفحة المصدر:",
        "import-interwiki-history": "انسخ كل نسخ التاريخ لهذه الصفحة",
        "import-interwiki-templates": "ضمن كل القوالب",
        "import-interwiki-submit": "استيراد",
        "tooltip-feed-atom": "تلقيم أتوم لهذه الصفحة",
        "tooltip-t-contributions": "رؤية قائمة مساهمات هذا المستخدم",
        "tooltip-t-emailuser": "أرسل رسالة لهذا المستخدم",
+       "tooltip-t-info": "المزيد من المعلومات عن هذه الصفحة",
        "tooltip-t-upload": "ارفع ملفات",
        "tooltip-t-specialpages": "قائمة بكل الصفحات الخاصة",
        "tooltip-t-print": "نسخة للطباعة لهذه الصفحة",
        "autosumm-replace": "استبدال الصفحة ب'$1'",
        "autoredircomment": "تحويل إلى [[$1]]",
        "autosumm-new": "أنشأ الصفحة ب'$1'",
+       "autosumm-newblank": "أنشأ صفحة فارغة",
        "size-bytes": "$1 بايت",
        "size-kilobytes": "$1 كيلوبايت",
        "size-megabytes": "$1 ميجابايت",
        "duplicate-defaultsort": "'''تحذير:''' مفتاح الترتيب الافتراضي \"$2\" يتجاوز مفتاح الترتيب الافتراضي السابق \"$1\".",
        "version": "نسخة",
        "version-extensions": "الامتدادات المثبتة",
-       "version-skins": "واجهات",
+       "version-skins": "الواجهات المنصبة",
        "version-specialpages": "صفحات خاصة",
        "version-parserhooks": "خطاطيف المحلل",
        "version-variables": "المتغيرات",
        "right-pagelang": "تغيير لغة الصفحة",
        "action-pagelang": "تغيير لغة الصفحة",
        "log-name-pagelang": "تغيير سجل الصفحة",
+       "default-skin-not-found-row-enabled": "* <code>$1</code> / $2 (مفعل)",
+       "default-skin-not-found-row-disabled": "* <code>$1</code> / $2 ('''معطل''')",
+       "mediastatistics": "إحصاءات الميديا",
+       "mediastatistics-table-mimetype": "نوع MIME",
+       "mediastatistics-table-extensions": "الامتدادات الممكنة",
+       "mediastatistics-table-count": "عدد الملفات",
+       "mediastatistics-table-totalbytes": "الحجم المدمج",
+       "mediastatistics-header-unknown": "غير معروف",
+       "mediastatistics-header-bitmap": "صور Bitmap",
+       "mediastatistics-header-audio": "صوت",
        "mediastatistics-header-video": "مقاطع الفيديو",
+       "mediastatistics-header-multimedia": "ريتش ميديا",
+       "mediastatistics-header-office": "مكتب",
+       "mediastatistics-header-text": "نصي",
+       "mediastatistics-header-executable": "تنفيذية",
+       "mediastatistics-header-archive": "صيغ مضغوطة",
        "json-error-syntax": "خطأ صياغة"
 }
index c74cfe8..96eeb13 100644 (file)
        "sort-descending": "آزالان سیرالاماق",
        "sort-ascending": "چوْخالان سیرالاماق",
        "nstab-main": "صفحه",
-       "nstab-user": "اÛ\8cستÛ\8cÙ\81ادÙ\87â\80\8cÚ\86Û\8c ØµØ­Û\8cÙ\81ه‌سی",
+       "nstab-user": "اÛ\8cØ´Ù\84دÙ\86 ØµÙ\81Ø­ه‌سی",
        "nstab-media": "مئدیا صحیفه‌سی",
        "nstab-special": "اؤزل صحیفه",
        "nstab-project": "پروژه صحیفه‌سی",
index af8a98c..4f642ef 100644 (file)
        "readonly": "Мәғлүмәттәр базаһы бикләнгән",
        "enterlockreason": "Ябылыу сәбәбен һәм ваҡытын белдерегеҙ.",
        "readonlytext": "Яңы мәҡәләләр өҫтәү һәм мәғлүмәттәр базаһындағы башҡа үҙгәртеүҙәр хәҙер ябылған. Был планлы хеҙмәтләндереү сәбәпле булыуы мөмкин, аҙаҡтан нормаль хәлгә ҡайтасаҡ.\n\nЯбыусы хаким ҡалдырған аңлатма:\n$1",
-       "missing-article": "Мәғлүмәттәр базаһында «$1» $2 битенең һоралған тексты табылманы.\n\nБыл, ғәҙәттә, иҫкергән һылтанма буйынса юйылған биттең  үҙгәртеү тарихына күскәндә килеп сыға.\n\nӘгәр хатаның сәбәбе ул булмаһа, тимәк һеҙ программала хата тапҡанһығыҙ.\nБыл турала зинһар URL-ды күрһәтеп, [[Special:ListUsers/sysop|хәкимгә]] белдерегеҙ.",
+       "missing-article": "Мәғлүмәттәр базаһында «$1» $2 битенең һоралған тексты табылманы.\n\nБыл, ғәҙәттә, иҫкергән һылтанма буйынса юйылған биттең  үҙгәртеү тарихына күскәндә килеп сыға.\n\nӘгәр хатаның сәбәбе ул булмаһа, тимәк һеҙ программала хата тапҡанһығыҙ.\nБыл турала зинһар URL-ды күрһәтеп, [[Special:ListUsers/sysop|хакимгә]] белдерегеҙ.",
        "missingarticle-rev": "(версия № $1)",
        "missingarticle-diff": "(айырма: $1, $2)",
        "readonly_lag": "Өҫтәмә сервер төп сервер менән синхронлашҡанға тиклем мәғлүмәттәр базаһы автоматик рәүештә үҙгәрештәргә ҡаршы ябылған.",
        "group-user-member": "{{GENDER:$1|ҡулланыусы}}",
        "group-autoconfirmed-member": "{{GENDER:$1|Автоматик раҫланған ҡулланыусы}}",
        "group-bot-member": "{{GENDER:$1|бот}}",
-       "group-sysop-member": "{{GENDER:$1|администратор}}",
+       "group-sysop-member": "{{GENDER:$1|хаким}}",
        "group-bureaucrat-member": "{{GENDER:$1|бей}}",
        "group-suppress-member": "{{GENDER:$1|күҙәтеүсе}}",
-       "grouppage-user": "{{ns:project}}:Ҡулланыусылар",
-       "grouppage-autoconfirmed": "{{ns:project}}:Автоматик раҫланған ҡулланыусылар",
+       "grouppage-user": "{{ns:project}}:Ҡатнашыусылар",
+       "grouppage-autoconfirmed": "{{ns:project}}:Автоматик раҫланған ҡатнашыусылар",
        "grouppage-bot": "{{ns:project}}:Боттар",
-       "grouppage-sysop": "{{ns:project}}:Хәкимдәр",
-       "grouppage-bureaucrat": "{{ns:project}}:Бюрократтар",
+       "grouppage-sysop": "{{ns:project}}:Хакимдәр",
+       "grouppage-bureaucrat": "{{ns:project}}:Бейҙәр",
        "grouppage-suppress": "{{ns:project}}:Тикшереүселәр",
        "right-read": "Биттәрҙе ҡарау",
        "right-edit": "Биттәрҙә мөхәррирләү",
        "upload-proto-error": "Протокол дөрөҫ түгел",
        "upload-proto-error-text": "Алыҫтан тейәү өсөн <code>http://</code> йәки <code>ftp://</code> менән башланған адрес кәрәк.",
        "upload-file-error": "Эске хата",
-       "upload-file-error-text": "Серверҙа ваҡытлы файл булдырған ваҡытта эске хата сыҡты.\nЗинһар, [[Special:ListUsers/sysop|хәкимгә]] мөрәжәғәт итегеҙ.",
+       "upload-file-error-text": "Серверҙа ваҡытлы файл булдырған ваҡытта эске хата сыҡты.\nЗинһар, [[Special:ListUsers/sysop|хакимгә]] мөрәжәғәт итегеҙ.",
        "upload-misc-error": "Билдәһеҙ тейәү хатаһы",
-       "upload-misc-error-text": "Файл тейәгәндә билдәһеҙ хата килеп сыҡты.\nЗинһар, URL адрестың дөрөҫлөгөн тикшерегеҙ һәм яңынан ҡабатлап ҡарағыҙ.\nӘгәр хата шул килеш ҡалһа, [[Special:ListUsers/sysop|хәкимгә]] мөрәжәғәт итегеҙ.",
+       "upload-misc-error-text": "Файл тейәгәндә билдәһеҙ хата килеп сыҡты.\nЗинһар, URL адрестың дөрөҫлөгөн тикшерегеҙ һәм яңынан ҡабатлап ҡарағыҙ.\nӘгәр хата шул килеш ҡалһа, [[Special:ListUsers/sysop|хакимгә]] мөрәжәғәт итегеҙ.",
        "upload-too-many-redirects": "URL бигерәк күп йүнәлтмәләр яһай.",
        "upload-http-error": "HTTP хата килеп сыҡты: $1",
        "upload-copy-upload-invalid-domain": "Был доменға ҡараған сайттарҙан файл күсереү асыҡ түгел",
        "namespace_association": "Бәйле арауыҡ",
        "tooltip-namespace_association": "Һайланған исемдәр арауығы менән бәйле әңгәмә(йәки тема) исем арауыҡтарын ҡушыр өсөн был билдәне ҡуйығыҙ.",
        "blanknamespace": "(Төп)",
-       "contributions": "{{GENDER:$1|Ҡатнашыусы}} өлөшө",
-       "contributions-title": "$1 исемле ҡулланыусының кереткән өлөшө",
-       "mycontris": "Өлөш",
-       "contribsub2": "{{GENDER:$3|$1}} өлөшө ($2)",
+       "contributions": "{{GENDER:$1|Ҡатнашыусы}} башҡарған эш",
+       "contributions-title": "$1 исемле ҡатнашыусы башҡарған эш",
+       "mycontris": "Башҡарған эштәр",
+       "contribsub2": "{{GENDER:$3|$1}} башҡарған эше ($2)",
        "nocontribs": "Күрһәтелгән шарттарға яуап биргән үҙгәртеүҙәр табылманы.",
        "uctop": "(ағымдағы)",
        "month": "Айҙан башлап (һәм элегерәк):",
        "year": "Йылдан башлап (һәм элегерәк):",
-       "sp-contributions-newbies": "ЯңÑ\8b Ð¸Ò«Ó\99п Ñ\8fÒ\99малаÑ\80Ñ\8b ÐºÐµÑ\80еÑ\82кÓ\99н Ó©Ð»Ó©Ñ\88Ñ\82Ó© генә күрһәтергә",
+       "sp-contributions-newbies": "ЯңÑ\8b Ð¸Ò«Ó\99п Ñ\8fÒ\99малаÑ\80Ñ\8b Ð±Ð°Ñ\88ҡаÑ\80Ò\93ан Ñ\8dÑ\88Ñ\82е генә күрһәтергә",
        "sp-contributions-newbies-sub": "Яңы иҫәп яҙмалары өсөн",
-       "sp-contributions-newbies-title": "Яңы иҫәп яҙмалары өсөн ҡатнашыусы өлөшө",
+       "sp-contributions-newbies-title": "Яңы теркәлгән ҡатнашыусылар башҡарған эш",
        "sp-contributions-blocklog": "блоклау яҙмалары",
-       "sp-contributions-deleted": "ҡулланыусының юйылған өлөшө",
+       "sp-contributions-deleted": "юйылған үҙгәртеүҙәр",
        "sp-contributions-uploads": "тейәүҙәр",
        "sp-contributions-logs": "журналдар",
        "sp-contributions-talk": "фекерләшеү",
        "sp-contributions-userrights": "ҡатнашыусы хоҡуҡтарын идаралау",
        "sp-contributions-blocked-notice": "Әлеге ваҡытта был ҡатнашыусы бикле.\nТүбәндә бикләү яҙмаларынан һуңғы ҡатнашыусыны бикләү яҙмаһы килтерелгән:",
        "sp-contributions-blocked-notice-anon": "Әлеге ваҡытта был IP адрес бикле.\nТүбәндә бикләү яҙмаларынан һуңғы адресты бикләү яҙмаһы килтерелгән:",
-       "sp-contributions-search": "Өлөштәрҙе эҙләү",
+       "sp-contributions-search": "Башҡарған эште эҙләү",
        "sp-contributions-username": "Ҡулланыусының IP-адресы йәки исеме:",
        "sp-contributions-toponly": "Һуңғы өлгөләрҙе генә күрһәтергә",
        "sp-contributions-submit": "Эҙлә",
index 59b80c2..0adc350 100644 (file)
        "nospecialpagetext": "<strong>Спэцыяльная старонка, да якой Вы зьвярнуліся, не існуе.</strong>\n\nСьпіс дзейных спэцыяльных старонак можна знайсьці на [[Special:SpecialPages|{{int:specialpages}}]].",
        "error": "Памылка",
        "databaseerror": "Памылка базы зьвестак",
-       "databaseerror-text": "Ð\9fÑ\80Ñ\8b Ð·Ð°Ð¿Ñ\8bÑ\86е Ð±Ð°Ð·Ñ\8b Ð·Ñ\8cвеÑ\81Ñ\82ак Ñ\83зÑ\8cнÑ\96кла Ð¿Ð°Ð¼Ñ\8bлка.\nÐ\93Ñ\8dÑ\82а Ð¼Ð¾Ð¶Ð° Ñ\81Ñ\8cведÑ\87Ñ\8bÑ\86Ñ\8c Ð¿Ñ\80а Ð½Ñ\8fÑ\81пÑ\80аÑ\9eнаÑ\81Ñ\8cÑ\86Ñ\8c Ð°Ð¿Ñ\80агÑ\80амаванÑ\8cнÑ\8f.",
+       "databaseerror-text": "Ð\9fÑ\80Ñ\8b Ð·Ð°Ð¿Ñ\8bÑ\86е Ð±Ð°Ð·Ñ\8b Ð·Ñ\8cвеÑ\81Ñ\82ак Ñ\83зÑ\8cнÑ\96кла Ð¿Ð°Ð¼Ñ\8bлка.\nÐ\93Ñ\8dÑ\82а Ð¼Ð¾Ð¶Ð° Ñ\81Ñ\8cведÑ\87Ñ\8bÑ\86Ñ\8c Ð¿Ñ\80а Ð¿Ð°Ð¼Ñ\8bлкÑ\83 Ñ\9e Ð¿Ñ\80агÑ\80амнÑ\8bм Ð·Ð°Ð±ÐµÑ\81Ñ\8cпÑ\8fÑ\87Ñ\8dнÑ\8cнÑ\96.",
        "databaseerror-textcl": "Узьнікла памылка запыту базы зьвестак.",
        "databaseerror-query": "Запыт: $1",
        "databaseerror-function": "Функцыя: $1",
        "databaseerror-error": "Памылка: $1",
-       "laggedslavemode": "'''Увага:''' старонка можа ня ўтрымліваць апошніх зьменаў.",
+       "laggedslavemode": "<strong>Увага:</strong> старонка можа ня ўтрымліваць апошнія зьмены.",
        "readonly": "База зьвестак заблякаваная",
        "enterlockreason": "Пазначце прычыну блякаваньня і заплянаваны час разблякаваньня",
        "readonlytext": "База зьвестак заблякаваная для дадаваньня новых старонак і іншых зьменаў, верагодна з прычыны тэхнічнага абслугоўваньня, пасьля якога будзе адноўлена звычайная праца.\n\nАдміністратар, які заблякаваў базу зьвестак, пакінуў наступнае тлумачэньне: $1",
index 3c29c24..d95f9d2 100644 (file)
        "revdelete-log": "কারণ:",
        "revdelete-submit": "নির্বাচিত {{PLURAL:$1|সংশোধনে|সংশোধসমূহে}} প্রয়োগ করো",
        "revdelete-success": "'''সংশোধন দৃশ্যমানতা সফলভাবে হালনাগাদ করা হয়েছে।'''",
-       "revdelete-failure": "'''রিভিশন ভিজিবিলিটি আপডেট সম্ভব নয়:'''\n$1",
+       "revdelete-failure": "<strong>সংশোধনের দৃশ্যমানতা হালনাগাদ করা যায়নি:</strong>\n$1",
        "logdelete-success": "'''ঘটনা দৃশ্যমানতা সফলভাবে স্থাপন করা হয়েছে।'''",
        "logdelete-failure": "'''লগ-এর দৃশ্যমানতা নির্ধারণ সম্ভব হচ্ছে না:'''\n$1",
        "revdel-restore": "দৃশ্যমানতা পরিবর্তন করো",
index adfc915..d02ffc5 100644 (file)
@@ -42,7 +42,8 @@
                        "לערי ריינהארט",
                        "아라",
                        "Calak",
-                       "F3RaN"
+                       "F3RaN",
+                       "ESM"
                ]
        },
        "tog-underline": "Subratlla els enllaços:",
        "versionrequired": "Cal la versió $1 del MediaWiki",
        "versionrequiredtext": "Cal la versió $1 del MediaWiki per a utilitzar aquesta pàgina. Vegeu [[Special:Version]]",
        "ok": "D’acord",
+       "pagetitle": "$1 - {{SITENAME}}",
+       "pagetitle-view-mainpage": "{{SITENAME}}",
+       "backlinksubtitle": "← $1",
        "retrievedfrom": "Obtingut de «$1»",
        "youhavenewmessages": "Tens $1 ($2).",
        "youhavenewmessagesfromusers": "Tens $1 {{PLURAL:$3|d'un altre usuari|de $3 usuaris}} ($2).",
        "site-atom-feed": "Canal Atom $1",
        "page-rss-feed": "«$1» RSS Feed",
        "page-atom-feed": "Canal Atom «$1»",
+       "feed-atom": "Atom",
+       "feed-rss": "RSS",
        "red-link-title": "$1 (encara no existeix)",
        "sort-descending": "Ordena descendentment",
        "sort-ascending": "Ordena ascendentment",
        "filerenameerror": "No s'ha pogut reanomenar el fitxer «$1» com «$2».",
        "filedeleteerror": "No s'ha pogut eliminar el fitxer «$1».",
        "directorycreateerror": "No s'ha pogut crear el directori «$1».",
-       "directoryreadonlyerror": "Directory \"$1\" is read-only.",
+       "directoryreadonlyerror": "El directori \"$1\" és de només lectura.",
+       "directorynotreadableerror": "El directori \"$1\" no és llegible",
        "filenotfound": "No s'ha pogut trobar el fitxer «$1».",
        "unexpected": "S'ha trobat un valor imprevist: «$1»=«$2».",
        "formerror": "Error: no s'ha pogut enviar les dades del formulari",
        "uploadnewversion-linktext": "Carrega una nova versió d'aquest fitxer",
        "shared-repo-from": "des de $1",
        "shared-repo": "un repositori compartit",
+       "shared-repo-name-wikimediacommons": "Wikimedia Commons",
        "upload-disallowed-here": "No pot sobreescriure aquest fitxer.",
        "filerevert": "Reverteix $1",
        "filerevert-legend": "Reverteix el fitxer",
        "apihelp-no-such-module": "No s'ha trobat el mòdul \"$1\".",
        "booksources": "Obres de referència",
        "booksources-search-legend": "Cerca fonts de llibres",
+       "booksources-isbn": "ISBN:",
        "booksources-search": "Cerca",
        "booksources-text": "A sota hi ha una llista d'enllaços d'altres llocs que venen llibres nous i de segona mà, i també podrien tenir més informació dels llibres que esteu cercant:",
        "booksources-invalid-isbn": "El codi ISBN donat no és vàlid. Comproveu si l'heu copiat correctament.",
        "tooltip-feed-atom": "Canal Atom d'aquesta pàgina",
        "tooltip-t-contributions": "Vegeu la llista de contribucions d'aquest usuari.",
        "tooltip-t-emailuser": "Envia un correu en aquest usuari.",
+       "tooltip-t-info": "Més informació sobre aquesta pàgina",
        "tooltip-t-upload": "Càrrega d'imatges o altres fitxers.",
        "tooltip-t-specialpages": "Llista de totes les pàgines especials.",
        "tooltip-t-print": "Versió per a impressió d'aquesta pàgina",
index 3cfa141..cdc6b03 100644 (file)
        "tog-fancysig": "Шен вики-къастаман куьгтаӀдар (ша шех хьажораг йоцуш)",
        "tog-uselivepreview": "Лелайа чехка хьалха хьажа (JavaScript, муха ю хьажарна)",
        "tog-forceeditsummary": "Дага даийта, нагахь нисйарх лаьцна чохь язйина яцахь",
-       "tog-watchlisthideown": "Къайлаяха ас нисйинарш оцу тергаме могӀам чура",
-       "tog-watchlisthidebots": "Ð\9aÑ\8aайладаÑ\85а Ñ\88аболÑ\85 Ð±ÐµÑ\87о Ð½Ð¸Ñ\81динаÑ\80Ñ\88 Ð¾Ñ\86Ñ\83 Ñ\82еÑ\80гаме Ð¼Ð¾Ð³Ó\80ам Ñ\87Ñ\83Ñ\80а",
-       "tog-watchlisthideminor": "Къайладаха кегийра нисдарш оцу тергаме могӀам чура",
-       "tog-watchlisthideliu": "Къайладаха бовзийтина болу декъашхойн нисдарш оцу тергаме могӀам чура",
-       "tog-watchlisthideanons": "Къайладаха къайлаха болу декъашхойн нисдарш оцу тергаме могӀам чура",
-       "tog-watchlisthidepatrolled": "Къайладаха хьаьжина долу нисдарш оцу тергаме могӀам чура",
+       "tog-watchlisthideown": "Къайлаяха ас нисйинарш тергаме могӀам чура",
+       "tog-watchlisthidebots": "Ð\9aÑ\8aайладаÑ\85а Ñ\82еÑ\80гаме Ð¼Ð¾Ð³Ó\80ам Ñ\87Ñ\83Ñ\80а Ð±Ð¾Ñ\82ан Ð½Ð¸Ñ\81динаÑ\80Ñ\88",
+       "tog-watchlisthideminor": "Къайладаха кегийра нисдарш тергаме могӀам чура",
+       "tog-watchlisthideliu": "Къайладаха бовзийтина болу декъашхойн нисдарш тергаме могӀам чура",
+       "tog-watchlisthideanons": "Къайладаха къайлаха болу декъашхойн нисдарш тергаме могӀам чура",
+       "tog-watchlisthidepatrolled": "Къайладаха хьаьжина долу нисдарш тергаме могӀам чура",
        "tog-ccmeonemails": "Дlадахьийта суна исанна кехат, аса дохьуьйтуш долу кхечу декъашхошна.",
        "tog-diffonly": "Ма гайта агlон чулацам шина башхонца цхьатерра йолуш",
        "tog-showhiddencats": "Гайта къайлаха йолу категореш",
        "confirmable-confirm": "Лаьий {{GENDER:$1|хьуна}}?",
        "confirmable-yes": "ХӀаъ",
        "confirmable-no": "ХӀахӀа",
-       "thisisdeleted": "Ð¥Ñ\8cажа Ñ\8f Ð¼ÐµÑ\82Ñ\82аÑ\85Ó\80оÑ\82Ñ\82айé $1?",
+       "thisisdeleted": "Ð¥Ñ\8cажа Ñ\8f Ð¼ÐµÑ\82Ñ\82аÑ\85Ó\80оÑ\82Ñ\82ае $1?",
        "viewdeleted": "Хьожий $1?",
        "restorelink": "{{PLURAL:$1|1=$1 дӀадаьккхина нийсдар|$1 дӀадяхна нийсдарш}}",
        "feedlinks": "Оцу хатlаьхь:",
        "filerenameerror": "Файлан «$1» цӀе хийца «$2» йиш яц.",
        "filedeleteerror": "ДӀаяккха цатарло файл «$1».",
        "directorycreateerror": "Йиш яц директори «$1» кхолла.",
+       "directoryreadonlyerror": "ХӀара «$1» еша бен луш дац.",
+       "directorynotreadableerror": "ХӀара «$1» еша луш дац.",
        "filenotfound": "Файл «$1» каро йиш яц.",
        "unexpected": "БIегIийла йоцу маьIна: «$1»=«$2».",
        "formerror": "ГӀалат: йиш яц хӀара формаш дӀакхачо",
        "viewsourcetext": "Хьоьга далундерг хьажар а дезахь хlокху агlон чура йоза хьаэцар:",
        "viewyourtext": "Хьан йиш ю '''хьой нисдинчу''' дӀадолалун йозе хьажа а цуна копи ян а:",
        "protectedinterface": "ХӀара схьгайтарна гӀирса хаамаш латтош йолу агӀо ю. Куьйгалхошна бен иза хийца цало.",
-       "editinginterface": "'''Тергам бе:''' Ахьа таеш ю интерфейсан йоза долу агӀо програмин латторан.\nЦуна бина хийцам хьокху википедин кхечу декъашхошна гур бу.\nХьокху хаамийн гочдар тӀетоха я хийца лела йе сайт MediaWiki [//translatewiki.net/ translatewiki.net].",
+       "editinginterface": "<strong>Тергам бе:</strong> Ахьа таеш ю интерфейсан йоза долу агӀо програмин латторан.\nЦуна бина хийцам хьокху википедин кхечу декъашхошна гур бу.",
        "cascadeprotected": "АгӀо хийцам ца байта гӀоралла дина ю {{PLURAL:$1|хӀокху агӀона|хӀокху агӀонийн}} юкъа йогӀуш хилар бахьнехь:\n$2",
        "namespaceprotected": "ХӀан бакъо яц анна цӀераш чохь тадарш да «$1».",
        "customcssprotected": "Хьан бакъо яц хӀара CSS-агӀо тая, иза кхечу декъашхочун гӀерс болу дера.",
        "subject": "Дlахьедар/коьрта могlа:",
        "minoredit": "Жим хийцам",
        "watchthis": "Латайе хӀара агӀо тергаме могӀанан юкъахь",
-       "savearticle": "Ð\94lайазÑ\8aé Ð°Ð³lо",
+       "savearticle": "Ð\90гÓ\80о Ð´Ó\80аÑ\8fзÑ\8aÑ\8fÑ\80",
        "preview": "Хьалха хьажар",
        "showpreview": "Хьалха хьажар",
        "showdiff": "Бина хийцамашка хьажар",
        "difference-title-multipage": "АгӀонийн башхалла «$1» а «$2» а",
        "difference-multipage": "(АгӀонийн башхалла)",
        "lineno": "МогӀа $1:",
-       "compareselectedversions": "Хаьржина версеш муха ю хьажа",
+       "compareselectedversions": "Хаьржина версешка хьажар",
        "showhideselectedversions": "Гайта/къайлаяха хаьржина башхонаш",
        "editundo": "цаоьшу",
        "diff-empty": "(башхалла яц)",
        "preferences": "ГӀирс нисбан",
        "mypreferences": "ГӀирс нисбан",
        "prefs-edits": "Нисдарийн дукхалла:",
-       "prefsnologintext2": "Ð\9eÑ\8cÑ\88Ñ\83 $1, Ð³Ó\80иÑ\80Ñ\81 Ð´Ó\80аниÑ\81бан.",
+       "prefsnologintext2": "Ð\94еÑ\85аÑ\80 Ð´Ð¾, Ð³Ó\80иÑ\80Ñ\81 Ð´Ó\80аниÑ\81бан Ñ\8fзÑ\8aÑ\8fÑ\80.",
        "prefs-skin": "Кечяран тема",
        "skin-preview": "Хьалха хьажар",
        "datedefault": "Iад йитарца",
        "prefs-tokenwatchlist": "Токен",
        "prefs-diffs": "Башхон верси",
        "prefs-help-prefershttps": "И хийцам болх байта юхугӀо системин чу.",
+       "prefs-tabs-navigation-hint": "Хьехам: Шу йиш ю аьрру а, аьтту а цхьамзан пиллигаш лелаян цхьана юкъадиллинарг тӀера вукхун тӀе долуш.",
        "email-address-validity-valid": "Го нийса",
        "userrights": "Декъашхочун бакъона урхалладар",
        "userrights-lookup-user": "Декъашхошан бакъонашан урхалладар",
        "recentchanges-feed-description": "Тергам бе тlаьхьара вики хийцаман хlокху ларца.",
        "recentchanges-label-newpage": "Оцу нисдарца кхоьллина керла агӀо.",
        "recentchanges-label-minor": "Хlара нисдинарг къастийна жимо долушсан",
-       "recentchanges-label-bot": "ХӀара нисдар шаболх бечо дина",
+       "recentchanges-label-bot": "ХӀара нисдар бото дина",
        "recentchanges-label-unpatrolled": "ХӀара нисдар хӀинца цхьано патрулировать дина дац",
        "recentchanges-label-plusminus": "байташкахь барам хийцар",
        "recentchanges-legend-heading": "'''Легенда:&nbsp;'''",
        "tooltip-ca-nstab-help": "ГӀоьна агӀо",
        "tooltip-ca-nstab-category": "Категорешан агӀо",
        "tooltip-minoredit": "Къастам бé хӀокху хийцамна кӀеззиг болуш санна",
-       "tooltip-save": "Хьан хийцамаш lалашбой",
+       "tooltip-save": "Хьан хийцамаш Ӏалашбой",
        "tooltip-preview": "Дехар до, агӀо Ӏалаш ярал хьалха хьажа муха ю из!",
        "tooltip-diff": "Гайта долуш долу йозанах бина болу хийцам.",
-       "tooltip-compareselectedversions": "ХӀокху шина хаьржина агӀона башхо муха ю хьажа.",
+       "tooltip-compareselectedversions": "ХӀокху агӀона шина хаьржина версийн башхалле хьажар.",
        "tooltip-watch": "ТӀетоха хӀара агӀо сан тергаме могӀанан юкъа",
        "tooltip-watchlistedit-normal-submit": "Билгалйина цӀераш дӀаяха",
        "tooltip-watchlistedit-raw-submit": "Тергаме могӀам карлабаккха",
        "file-nohires": "Кхи йоккха гlоле башхо яц.",
        "svg-long-desc": "SVG-файл, лартӀахь ю $1 × $2 пиксель, файлан барам: $3",
        "svg-long-desc-animated": "Анимироват йина SVG-файл, номиналан $1 × $2 пиксель, файлан барам: $3",
-       "show-big-image": "СÑ\83Ñ\80Ñ\82 Ñ\86lанал Ð»Ð°ÐºÐºÑ\85аÑ\80а Ð±Ð°ÐºÑ\8aонÑ\86а",
+       "show-big-image": "Ð\9eÑ\80игиналан Ñ\84айл",
        "show-big-image-preview": "Барам хьажале: $1.",
        "show-big-image-other": "{{PLURAL:$2|1=Кхин шоралла|Кхин шоралла}}: $1.",
        "show-big-image-size": "$1 × $2 пиксель",
index e715dbb..0e0d207 100644 (file)
@@ -44,6 +44,7 @@
        "tog-diffonly": "Тенъештирме саифелеринде саифенинъ эсас мундериджесини косьтерме",
        "tog-showhiddencats": "Гизли категорияларны косьтер",
        "tog-norollbackdiff": "Кери къайтарув япылгъан сонъ версиялар арасындаки фаркъны косьтерме",
+       "tog-prefershttps": "Системагъа кирген сонъ эр вакъыт телюкесиз багълама къулланылсын",
        "underline-always": "Даима",
        "underline-never": "Асла",
        "underline-default": "Браузер сазламалары къулланылсын",
        "pool-errorunknown": "Билинмеген хата",
        "aboutsite": "{{SITENAME}} акъкъында",
        "aboutpage": "Project:Акъкъында",
-       "copyright": "Ð\9cалÑ\8eмаÑ\82 $1 Ð±Ð¸Ð½Ð°Ñ\8dн ÐºÐµÑ\87илип Ð¾Ð»Ð°.",
+       "copyright": "Ð\91аÑ\88кÑ\8aаÑ\81Ñ\8b Ð±Ð¸Ð»Ñ\8cдиÑ\80илÑ\8cмеÑ\81е, Ð¼Ð°Ð»Ñ\8eмаÑ\82 $1 Ð»Ð¸Ñ\86ензиÑ\8fÑ\81Ñ\8bнен Ð±ÐµÑ\80иле.",
        "copyrightpage": "{{ns:project}}:Муэллифлик акълары",
        "currentevents": "Шимдики вакъиалар",
        "currentevents-url": "Project:Агъымдаки вакъиалар",
        "perfcached": "Ашагъыдаки малюмат кэштен алынды ве эскирген ола билир! Кэште энъ чокъ {{PLURAL:$1|1=бир нетидже|$1 нетидже}} сакъланып тура.",
        "perfcachedts": "Ашагъыдаки малюмат кэштен алынды, кэшнинъ сонъки янъартылгъан вакъты: $1. Кэште энъ чокъ {{PLURAL:$1|1=бир нетидже|$1 нетидже}} сакъланып тура.",
        "querypage-no-updates": "Бу саифени денъиштирмеге шимди изин ёкъ. Бу малюмат аман янъартылмайджакъ.",
-       "viewsource": "менба кодуны косьтер",
+       "viewsource": "Ð\9cенба кодуны косьтер",
        "viewsource-title": "$1 саифесининъ менба коду",
        "actionthrottled": "Арекет токъталды",
        "actionthrottledtext": "Спамгъа къаршы куреш себебинден бу арекетни аз вакъыт ичинде чокъ кере текрарлап оламайсынъыз. Мумкюн олгъан къарардан зияде арекет яптынъыз. Бир къач дакъкъадан сонъ текрарлап бакъынъыз.",
        "virus-badscanner": "Янълыш сазлама. Билинмеген вирус сканери: ''$1''",
        "virus-scanfailed": "скан этюв мувафакъиетсиз (код $1)",
        "virus-unknownscanner": "билинмеген антивирус:",
-       "logouttext": "'''Отурымны къапаттынъыз.'''\n\nШимди {{SITENAME}} сайтыны аноним оларакъ къулланып оласынъыз, я да янъыдан <span class='plainlinks'>[$1 отурым ачып]</span> оласынъыз (истер айны къулланыджы адынен, истер башкъа бир къулланыджы адынен). Web браузеринъиз кэшини темизлегендже базы саифелер санки аля даа отурымынъыз ачыкъ экен киби корюнип олур.",
+       "logouttext": "<strong>Системадан чыкътынъыз.</strong>\n\nБраузеринъиз кешини темизлегендже базы саифелер системадан аля даа чыкъмагъансынъыз киби корюнип олур.",
+       "welcomeuser": "Хош кельдинъиз, $1!",
+       "welcomecreation-msg": "Эсап язынъыз яратылды.\nИстесенъиз, [[Special:Preferences|{{SITENAME}} сазламаларынъызны]] денъиштире билесинъиз.",
        "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": "Парольни бир даа язынъыз",
        "remembermypassword": "Киришимни бу компьютерде хатырла (энъ чокъ $1 {{PLURAL:$1|1=кунь|кунь}} ичюн)",
+       "userlogin-remembermypassword": "Системада къалайым",
+       "userlogin-signwithsecure": "Телюкесиз багълама къулланылсын",
        "yourdomainname": "Домен адынъыз",
+       "password-change-forbidden": "Бу викиде паролинъизни денъиштирип оламайсынъыз",
        "externaldberror": "Сайткъа киргенде бир хата олды. Бу тыш эсабынъызны денъиштирмек акъкъынъыз олмагъанындан себеп мейдангъа келип ола.",
        "login": "Кириш",
        "nav-login-createaccount": "Кириш / Къайд олув",
        "userloginnocreate": "Кириш",
        "logout": "Чыкъыш",
        "userlogout": "Чыкъыш",
-       "notloggedin": "Отурым ачмадынъыз.",
+       "notloggedin": "Системагъа кирмединъиз.",
+       "userlogin-noaccount": "Аккаунтынъыз ёкъмы?",
+       "userlogin-joinproject": "{{SITENAME}} лейхасына къошулынъыз",
        "nologin": "Даа эсап ачмадынъызмы? '''$1'''.",
        "nologinlink": "Къайд ол",
-       "createaccount": "ЯнÑ\8aÑ\8b Ñ\8dÑ\81ап Ð°Ñ\87",
-       "gotaccount": "Ð\94аа Ñ\8dвелÑ\8c Ñ\8dÑ\81ап Ð°Ñ\87къан эдинъизми? '''$1'''.",
-       "gotaccountlink": "Ð\9eÑ\82Ñ\83Ñ\80Ñ\8bм Ð°Ñ\87Ñ\8bнÑ\8aÑ\8bз",
+       "createaccount": "Ð\9aÑ\8aайд Ð¾Ð»Ñ\83в",
+       "gotaccount": "Ð\94аа Ñ\8dвелÑ\8c Ñ\81айÑ\82Ñ\82а ÐºÑ\8aайд Ð¾Ð»Ð³ъан эдинъизми? '''$1'''.",
+       "gotaccountlink": "СиÑ\81Ñ\82емагÑ\8aа ÐºÐ¸Ñ\80инÑ\8aиз",
        "userlogin-resetlink": "Кириш малюматыны унуттынъызмы?",
-       "createaccountmail": "e-mail вастасынен",
+       "createacct-emailrequired": "E-mail adresi",
+       "createacct-emailoptional": "E-mail адреси (меджбурий дегиль)",
+       "createacct-email-ph": "E-mail адресинъизни язынъыз",
+       "createacct-another-email-ph": "E-mail адресинъизни язынъыз",
+       "createaccountmail": "Автоматик оларакъ мейдангъа кетирильген мувакъкъат бир пароль къуллана билир ве бу парольни бильдирильген e-mail адресине ёллай билирсинъиз",
+       "createacct-realname": "Акъикъий адынъыз (меджбурий дегиль)",
        "createaccountreason": "Себеп:",
+       "createacct-reason": "Себеп",
+       "createacct-reason-ph": "Башкъа бир эсап язысы неден себеп яратасынъыз",
+       "createacct-captcha": "Телюкесизлик контроли",
+       "createacct-imgcaptcha-ph": "Юкъарыда корьген метнинъизни язынъыз",
+       "createacct-submit": "Эсап язынъызны яратынъыз",
+       "createacct-another-submit": "Башкъа бир эсап язысы яратынъыз",
+       "createacct-benefit-heading": "{{SITENAME}} сизинъ киби адамлар тарафындан языла.",
+       "createacct-benefit-body1": "денъиштирме",
+       "createacct-benefit-body2": "{{PLURAL:$1|саифе|саифе}}",
+       "createacct-benefit-body3": "сонъки вакъытларда {{PLURAL:$1|иссесини къошкъан къулланыджы|иссесини къошкъан къулланыджы}}",
        "badretype": "Кирсеткен пароллеринъиз айны дегиль.",
        "userexists": "Кирсеткен къулланыджы адынъыз энди къулланыла.\nЛютфен, башкъа бир къулланыджы ады сайланъыз.",
        "loginerror": "Отурым ачма хатасы",
+       "createacct-error": "Эсап язысыны яратув хатасы",
        "createaccounterror": "Эсап яратылып оламай: $1",
        "nocookiesnew": "Къулланыджы эсабы ачылгъан, факъат танытылмагъан. {{SITENAME}} къулланыджыларны танытмакъ ичюн «cookies»ни къуллана. Сизде бу функция къапалы вазиеттедир. «Cookies» функциясыны ишлетип текрар янъы адынъыз ве паролинъизнен тырышып бакъыныз.",
        "nocookieslogin": "{{SITENAME}} «cookies»ни къуллана. Сизде бу функция къапалы вазиеттедир. «Cookies» функциясыны ишлетип текрар тырышып бакъынъыз.",
        "passwordtooshort": "Паролинъизде энъ аз {{PLURAL:$1|1=1|$1}} ишарет олмалы.",
        "password-name-match": "Паролинъиз къулланыджы адынъыздан фаркълы олмалы.",
        "password-login-forbidden": "Бу къулланыджы ады ве парольни къулланмакъ ясакътыр.",
-       "mailmypassword": "ЯнÑ\8aÑ\8b Ð¿Ð°Ñ\80олÑ\8c Ð¹Ð¸Ð±ÐµÑ\80",
+       "mailmypassword": "Ð\9fаÑ\80олÑ\8cни Ñ\81Ñ\8bÑ\84Ñ\8bÑ\80ла",
        "passwordremindertitle": "{{grammar:genitive|{{SITENAME}}}} къулланыджынынъ пароль хатырлатувы",
        "passwordremindertext": "Бирев (бельки де бу сизсинъиз, $1 IP адресинден) {{SITENAME}} сайты ичюн ($4) янъы къулланыджы паролини истеди.\n$2 къулланыджысына вакътынджа <code>$3</code> пароли яратылды. Эгер бу керчектен де сизинъ истегинъиз олгъан олса, отурым ачып янъы бир пароль яратманъыз керектир. Мувакъкъат паролинъизнинъ муддети {{PLURAL:$5|1=1 кунь|$5 кунь}} ичинде доладжакъ.\n\nЭгер де янъы пароль талап этмеген олсанъыз я да эски паролинъизни хатырлап энди оны денъиштирмеге истемесенъиз, бу мектюпни дикъкъаткъа алмайып эски паролинъизни къулланмагъа девам этип оласынъыз.",
        "noemail": "$1 адлы къулланыджы ичюн e-mail бильдирильмеди.",
        "login-throttled": "Якъын заманда пек чокъ кере кирмеге тырыштынъыз.\nЛютфен, къайта кирмезден эвель бираз бекленъиз.",
        "loginlanguagelabel": "Тиль: $1",
        "suspicious-userlogout": "Чыкъыш истегенинъиз ред этильди, чюнки бозукъ бир браузер я да кэшлейиджи прокси тарафындан ёллангъан киби корюне.",
+       "pt-login": "Кириш",
+       "pt-login-button": "Кириш",
+       "pt-createaccount": "Къайд олув",
+       "pt-userlogout": "Чыкъыш",
        "changepassword": "Пароль денъиштир",
        "resetpass_announce": "Мувакъкъат код вастасынен кирдинъиз. Киришни тамамламакъ ичюн янъы парольни мында къоюнъыз:",
        "resetpass_header": "Эсапнынъ паролини денъиштир",
        "session_fail_preview": "''' Сервер сиз япкъан денъиштирмелерни сессия идентификаторы джоюлгъаны себебинден сакълап оламады.\nБу вакътынджа проблемадыр. Лютфен, текрар сакълап бакъынъыз.\nБундан да сонъ олып чыкъмаса, малюмат локаль файлгъа сакъланъыз да браузеринъизни бир къапатып ачынъыз.'''",
        "session_fail_preview_html": "'''Афу этинъиз! HTML сессиянынъ малюматлары гъайып олгъаны себебинден сизинъ денъиштирмелеринъизни къабул этмеге имкян ёкътыр.'''",
        "token_suffix_mismatch": "'''Сизинъ программанъызнынъ озь тюрлендирюв пенджересинде пунктуация ишаретлерини догъру ишлемегени ичюн япкъан денъиштирмелеринъиз къабул олунмады. Денъиштирмелер саифе метнининъ корюниши бозулмасын деп лягъу этильди.\nБунынъ киби проблемалар хаталы аноним web-проксилер къулланувдан чыкъып ола.'''",
-       "editing": "\"$1\" саифесини денъиштиреятасыз",
+       "editing": "«$1» саифесини денъиштиреятасыз",
+       "creating": "«$1» саифесини яратув",
        "editingsection": "\"$1\" саифесинде болюк денъиштиреятасыз",
        "editingcomment": "$1 саифесини денъиштиреятасыз (янъы болюк)",
        "editconflict": "Денъиштирмелер чатышмасы: $1",
        "edit-gone-missing": "Саифе янъартылып оламай.\nБельки о ёкъ этильгендир.",
        "edit-conflict": "Денъиштирмелер чатышмасы.",
        "edit-no-change": "Япкъан денъиштирменъиз сакъланмагъан, чюнки метинде бир тюрлю денъиштирильме япылмады.",
+       "postedit-confirmation-created": "Саифе яратылды.",
+       "postedit-confirmation-saved": "Япкъан денъиштирменъиз сакъланды.",
        "edit-already-exists": "Янъы саифени яратмакъ мумкюн дегиль.\nО энди бар.",
        "undo-success": "Денъиштирме лягъу этилип ола. Лютфен, мына бу денъиштирмелерни япмагъа истегенинъизден эмин олмакъ ичюн версиялар тенъештирилювини козьден кечирип денъиштирмелерни сакъламакъ ичюн «Саифени сакъла» дёгмесине басынъыз.",
        "undo-failure": "Арадаки денъиштирмелер бир-бирине келишикли олмагъаны ичюн денъиштирме лягъу этилип оламай.",
        "viewpagelogs": "Бу саифенинъ журналларыны косьтер",
        "nohistory": "Бу саифенинъ кечмиш версиясы ёкъ.",
        "currentrev": "Шимдики версия",
-       "currentrev-asof": "$1 тарихында сонъки кере денъиштирильген саифенинъ шимдики алы",
+       "currentrev-asof": "$1 тарихындан башлап саифенинъ шимдики алы",
        "revisionasof": "Саифенинъ $1 тарихындаки алы",
        "revision-info": "Саифенинъ $2 тарафындан язылгъан $1 тарихындаки алы",
        "previousrevision": "← Эвельки алы",
        "preferences": "Сазламалар",
        "mypreferences": "Сазламалар",
        "prefs-edits": "Денъиштирмелер сайысы:",
+       "prefsnologintext2": "Сазламаларынъызны денъиштирмек ичюн лютфен системагъа киринъиз.",
        "prefs-skin": "Ресимлеме",
        "skin-preview": "Бакъып чыкъув",
        "datedefault": "Стандарт",
        "recentchanges-label-minor": "Бу, кичик бир денъиштирме",
        "recentchanges-label-bot": "Бу бир ботнынъ япкъан денъиштирмеси",
        "recentchanges-label-unpatrolled": "Бу денъиштирме аля даа тешкерильмеген",
+       "recentchanges-label-plusminus": "Байт эсабынен саифе буюклигининъ денъиштирильмеси",
+       "recentchanges-legend-heading": "'''Ишаретлер:'''",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} ([[Special:NewPages|янъы саифелер джедвелине]] де бакъынъыз)",
-       "rcnotefrom": "'''$2''' тарихындан итибарен япылгъан денъиштирмелер ашагъыдадыр (энъ чокъ '''$1''' дане саифе косьтериле).",
+       "rcnotefrom": "<strong>$3, $4</strong> тарихындан башлап япылгъан {{PLURAL:$5|денъиштирме|денъиштирмелер}} ашагъыдадыр (энъ чокъ <strong>$1</strong> дане саифе косьтериле).",
        "rclistfrom": "$3 $2 тарихындан берли япылгъан денъиштирмелерни косьтер",
        "rcshowhideminor": "кичик денъиштирмелерни $1",
        "rcshowhideminor-show": "косьтер",
        "rcshowhidebots": "ботларны $1",
        "rcshowhidebots-show": "косьтер",
        "rcshowhidebots-hide": "гизле",
-       "rcshowhideliu": "Ð\9aъайдлы къулланыджыларны $1",
+       "rcshowhideliu": "къайдлы къулланыджыларны $1",
        "rcshowhideliu-show": "косьтер",
        "rcshowhideliu-hide": "гизле",
        "rcshowhideanons": "аноним къулланыджыларны $1",
        "number_of_watching_users_pageview": "[$1 {{PLURAL:$1|1=къулланыджы|къулланыджы}} козете]",
        "rc_categories": "Тек категориялардан («|» иле айырыла)",
        "rc_categories_any": "Эр анги",
+       "rc-change-size-new": "Денъиштирильген сонъ $1 {{PLURAL:$1|байт|байт}}",
        "newsectionsummary": "/* $1 */ янъы болюк",
        "rc-enhanced-expand": "Тафсилятыны косьтер",
        "rc-enhanced-hide": "Тафсилятыны гизле",
        "reuploaddesc": "Юклеме формасына кери къайт.",
        "upload-tryagain": "Денъиштирильген файл тарифини ёлла",
        "uploadnologin": "Отурым ачмадынъыз",
-       "uploadnologintext": "Файл юклеп олмакъ ичюн [[Special:UserLogin|отурым ачмакъ]] керексинъиз.",
+       "uploadnologintext": "Файл юклеп олмакъ ичюн лютфен $1.",
        "upload_directory_missing": "Юклемелер ичюн директория ($1) мевджут дегиль ве веб-сервер тарафындан япылып оламай.",
        "upload_directory_read_only": "Web серверининъ ($1) джузьданына файллар сакъламагъа акълары ёкътыр.",
        "uploaderror": "Юклеме хатасы",
        "statistics-header-hooks": "Дигер статистика",
        "doubleredirects": "Ёлламагъа олгъан ёлламалар",
        "doubleredirectstext": "Бу саифеде дигер ёллама саифелерине ёлланма олгъан саифелери косьтериле.\nЭр сатырда биринджи ве экинджи ёлламагъа багълантылар да, экинджи ёлламанынъ макъсат саифеси (адетиндже о биринджи ёлламанынъ керекли макъсады ола) да бар.\n<del>Устю сызылгъан</del> меселелер энди чезильген.",
-       "double-redirect-fixed-move": "[[$1]] авуштырылды, шимди [[$2]] саифесине ёллап тура.",
+       "double-redirect-fixed-move": "[[$1]] авуштырылды. О, автоматик оларакъ янъартылып шимди [[$2]] саифесине ёнетип тура.",
        "brokenredirects": "Бар олмагъан саифеге япылгъан ёлламалар",
        "brokenredirectstext": "Ашагъыдаки ёлламалар бар олмагъан саифелерге багъланты берелер:",
        "brokenredirects-edit": "денъиштир",
        "pager-older-n": "{{PLURAL:$1|1=даа эски 1|даа эски $1}}",
        "booksources": "Китаплар менбасы",
        "booksources-search-legend": "Китаплар менбасыны къыдырув",
+       "booksources-search": "Къыдыр",
        "specialloguserlabel": "Къулланыджы:",
        "speciallogtitlelabel": "Серлева:",
        "log": "Журналлар",
        "emailsenttext": "Сизинъ e-mail беянатынъыз ёлланды",
        "emailuserfooter": "Бу мектюп $1 тарафындан $2 къулланыджысына, {{SITENAME}} сайтындаки \"Къулланыджыгъа e-mail ёлла\" функциясынен ёллангъан.",
        "watchlist": "Козетюв джедвели",
-       "mywatchlist": "Козетюв джедвелим",
+       "mywatchlist": "Козетюв джедвели",
+       "watchlistfor2": "$1 ичюн $2",
        "nowatchlist": "Сизинъ козетюв джедвелинъиз боштыр.",
        "watchlistanontext": "Козетюв джедвелини бакъмакъ я да денъиштирмек ичюн $1 борджлусынъыз.",
        "watchnologin": "Отурым ачмакъ керек",
        "unwatching": "Козетюв джедвелинден ёкъ этильмекте...",
        "enotif_reset": "Джумле саифелерни бакъылгъан оларакъ ишаретле",
        "enotif_impersonal_salutation": "{{SITENAME}} къулланыджысы",
+       "enotif_subject_moved": "{{SITENAME}} лейхасынынъ $1 адлы саифесининъ ады, $2 тарафындан {{GENDER:$2|денъиштирильди}}.",
+       "enotif_body_intro_moved": "{{SITENAME}} лейхасынынъ $1 адлы саифесининъ ады $PAGEEDITDATE тарихында $2 тарафындан {{GENDER:$2|денъиштирильди}}. Шимдики алыны мында коре билесинъиз: $3.",
        "enotif_lastvisited": "Сонъки зияретинъизден берли япылгъан денъиштирмелерни корьмек ичюн $1 бакъынъыз.",
        "enotif_anon_editor": "адсыз (аноним) къулланыджы $1",
        "enotif_body": "Сайгъылы $WATCHINGUSERNAME,\n\n$PAGEINTRO $NEWPAGE\n\nДенъиштирменинъ къыскъа тарифи: $PAGESUMMARY $PAGEMINOREDIT\n\nСаифени денъиштирген къулланыджынен багъланмакъ ичюн:\nэ-маиль адреси: $PAGEEDITOR_EMAIL\nвики саифеси: $PAGEEDITOR_WIKI\n\nБу саифени зиярет этмесенъиз, бирев оны бир даа денъиштирсе де, ич бир тенби беянаты ёлланмайджакъ. Козетюв джедвелинъиздеки бутюн саифелер ичюн тенби сазламаларыны денъиштире билесинъиз.\n\n{{SITENAME}} бильдирюв системасы\n\n--\n\nБильдирюв сазламаларыны денъиштирмек ичюн:\n{{canonicalurl:{{#special:Preferences}}}}\n\nКозетюв джедвели сазламаларыны денъиштирмек ичюн:\n{{canonicalurl:{{#special:EditWatchlist}}}}\n\nСаифени козетюв джедвелинден чыкъармакъ ичюн:\n$UNWATCHURL\n\nЯрдым ве теклифлер ичюн:\n$HELPPAGE",
        "protectedarticle": "\"[[$1]]\" къорчалав алтына алынды",
        "modifiedarticleprotection": "«[[$1]]» ичюн къорчалав севиеси денъиштирильди",
        "unprotectedarticle": "\"[[$1]]\" саифесинден къорчалав чыкъарлыды",
-       "prot_1movedto2": "\"[[$1]]\" саифесининъ ады \"[[$2]]\" оларакъ денъиштирильди",
+       "prot_1movedto2": "[[$1]] саифесининъ ады [[$2]] деп денъиштирильди",
        "protect-legend": "Къорчалавны тасдыкъла",
        "protectcomment": "Себеп:",
        "protectexpiry": "Битиш тарихы:",
        "undeletecomment": "Себеп:",
        "undeletedrevisions": "Топлам {{PLURAL:$1|1=1 къайд|$1 къайд}} кери кетирильди.",
        "undelete-header": "Кеченлерде ёкъ этильген саифелерни корьмек ичюн [[Special:Log/delete|ёкъ этюв журналына]] бакъынъыз.",
+       "undelete-search-submit": "Къыдыр",
        "namespace": "Исим фезасы:",
        "invert": "Сайлангъан тышындакилерни сайла",
+       "namespace_association": "Багълы исим фезасы",
        "blanknamespace": "(Эсас)",
        "contributions": "{{GENDER:$1|Къулланыджынынъ}} исселери",
        "contributions-title": "$1 къулланыджысынынъ исселери",
        "sp-contributions-userrights": "къулланыджы акъларыны идаре этюв",
        "sp-contributions-search": "Исселерни къыдырув",
        "sp-contributions-username": "IP адреси я да къулланыджы ады:",
+       "sp-contributions-toponly": "Тек сонъки версиясы олгъан денъиштирмелерни косьтер",
+       "sp-contributions-newonly": "Тек янъы саифе яраткъан денъиштирмелерни косьтер",
        "sp-contributions-submit": "Къыдыр",
        "whatlinkshere": "Бу саифеге багълантылар",
        "whatlinkshere-title": "$1 саифесине багъланты олгъан саифелер",
        "unblockip": "Къулланыджынынъ блок этмесини чыкъар",
        "ipusubmit": "Бу блок этмени чыкъар",
        "ipblocklist": "Блок этильген къулланыджылар ве IP адреслери",
+       "ipblocklist-submit": "Къыдыр",
        "infiniteblock": "муддетсиз",
        "expiringblock": "$1 $2 тарихында битеджек",
        "blocklink": "блок эт",
        "lockbtn": "Малюмат базасы килитли",
        "move-page": "$1 саифесининъ адыны денъиштиреятасыз",
        "move-page-legend": "Саифенинъ адыны денъиштирюв",
-       "movepagetext": "Ð\90Ñ\88агÑ\8aÑ\8bдаки Ñ\84оÑ\80ма ÐºÑ\8aÑ\83лланÑ\8bлÑ\8bп Ñ\81аиÑ\84енинÑ\8a Ð°Ð´Ñ\8b Ð´ÐµÐ½Ñ\8aиÑ\88Ñ\82иÑ\80илиÑ\80. Ð\91Ñ\83нÑ\8bнÑ\8aнен Ð±ÐµÑ\80абеÑ\80 Ð´ÐµÐ½Ñ\8aиÑ\88Ñ\82иÑ\80мелеÑ\80 Ð¶Ñ\83Ñ\80налÑ\8b Ð´Ð° Ñ\8fнÑ\8aÑ\8b Ð°Ð´Ð³Ñ\8aа Ð°Ð²Ñ\83Ñ\88Ñ\82Ñ\8bÑ\80Ñ\8bлÑ\8bÑ\80.\nЭÑ\81ки Ð°Ð´Ñ\8b Ñ\8fнÑ\8aÑ\8b Ð°Ð´Ñ\8bна Ñ\91ллама Ð¾Ð»Ñ\83Ñ\80. Ð­Ñ\81ки Ñ\81еÑ\80левагÑ\8aа Ñ\91ллама Ñ\81аиÑ\84елеÑ\80ни Ð°Ð²Ñ\82омаÑ\82ик Ð¾Ð»Ð°Ñ\80акÑ\8a Ñ\8fнÑ\8aаÑ\80Ñ\82Ñ\8bп Ð¾Ð»Ð°Ñ\81Ñ\8bнÑ\8aÑ\8bз. Ð\91Ñ\83 Ð°Ñ\80екеÑ\82ни Ð°Ð²Ñ\82омаÑ\82ик Ñ\8fпмагÑ\8aа Ð¸Ñ\81Ñ\82емеÑ\81енÑ\8aиз, Ð±Ñ\83Ñ\82Ñ\8eн [[Special:DoubleRedirects|Ñ\87иÑ\84Ñ\82]] Ð²Ðµ [[Special:BrokenRedirects|йÑ\8bÑ\80Ñ\82Ñ\8bкÑ\8a]] Ñ\91ллама Ñ\81аиÑ\84елеÑ\80ини Ð¾Ð·Ñ\8eнÑ\8aиз Ñ\82Ñ\8eзеÑ\82меге Ð¼ÐµÐ´Ð¶Ð±Ñ\83Ñ\80 Ð¾Ð»Ñ\83Ñ\80Ñ\81Ñ\8bнÑ\8aÑ\8bз. Ð\91агÑ\8aланÑ\82Ñ\8bлаÑ\80 Ñ\8dндиден Ð±ÐµÑ\80ли Ð´Ð¾Ð³Ñ\8aÑ\80Ñ\83 Ñ\87алÑ\8bÑ\88маÑ\81Ñ\8bндан Ñ\8dмин Ð¾Ð»Ð¼Ð°Ð»Ñ\8bÑ\81Ñ\8bнÑ\8aÑ\8bз.\n\nЯнÑ\8aÑ\8b Ð°Ð´Ð´Ð° Ð±Ð¸Ñ\80 Ñ\81аиÑ\84е Ñ\8dнди Ð±Ð°Ñ\80 Ð¾Ð»Ñ\81а, Ð°Ð´ Ð´ÐµÐ½Ñ\8aиÑ\88Ñ\82иÑ\80илÑ\8eви '''Ñ\8fпÑ\8bлмайджакÑ\8a''', Ð°Ð½Ð´Ð¶Ð°ÐºÑ\8a Ð±Ð°Ñ\80 Ð¾Ð»Ð³Ñ\8aан Ñ\81аиÑ\84е Ñ\91ллама Ñ\8f Ð´Ð° Ð±Ð¾Ñ\88 Ð¾Ð»Ñ\81а Ð°Ð´ Ð´ÐµÐ½Ñ\8aиÑ\88Ñ\82иÑ\80илÑ\8eви Ð¼Ñ\83мкÑ\8eн Ð¾Ð»Ð°Ð´Ð¶Ð°ÐºÑ\8a. Ð\91Ñ\83 Ð´ÐµÐ¼ÐµÐº ÐºÐ¸, Ñ\81аиÑ\84енинÑ\8a Ð°Ð´Ñ\8bнÑ\8b Ñ\8fнÑ\8aлÑ\8bÑ\88Ñ\82ан Ð´ÐµÐ½Ñ\8aиÑ\88Ñ\82иÑ\80ген Ð¾Ð»Ñ\81анÑ\8aÑ\8bз Ð´ÐµÐ¼Ð¸Ð½ÐºÐ¸ Ð°Ð´Ñ\8bнÑ\8b ÐºÐµÑ\80и ÐºÑ\8aайÑ\82аÑ\80Ñ\8bп Ð¾Ð»Ð°Ñ\81Ñ\8bнÑ\8aÑ\8bз, Ð°Ð¼Ð¼Ð° Ð±Ð°Ñ\80 Ð¾Ð»Ð³Ñ\8aан Ñ\81аиÑ\84ени Ñ\82еÑ\81адÑ\8eÑ\84ен Ñ\91кÑ\8a Ñ\8dÑ\82амайÑ\81Ñ\8bнÑ\8aÑ\8bз.\n\n'''ТÐ\95Ð\9dÐ\91Ð\98!'''\nАд денъиштирилюви популяр саифелер ичюн буюк ве бекленмеген денъишмелерге себеп ола билир. Лютфен, денъиштирме япмаздан эвель ола биледжеклерни козь огюне алынъыз.",
+       "movepagetext": "Ð\90Ñ\88агÑ\8aÑ\8bдаки Ñ\84оÑ\80ма ÐºÑ\8aÑ\83лланÑ\8bлÑ\8bп Ñ\81аиÑ\84енинÑ\8a Ð°Ð´Ñ\8b Ð´ÐµÐ½Ñ\8aиÑ\88Ñ\82иÑ\80илиÑ\80. Ð\91Ñ\83нÑ\8bнÑ\8aнен Ð±ÐµÑ\80абеÑ\80 Ð´ÐµÐ½Ñ\8aиÑ\88Ñ\82иÑ\80мелеÑ\80 Ð¶Ñ\83Ñ\80налÑ\8b Ð´Ð° Ñ\8fнÑ\8aÑ\8b Ð°Ð´Ð³Ñ\8aа Ð°Ð²Ñ\83Ñ\88Ñ\82Ñ\8bÑ\80Ñ\8bлÑ\8bÑ\80.\nЭÑ\81ки Ð°Ð´Ñ\8b Ñ\8fнÑ\8aÑ\8b Ð°Ð´Ñ\8bна Ñ\91неÑ\82ме Ð¾Ð»Ñ\83Ñ\80. Ð­Ñ\81ки Ñ\81еÑ\80левагÑ\8aа Ñ\91неÑ\82ип Ñ\82Ñ\83Ñ\80гÑ\8aан Ñ\81аиÑ\84елеÑ\80ни Ð°Ð²Ñ\82омаÑ\82ик Ð¾Ð»Ð°Ñ\80акÑ\8a Ñ\8fнÑ\8aаÑ\80Ñ\82Ñ\8bп Ð¾Ð»Ð°Ñ\81Ñ\8bнÑ\8aÑ\8bз. Ð\91Ñ\83 Ð°Ñ\80екеÑ\82ни Ð°Ð²Ñ\82омаÑ\82ик Ñ\8fпмагÑ\8aа Ð¸Ñ\81Ñ\82емеÑ\81енÑ\8aиз, Ð±Ñ\83Ñ\82Ñ\8eн [[Special:DoubleRedirects|Ñ\87иÑ\84Ñ\82]] Ð²Ðµ [[Special:BrokenRedirects|йÑ\8bÑ\80Ñ\82Ñ\8bкÑ\8a]] Ñ\91неÑ\82ме Ñ\81аиÑ\84елеÑ\80ини Ð¾Ð·Ñ\8eнÑ\8aиз Ñ\82еÑ\88кеÑ\80меге Ð¼ÐµÐ´Ð¶Ð±Ñ\83Ñ\80 Ð¾Ð»Ñ\83Ñ\80Ñ\81Ñ\8bнÑ\8aÑ\8bз. Ð\91агÑ\8aланÑ\82Ñ\8bлаÑ\80 Ñ\8dндиден Ð±ÐµÑ\80ли Ð´Ð¾Ð³Ñ\8aÑ\80Ñ\83 Ñ\87алÑ\8bÑ\88маÑ\81Ñ\8bндан Ñ\8dмин Ð¾Ð»Ð¼Ð°Ð»Ñ\8bÑ\81Ñ\8bнÑ\8aÑ\8bз.\n\nЯнÑ\8aÑ\8b Ð°Ð´Ð´Ð° Ð±Ð¸Ñ\80 Ñ\81аиÑ\84е Ñ\8dнди Ð±Ð°Ñ\80 Ð¾Ð»Ñ\81а, Ð°Ð´ Ð´ÐµÐ½Ñ\8aиÑ\88Ñ\82иÑ\80илÑ\8eви <strong>Ñ\8fпÑ\8bлмайджакÑ\8a</strong>, Ð°Ð½Ð´Ð¶Ð°ÐºÑ\8a Ð±Ð°Ñ\80 Ð¾Ð»Ð³Ñ\8aан Ñ\81аиÑ\84е Ñ\91неÑ\82ме Ñ\8f Ð´Ð° Ð±Ð¾Ñ\88 Ð¾Ð»Ñ\81а Ð°Ð´ Ð´ÐµÐ½Ñ\8aиÑ\88Ñ\82иÑ\80илÑ\8eви Ð¼Ñ\83мкÑ\8eн Ð¾Ð»Ð°Ð´Ð¶Ð°ÐºÑ\8a. Ð\91Ñ\83 Ð´ÐµÐ¼ÐµÐº ÐºÐ¸, Ñ\81аиÑ\84енинÑ\8a Ð°Ð´Ñ\8bнÑ\8b Ñ\8fнÑ\8aлÑ\8bÑ\88Ñ\82ан Ð´ÐµÐ½Ñ\8aиÑ\88Ñ\82иÑ\80ген Ð¾Ð»Ñ\81анÑ\8aÑ\8bз Ð´ÐµÐ¼Ð¸Ð½ÐºÐ¸ Ð°Ð´Ñ\8bнÑ\8b ÐºÐµÑ\80и ÐºÑ\8aайÑ\82аÑ\80Ñ\8bп Ð¾Ð»Ð°Ñ\81Ñ\8bнÑ\8aÑ\8bз, Ð°Ð¼Ð¼Ð° Ð±Ð°Ñ\80 Ð¾Ð»Ð³Ñ\8aан Ñ\81аиÑ\84ени Ñ\82еÑ\81адÑ\8eÑ\84ен Ñ\91кÑ\8a Ñ\8dÑ\82амайÑ\81Ñ\8bнÑ\8aÑ\8bз.\n\n<strong>ТÐ\95Ð\9dÐ\91Ð\98!</strong>\nАд денъиштирилюви популяр саифелер ичюн буюк ве бекленмеген денъишмелерге себеп ола билир. Лютфен, денъиштирме япмаздан эвель ола биледжеклерни козь огюне алынъыз.",
        "movepagetalktext": "Къошулгъан музакере саифесининъ де (бар олса)\nады автоматик тарзда денъиштириледжек. '''Мустесналар:'''\n\n* Айны бу адда бош олмагъан бир музакере саифеси энди бар;\n* Ашагъыдаки бошлукъкъа ишарет къоймадынъыз.\n\nБойле алларда, керек олса, саифелерни къолнен ташымагъа я да бирлештирмеге меджбур олурсынъыз.",
        "movearticle": "Эски ад",
+       "movecategorypage-warning": "<strong>Ихтар:</strong> Бир категория саифесининъ адыны денъиштирмек узьресинъиз. Лютфен, ялынъыз категория саифесининъ кочюриледжегини ве эски категорияда ер алгъан саифелернинъ янъы категориягъа авотматик оларакъ <em>авуштырылмайджагъыны</em> унутманъыз.",
        "movenologintext": "Саифенинъ адыны денъиштирип олмакъ ичюн [[Special:UserLogin|отурым ачынъыз]].",
        "movenotallowed": "Саифелер адларыны денъиштирмеге изининъиз ёкъ.",
        "newtitle": "Янъы ад",
        "move-subpages": "Алт саифелернинъ адларыны да денъиштир ($1 саифеге къадар)",
        "move-talk-subpages": "Muzakere saifesi alt saifeleriniñ adlarını da deñiştir ($1 saifege qadar)",
        "movepage-page-exists": "$1 саифеси энди бар, ве автоматик оларакъ янъыдан язылып оламаз.",
-       "movepage-page-moved": "$1 Ñ\81аиÑ\84еÑ\81ининÑ\8a Ð°Ð´Ñ\8b $2 Ð¾Ð»Ð°Ñ\80акÑ\8a денъиштирильди.",
+       "movepage-page-moved": "$1 Ñ\81аиÑ\84еÑ\81ининÑ\8a Ð°Ð´Ñ\8b $2 Ð´ÐµÐ¿ денъиштирильди.",
        "movepage-page-unmoved": "$1 саифесининъ ады $2 оларакъ денъиштирилип оламай.",
        "movelogpage": "Ад денъиштирильмелери журналы",
        "movelogpagetext": "Ашагъыда булунгъан джедвель ады денъиштирильген саифелерни косьтере",
        "allmessagesdefault": "Оригиналь метин",
        "allmessagescurrent": "Шимди къулланылгъан метин",
        "allmessagestext": "Ишбу джедвель MediaWiki-де мевджут олгъан бутюн система беянатларынынъ джедвелидир.\nMediaWiki интерфейсининъ чешит тиллерге терджиме этювде иштирак этмеге истесенъиз [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation MediaWiki Localisation] ве [//translatewiki.net translatewiki.net] саифелерине зиярет этинъиз.",
+       "allmessages-filter-legend": "Сюзгюч",
+       "allmessages-language": "Тиль:",
        "thumbnail-more": "Буют",
        "filemissing": "Файл тапылмады",
        "thumbnail_error": "Кичик ресим (thumbnail) яратылгъанда бир хата чыкъты: $1",
        "tooltip-pt-watchlist": "Козетювге алгъан саифелеринъиз",
        "tooltip-pt-mycontris": "Къошкъан исселеринъизнинъ джедвели",
        "tooltip-pt-login": "Отурым ачманъыз тевсие олуныр амма меджбур дегильсинъиз.",
-       "tooltip-pt-logout": "СиÑ\81Ñ\82емадан Ñ\87Ñ\8bкÑ\8aÑ\83в",
+       "tooltip-pt-logout": "ЧÑ\8bкÑ\8aÑ\8bÑ\88",
        "tooltip-ca-talk": "Саифедеки малюматнен багълы музакере",
        "tooltip-ca-edit": "Бу саифени денъиштирип оласынъыз. Сакъламаздан эвель бакъып чыкъмагъа унутманъыз.",
        "tooltip-ca-addsection": "Янъы болюкни ачув",
        "spambot_username": "Спамдан темизлев",
        "spam_reverting": "$1 сайтына багълантысы олмагъан сонъки версиягъа кери кетирюв",
        "spam_blanking": "Бар олгъан версияларда $1 сайтына багълантылар бар, темизлев",
+       "pageinfo-language": "Саифе ичиндекисининъ тили",
        "patrol-log-page": "Тешкерюв журналы",
        "log-show-hide-patrol": "Тешкерюв журналыны $1",
        "deletedrevision": "$1 сайылы эски версия ёкъ этильди.",
        "exif-gpsaltitude": "Юксеклик",
        "exif-gpstimestamp": "GPS сааты (атом сааты)",
        "exif-gpssatellites": "Ольчемек ичюн къуллангъаны спутниклер",
+       "exif-languagecode": "Тиль",
        "exif-compression-1": "Сыкъыштырылмагъан",
        "exif-orientation-3": "180° айландырылгъан",
        "exif-exposureprogram-1": "Эльнен",
        "specialpages": "Махсус саифелер",
        "specialpages-group-maintenance": "Бакъым эсабатлары",
        "specialpages-group-other": "Дигер махсус саифелер",
-       "specialpages-group-login": "Ð\9aиÑ\80иÑ\88 / Ð\9aъайд олув",
+       "specialpages-group-login": "Ð\9aиÑ\80иÑ\88 / Ðºъайд олув",
        "specialpages-group-changes": "Сонъки денъишикликлер ве журналлар",
        "specialpages-group-media": "Файл эсабатлары ве юклеме",
        "specialpages-group-users": "Къулланыджылар ве акълары",
        "specialpages-group-spam": "Спамгъа къаршы алетлер",
        "blankpage": "Бош саифе",
        "intentionallyblankpage": "Бу саифе аселет бош къалдырылгъан",
+       "tag-filter": "[[Special:Tags|Бельги]] сюзгючи:",
+       "tag-filter-submit": "Сюз",
+       "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Бельги|Бельгилер}}]]: $2)",
+       "tags-title": "Бельгилер",
        "htmlform-reset": "Денъишикликлерни кери ал",
-       "searchsuggest-containing": "ичинде бу олгъан..."
+       "logentry-move-move": "$1 адлы къуланыджы $3 саифесининъ адыны $4 деп {{GENDER:$2|денъиштирильди}}.",
+       "logentry-move-move-noredirect": "$1 адлы къулланыджы $3 саифесининъ адыны ёнетме къалдырмайып $4 деп {{GENDER:$2|денъиштирди}}",
+       "logentry-move-move_redir": "$1, $3 саифесининъ адыны ёнетме узеринден $4 деп {{GENDER:$2|денъиштирди}}",
+       "logentry-move-move_redir-noredirect": "$1 адлы къулланыджы $3 саифесининъ адыны ёнетме узеринден янъы бир ёнетме къалдырмайып $4 деп {{GENDER:$2|денъиштирди}}",
+       "searchsuggest-search": "Къыдыр",
+       "searchsuggest-containing": "ичинде бу олгъан...",
+       "pagelang-language": "Тиль"
 }
index 3d6d991..323b05f 100644 (file)
@@ -43,6 +43,7 @@
        "tog-diffonly": "Teñeştirme saifelerinde saifeniñ esas mündericesini kösterme",
        "tog-showhiddencats": "Gizli kategoriyalarnı köster",
        "tog-norollbackdiff": "Keri qaytaruv yapılğan soñ versiyalar arasındaki farqnı kösterme",
+       "tog-prefershttps": "Sistemağa kirgen soñ er vaqıt telükesiz bağlama qullanılsın",
        "underline-always": "Daima",
        "underline-never": "Asla",
        "underline-default": "Brauzer sazlamaları qullanılsın",
        "pool-errorunknown": "Bilinmegen hata",
        "aboutsite": "{{SITENAME}} aqqında",
        "aboutpage": "Project:Aqqında",
-       "copyright": "Malümat $1 binaen keçilip ola.",
+       "copyright": "Başqası bildirilmese, malümat $1 litsenziyasınen berile.",
        "copyrightpage": "{{ns:project}}:Müelliflik aqları",
        "currentevents": "Şimdiki vaqialar",
        "currentevents-url": "Project:Ağımdaki vaqialar",
        "perfcached": "Aşağıdaki malümat keşten alındı ve eskirgen ola bilir! Keşte eñ çoq {{PLURAL:$1|bir netice|$1 netice}} saqlanıp tura.",
        "perfcachedts": "Aşağıdaki malümat keşten alındı, keşniñ soñki yañartılğan vaqtı: $1. Keşte eñ çoq {{PLURAL:$1|bir netice|$1 netice}} saqlanıp tura.",
        "querypage-no-updates": "Bu saifeni deñiştirmege şimdi izin yoq. Bu malümat aman yañartılmaycaq.",
-       "viewsource": "menba kodunı köster",
+       "viewsource": "Menba kodunı köster",
        "viewsource-title": "$1 saifesiniñ menba kodu",
        "actionthrottled": "Areket toqtaldı",
        "actionthrottledtext": "Spamğa qarşı küreş sebebinden bu areketni az vaqıt içinde çoq kere tekrarlap olamaysıñız. Mümkün olğan qarardan ziyade areket yaptıñız. Bir qaç daqqadan soñ tekrarlap baqıñız.",
        "virus-badscanner": "Yañlış sazlama. Bilinmegen virus skaneri: ''$1''",
        "virus-scanfailed": "skan etüv muvafaqiyetsiz (kod $1)",
        "virus-unknownscanner": "bilinmegen antivirus:",
-       "logouttext": "'''Oturımnı qapattıñız.'''\n\nŞimdi {{SITENAME}} saytını anonim olaraq qullanıp olasıñız, ya da yañıdan <span class='plainlinks'>[$1 oturım açıp]</span> olasıñız (ister aynı qullanıcı adınen, ister başqa bir qullanıcı adınen). Web brauzeriñiz keşini temizlegence bazı saifeler sanki alâ daa oturımıñız açıq eken kibi körünip olur.",
+       "logouttext": "<strong>Sistemadan çıqtıñız.</strong>\n\nBrauzeriñiz keşini temizlegence bazı saifeler sistemadan alâ daa çıqmağansıñız kibi körünip olur.",
+       "welcomeuser": "Hoş keldiñiz, $1!",
+       "welcomecreation-msg": "Esap yazıñız yaratıldı.\nİsteseñiz, [[Special:Preferences|{{SITENAME}} sazlamalarıñıznı]] deñiştire bilesiñiz.",
        "yourname": "Qullanıcı adıñız",
+       "userlogin-yourname": "Qullanıncı adı",
+       "userlogin-yourname-ph": "Qullanıcı adıñıznı yazıñız",
+       "createacct-another-username-ph": "Qullanıcı adıñıznı yazıñız",
        "yourpassword": "Paroliñiz",
+       "userlogin-yourpassword": "Parol",
+       "userlogin-yourpassword-ph": "Paroliñizni yazıñız",
+       "createacct-yourpassword-ph": "Parolni yazıñız",
        "yourpasswordagain": "Parolni bir daa yazıñız:",
+       "createacct-yourpasswordagain": "Parolni tasdıqlañız",
+       "createacct-yourpasswordagain-ph": "Parolni bir daa yazıñız",
        "remembermypassword": "Kirişimni bu kompyuterde hatırla (eñ çoq $1 {{PLURAL:$1|kün|kün}} içün)",
+       "userlogin-remembermypassword": "Sistemada qalayım",
+       "userlogin-signwithsecure": "Telükesiz bağlama qullanılsın",
        "yourdomainname": "Domen adıñız",
+       "password-change-forbidden": "Bu vikide paroliñizni deñiştirip olamaysıñız",
        "externaldberror": "Saytqa kirgende bir hata oldı. Bu tış esabıñıznı deñiştirmek aqqıñız olmağanından sebep meydanğa kelip ola.",
        "login": "Kiriş",
        "nav-login-createaccount": "Kiriş / Qayd oluv",
        "userloginnocreate": "Kiriş",
        "logout": "Çıqış",
        "userlogout": "Çıqış",
-       "notloggedin": "Oturım açmadıñız.",
+       "notloggedin": "Sistemağa kirmediñiz.",
+       "userlogin-noaccount": "Akkauntıñız yoqmı?",
+       "userlogin-joinproject": "{{SITENAME}} leyhasına qoşulıñız",
        "nologin": "Daa esap açmadıñızmı? '''$1'''.",
        "nologinlink": "Qayd ol",
-       "createaccount": "Yañı esap aç",
-       "gotaccount": "Daa evel esap açqan ediñizmi? '''$1'''.",
-       "gotaccountlink": "Oturım açıñız",
+       "createaccount": "Qayd oluv",
+       "gotaccount": "Daa evel saytta qayd olğan ediñizmi? '''$1'''.",
+       "gotaccountlink": "Sistemağa kiriñiz",
        "userlogin-resetlink": "Kiriş malümatını unuttıñızmı?",
-       "createaccountmail": "e-mail vastasınen",
+       "createacct-emailrequired": "E-mail adresi",
+       "createacct-emailoptional": "E-mail adresi (mecburiy degil)",
+       "createacct-email-ph": "E-mail adresiñizni yazıñız",
+       "createacct-another-email-ph": "E-mail adresiñizni yazıñız",
+       "createaccountmail": "Avtomatik olaraq meydanğa ketirilgen muvaqqat bir parol qullana bilir ve bu parolni bildirilgen e-mail adresine yollay bilirsiñiz",
+       "createacct-realname": "Aqiqiy adıñız (mecburiy degil)",
        "createaccountreason": "Sebep:",
+       "createacct-reason": "Sebep",
+       "createacct-reason-ph": "Başqa bir esap yazısı neden sebep yaratasıñız",
+       "createacct-captcha": "Telükesizlik kontroli",
+       "createacct-imgcaptcha-ph": "Yuqarıda körgen metniñizni yazıñız",
+       "createacct-submit": "Esap yazıñıznı yaratıñız",
+       "createacct-another-submit": "Başqa bir esap yazısı yaratıñız",
+       "createacct-benefit-heading": "{{SITENAME}} siziñ kibi adamlar tarafından yazıla.",
+       "createacct-benefit-body1": "{{PLURAL:$1|deñiştirme|deñiştirme}}",
+       "createacct-benefit-body2": "{{PLURAL:$1|saife|saife}}",
+       "createacct-benefit-body3": "soñki vaqıtlarda {{PLURAL:$1|issesini qoşqan qullanıcı|issesini qoşqan qullanıcı}}",
        "badretype": "Kirsetken parolleriñiz aynı degil.",
        "userexists": "Kirsetken qullanıcı adıñız endi qullanıla.\nLütfen, başqa bir qullanıcı adı saylañız.",
        "loginerror": "Oturım açma hatası",
+       "createacct-error": "Esap yazısını yaratuv hatası",
        "createaccounterror": "Esap yaratılıp olamay: $1",
        "nocookiesnew": "Qullanıcı esabı açılğan, faqat tanıtılmağan. {{SITENAME}} qullanıcılarnı tanıtmaq içün \"cookies\"ni qullana. Sizde bu funktsiya qapalı vaziyettedir. \"Cookies\" funktsiyasını işletip tekrar yañı adıñız ve paroliñiznen tırışıp baqınız.",
        "nocookieslogin": "{{SITENAME}} \"cookies\"ni qullana. Sizde bu funktsiya qapalı vaziyettedir. \"Cookies\" funktsiyasını işletip tekrar tırışıp baqıñız.",
        "passwordtooshort": "Paroliñizde eñ az {{PLURAL:$1|1|$1}} işaret olmalı.",
        "password-name-match": "Paroliñiz qullanıcı adıñızdan farqlı olmalı.",
        "password-login-forbidden": "Bu qullanıcı adı ve parolni qullanmaq yasaqtır.",
-       "mailmypassword": "Yañı parol yiber",
+       "mailmypassword": "Parolni sıfırla",
        "passwordremindertitle": "{{grammar:genitive|{{SITENAME}}}} qullanıcınıñ parol hatırlatuvı",
        "passwordremindertext": "Birev (belki de bu sizsiñiz, $1 IP adresinden) {{SITENAME}} saytı içün ($4) yañı qullanıcı parolini istedi.\n$2 qullanıcısına vaqtınca <code>$3</code> paroli yaratıldı. Eger bu kerçekten de siziñ istegiñiz olğan olsa, oturım açıp yañı bir parol yaratmañız kerektir. Muvaqqat paroliñizniñ müddeti {{PLURAL:$5|1 kün|$5 kün}} içinde dolacaq.\n\nEger de yañı parol talap etmegen olsañız ya da eski paroliñizni hatırlap endi onı deñiştirmege istemeseñiz, bu mektüpni diqqatqa almayıp eski paroliñizni qullanmağa devam etip olasıñız.",
        "noemail": "$1 adlı qullanıcı içün e-mail bildirilmedi.",
        "login-throttled": "Yaqın zamanda pek çoq kere kirmege tırıştıñız.\nLütfen, qayta kirmezden evel biraz bekleñiz.",
        "loginlanguagelabel": "Til: $1",
        "suspicious-userlogout": "Çıqış istegeniñiz red etildi, çünki bozuq bir brauzer ya da keşleyici proksi tarafından yollanğan kibi körüne.",
+       "pt-login": "Kiriş",
+       "pt-login-button": "Kiriş",
+       "pt-createaccount": "Qayd oluv",
+       "pt-userlogout": "Çıqış",
        "changepassword": "Parol deñiştir",
        "resetpass_announce": "Muvaqqat kod vastasınen kirdiñiz. Kirişni tamamlamaq içün yañı parolni mında qoyuñız:",
        "resetpass_header": "Esapnıñ parolini deñiştir",
        "session_fail_preview": "''' Server siz yapqan deñiştirmelerni sessiya identifikatorı\ncoyulğanı sebebinden saqlap olamadı. Bu vaqtınca problemadır. Lütfen, tekrar saqlap baqıñız.\nBundan da soñ olıp çıqmasa, malümat lokal faylğa saqlañız da brauzeriñizni bir qapatıp\naçıñız.'''",
        "session_fail_preview_html": "'''Afu etiñiz! HTML sessiyanıñ malümatları ğayıp olğanı sebebinden siziñ deñiştirmeleriñizni qabul etmege imkân yoqtır.'''",
        "token_suffix_mismatch": "'''Siziñ programmañıznıñ öz türlendirüv penceresinde punktuatsiya işaretlerini doğru işlemegeni içün yapqan deñiştirmeleriñiz qabul olunmadı. Deñiştirmeler saife metniniñ körünişi bozulmasın dep lâğu etildi.\nBunıñ kibi problemalar hatalı anonim web-proksiler qullanuvdan çıqıp ola.'''",
-       "editing": "\"$1\" saifesini deñiştireyatasız",
+       "editing": "“$1” saifesini deñiştireyatasız",
+       "creating": "“$1” saifesini yaratuv",
        "editingsection": "\"$1\" saifesinde bölük deñiştireyatasız",
        "editingcomment": "$1 saifesini deñiştireyatasız (yañı bölük)",
        "editconflict": "Deñiştirmeler çatışması: $1",
        "edit-gone-missing": "Saife yañartılıp olamay.\nBelki o yoq etilgendir.",
        "edit-conflict": "Deñiştirmeler çatışması.",
        "edit-no-change": "Yapqan deñiştirmeñiz saqlanmağan, çünki metinde bir türlü deñiştirilme yapılmadı.",
+       "postedit-confirmation-created": "Saife yaratıldı.",
+       "postedit-confirmation-saved": "Yapqan deñiştirmeñiz saqlandı.",
        "edit-already-exists": "Yañı saifeni yaratmaq mümkün degil.\nO endi bar.",
        "undo-success": "Deñiştirme lâğu etile bile. Lütfen, mına bu deñiştirmelerni yapmağa istegeniñizden emin olmaq içün versiyalar teñeştirilüvini közden keçirip deñiştirmelerni saqlamaq içün \"Saifeni saqla\" dögmesine basıñız.",
        "undo-failure": "Aradaki deñiştirmeler bir-birine kelişikli olmağanı içün deñiştirme lâğu etilip olamay.",
        "viewpagelogs": "Bu saifeniñ jurnallarını köster",
        "nohistory": "Bu saifeniñ keçmiş versiyası yoq.",
        "currentrev": "Şimdiki versiya",
-       "currentrev-asof": "$1 tarihında soñki kere deñiştirilgen saifeniñ şimdiki alı",
+       "currentrev-asof": "$1 tarihından başlap saifeniñ şimdiki alı",
        "revisionasof": "Saifeniñ $1 tarihındaki alı",
        "revision-info": "Saifeniñ $2 tarafından yazılğan $1 tarihındaki alı",
        "previousrevision": "← Evelki alı",
        "preferences": "Sazlamalar",
        "mypreferences": "Sazlamalar",
        "prefs-edits": "Deñiştirmeler sayısı:",
+       "prefsnologintext2": "Sazlamalarıñıznı deñiştirmek içün lütfen sistemağa kiriñiz.",
        "prefs-skin": "Resimleme",
        "skin-preview": "Baqıp çıquv",
        "datedefault": "Standart",
        "recentchanges-label-minor": "Bu, kiçik bir deñiştirme",
        "recentchanges-label-bot": "Bu bir botnıñ yapqan deñiştirmesi",
        "recentchanges-label-unpatrolled": "Bu deñiştirme alâ daa teşkerilmegen",
+       "recentchanges-label-plusminus": "Bayt esabınen saife büyükliginiñ deñiştirilmesi",
+       "recentchanges-legend-heading": "'''İşaretler:'''",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} ([[Special:NewPages|yañı saifeler cedveline]] de baqıñız)",
-       "rcnotefrom": "'''$2''' tarihından itibaren yapılğan deñiştirmeler aşağıdadır (eñ çоq '''$1''' dane saife kösterile).",
+       "rcnotefrom": "<strong>$3, $4</strong> tarihından başlap yapılğan {{PLURAL:$5|deñiştirme|deñiştirmeler}} aşağıdadır (eñ çоq <strong>$1</strong> dane saife kösterile).",
        "rclistfrom": "$3 $2 tarihından berli yapılğan deñiştirmelerni köster",
        "rcshowhideminor": "kiçik deñiştirmelerni $1",
        "rcshowhideminor-show": "köster",
        "rcshowhidebots": "botlarnı $1",
        "rcshowhidebots-show": "köster",
        "rcshowhidebots-hide": "gizle",
-       "rcshowhideliu": "Qaydlı qullanıcılarnı $1",
+       "rcshowhideliu": "qaydlı qullanıcılarnı $1",
        "rcshowhideliu-show": "köster",
        "rcshowhideliu-hide": "gizle",
        "rcshowhideanons": "anonim qullanıcılarnı $1",
        "number_of_watching_users_pageview": "[$1 {{PLURAL:$1|qullanıcı|qullanıcı}} közete]",
        "rc_categories": "Tek kategoriyalardan (\"|\" ile ayırıla)",
        "rc_categories_any": "Er angi",
+       "rc-change-size-new": "Deñiştirilgen soñ $1 {{PLURAL:$1|bayt|bayt}}",
        "newsectionsummary": "/* $1 */ yañı bölük",
        "rc-enhanced-expand": "Tafsilâtını köster",
        "rc-enhanced-hide": "Tafsilâtını gizle",
        "reuploaddesc": "Yükleme formasına keri qayt.",
        "upload-tryagain": "Deñiştirilgen fayl tarifini yolla",
        "uploadnologin": "Oturım açmadıñız",
-       "uploadnologintext": "Fayl yüklep olmaq içün [[Special:UserLogin|oturım açmaq]] kereksiñiz.",
+       "uploadnologintext": "Fayl yüklep olmaq içün lütfen $1.",
        "upload_directory_missing": "Yüklemeler içün direktoriya ($1) mevcut degil ve veb-server tarafından yapılıp olamay.",
        "upload_directory_read_only": "Web serverniñ ($1) cüzdanına fayllar saqlamağa aqları yoqtır.",
        "uploaderror": "Yükleme hatası",
        "statistics-header-hooks": "Diger statistika",
        "doubleredirects": "Yollamağa olğan yollamalar",
        "doubleredirectstext": "Bu saifede diger yollama saifelerine yollanma olğan saifeleri kösterile.\nEr satırda birinci ve ekinci yollamağa bağlantılar da, ekinci yollamanıñ maqsat saifesi (adetince o birinci yollamanıñ kerekli maqsadı ola) da bar.\n<del>Üstü sızılğan</del> meseleler endi çezilgen.",
-       "double-redirect-fixed-move": "[[$1]] avuştırıldı, şimdi [[$2]] saifesine yollap tura.",
+       "double-redirect-fixed-move": "[[$1]] avuştırıldı. O, avtomatik olaraq yañartılıp şimdi [[$2]] saifesine yönetip tura.",
        "brokenredirects": "Bar olmağan saifege yapılğan yollamalar",
        "brokenredirectstext": "Aşağıdaki yollamalar bar olmağan saifelerge bağlantı bereler:",
        "brokenredirects-edit": "deñiştir",
        "pager-older-n": "{{PLURAL:$1|daa eski 1|daa eski $1}}",
        "booksources": "Kitaplar menbası",
        "booksources-search-legend": "Kitaplar menbasını qıdıruv",
+       "booksources-search": "Qıdır",
        "specialloguserlabel": "Qullanıcı:",
        "speciallogtitlelabel": "Serleva:",
        "log": "Jurnallar",
        "emailsenttext": "Siziñ e-mail beyanatıñız yollandı",
        "emailuserfooter": "Bu mektüp $1 tarafından $2 qullanıcısına, {{SITENAME}} saytındaki \"Qullanıcığa e-mail yolla\" funktsiyasınen yollanğan.",
        "watchlist": "Közetüv cedveli",
-       "mywatchlist": "Közetüv cedvelim",
+       "mywatchlist": "Közetüv cedveli",
+       "watchlistfor2": "$1 içün $2",
        "nowatchlist": "Siziñ közetüv cedveliñiz boştır.",
        "watchlistanontext": "Közetüv cedvelini baqmaq ya da deñiştirmek içün $1 borclusıñız.",
        "watchnologin": "Oturım açmaq kerek",
        "unwatching": "Közetüv cedvelinden yoq etilmekte...",
        "enotif_reset": "Cümle saifelerni baqılğan olaraq işaretle",
        "enotif_impersonal_salutation": "{{SITENAME}} qullanıcısı",
+       "enotif_subject_moved": "{{SITENAME}} leyhasınıñ $1 adlı saifesiniñ adı, $2 tarafından {{GENDER:$2|deñiştirildi}}.",
+       "enotif_body_intro_moved": "{{SITENAME}} leyhasınıñ $1 adlı saifesiniñ adı $PAGEEDITDATE tarihında $2 tarafından {{GENDER:$2|deñiştirildi}}. Şimdiki alını mında köre bilesiñiz: $3.",
        "enotif_lastvisited": "Soñki ziyaretiñizden berli yapılğan deñiştirmelerni körmek içün $1 baqıñız.",
        "enotif_anon_editor": "adsız (anonim) qullanıcı $1",
        "enotif_body": "Sayğılı $WATCHINGUSERNAME,\n\n$PAGEINTRO $NEWPAGE\n\nDeñiştirmeniñ qısqa tarifi: $PAGESUMMARY $PAGEMINOREDIT\n\nSaifeni deñiştirgen qullanıcınen bağlanmaq içün:\ne-mail adresi: $PAGEEDITOR_EMAIL\nviki saifesi: $PAGEEDITOR_WIKI\n\nBu saifeni ziyaret etmeseñiz, birev onı bir daa deñiştirse de, iç bir tenbi beyanatı yollanmaycaq. Közetüv cedveliñizdeki bütün saifeler içün tenbi sazlamalarını deñiştire bilesiñiz.\n\n{{SITENAME}} bildirüv sisteması\n\n--\n\nBildirüv sazlamalarını deñiştirmek içün:\n{{canonicalurl:{{#special:Preferences}}}}\n\nKözetüv cedveli sazlamalarını deñiştirmek içün:\n{{canonicalurl:{{#special:EditWatchlist}}}}\n\nSaifeni közetüv cedvelinden çıqarmaq içün:\n$UNWATCHURL\n\nYardım ve teklifler içün:\n$HELPPAGE",
        "protectedarticle": "\"[[$1]]\" qorçalav altına alındı",
        "modifiedarticleprotection": "\"[[$1]]\" içün qorçalav seviyesi deñiştirildi",
        "unprotectedarticle": "\"[[$1]]\" saifesinden qorçalav çıqarlıdı",
-       "prot_1movedto2": "\"[[$1]]\" saifesiniñ adı \"[[$2]]\" olaraq deñiştirildi",
+       "prot_1movedto2": "[[$1]] saifesiniñ adı [[$2]] dep deñiştirildi",
        "protect-legend": "Qorçalavnı tasdıqla",
        "protectcomment": "Sebep:",
        "protectexpiry": "Bitiş tarihı:",
        "undeletecomment": "Sebep:",
        "undeletedrevisions": "Toplam {{PLURAL:$1|1 qayd|$1 qayd}} keri ketirildi.",
        "undelete-header": "Keçenlerde yoq etilgen saifelerni körmek içün [[Special:Log/delete|yoq etüv jurnalına]] baqıñız.",
+       "undelete-search-submit": "Qıdır",
        "namespace": "İsim fezası:",
        "invert": "Saylanğan tışındakilerni sayla",
+       "namespace_association": "Bağlı isim fezası",
        "blanknamespace": "(Esas)",
        "contributions": "{{GENDER:$1|Qullanıcınıñ}} isseleri",
        "contributions-title": "$1 qullanıcısınıñ isseleri",
        "sp-contributions-userrights": "qullanıcı aqlarını idare etüv",
        "sp-contributions-search": "İsselerni qıdıruv",
        "sp-contributions-username": "IP adresi ya da qullanıcı adı:",
+       "sp-contributions-toponly": "Tek soñki versiyası olğan deñiştirmelerni köster",
+       "sp-contributions-newonly": "Tek yañı saife yaratqan deñiştirmelerni köster",
        "sp-contributions-submit": "Qıdır",
        "whatlinkshere": "Bu saifege bağlantılar",
        "whatlinkshere-title": "$1 saifesine bağlantı bergen saifeler",
        "unblockip": "Qullanıcınıñ blok etmesini çıqar",
        "ipusubmit": "Bu blok etmeni çıqar",
        "ipblocklist": "Blok etilgen qullanıcılar ve IP adresleri",
+       "ipblocklist-submit": "Qıdır",
        "infiniteblock": "müddetsiz",
        "expiringblock": "$1 $2 tarihında bitecek",
        "blocklink": "blok et",
        "lockbtn": "Malümat bazası kilitli",
        "move-page": "$1 saifesiniñ adını deñiştireyatasız",
        "move-page-legend": "Saifeniñ adını deñiştirüv",
-       "movepagetext": "Aşağıdaki forma qullanılıp saifeniñ adı deñiştirilir. Bunıñnen beraber deñiştirmeler jurnalı da yañı adğa avuştırılır.\nEski adı yañı adına yollama olur. Eski serlevağa yollama saifelerni avtomatik olaraq yañartıp olasıñız. Bu areketni avtomatik yapmağa istemeseñiz, bütün [[Special:DoubleRedirects|çift]] ve [[Special:BrokenRedirects|yırtıq]] yollama saifelerini özüñiz tüzetmege mecbur olursıñız. Bağlantılar endiden berli doğru çalışmasından emin olmalısıñız.\n\nYañı adda bir saife endi bar olsa, ad deñiştirilüvi '''yapılmaycaq''', ancaq bar olğan saife yollama ya da boş olsa ad deñiştirilüvi mümkün olacaq. Bu demek ki, saifeniñ adını yañlıştan deñiştirgen olsañız deminki adını keri qaytarıp olasıñız, amma bar olğan saifeni tesadüfen yoq etamaysıñız.\n\n'''TENBİ!'''\nAd deñiştirilüvi populâr saifeler içün büyük ve beklenmegen deñişmelerge sebep ola bilir. Lütfen, deñiştirme yapmazdan evel ola bileceklerni köz ögüne alıñız.",
+       "movepagetext": "Aşağıdaki forma qullanılıp saifeniñ adı deñiştirilir. Bunıñnen beraber deñiştirmeler jurnalı da yañı adğa avuştırılır.\nEski adı yañı adına yönetme olur. Eski serlevağa yönetip turğan saifelerni avtomatik olaraq yañartıp olasıñız. Bu areketni avtomatik yapmağa istemeseñiz, bütün [[Special:DoubleRedirects|çift]] ve [[Special:BrokenRedirects|yırtıq]] yönetme saifelerini özüñiz teşkermege mecbur olursıñız. Bağlantılar endiden berli doğru çalışmasından emin olmalısıñız.\n\nYañı adda bir saife endi bar olsa, ad deñiştirilüvi <strong>yapılmaycaq</strong>, ancaq bar olğan saife yönetme ya da boş olsa ad deñiştirilüvi mümkün olacaq. Bu demek ki, saifeniñ adını yañlıştan deñiştirgen olsañız deminki adını keri qaytarıp olasıñız, amma bar olğan saifeni tesadüfen yoq etamaysıñız.\n\n<strong>TENBİ!</strong>\nAd deñiştirilüvi populâr saifeler içün büyük ve beklenmegen deñişmelerge sebep ola bilir. Lütfen, deñiştirme yapmazdan evel ola bileceklerni köz ögüne alıñız.",
        "movepagetalktext": "Qoşulğan muzakere saifesiniñ de (bar olsa) adı avtomatik tarzda deñiştirilecek. '''Müstesnalar:'''\n\n*Aynı bu isimde boş olmağan bir muzakere saifesi endi bar;\n*Aşağıdaki boşluqqa işaret qoymadıñız.\n\nBöyle allarda, kerek olsa, saifelerni qolnen taşımağa ya da birleştirmege mecbur olursıñız.",
        "movearticle": "Eski ad",
+       "movecategorypage-warning": "<strong>İhtar:</strong> Bir kategoriya saifesiniñ adını deñiştirmek üzresiñiz. Lütfen, yalıñız kategoriya saifesiniñ köçürilecegini ve eski kategoriyada yer alğan saifelerniñ yañı kategoriyağa avotmatik olaraq <em>avuştırılmaycağını</em> unutmañız.",
        "movenologintext": "Saifeniñ adını deñiştirip olmaq içün [[Special:UserLogin|oturım açıñız]].",
        "movenotallowed": "Saifeler adlarını deñiştirmege iziniñiz yoq.",
        "newtitle": "Yañı ad",
        "move-subpages": "Alt saifelerniñ adlarını da deñiştir ($1 saifege qadar)",
        "move-talk-subpages": "Muzakere saifesi alt saifeleriniñ adlarını da deñiştir ($1 saifege qadar)",
        "movepage-page-exists": "$1 saifesi endi bar, ve avtomatik olaraq yañıdan yazılıp olamaz.",
-       "movepage-page-moved": "$1 saifesiniñ adı $2 olaraq deñiştirildi.",
+       "movepage-page-moved": "$1 saifesiniñ adı $2 dep deñiştirildi.",
        "movepage-page-unmoved": "$1 saifesiniñ adı $2 olaraq deñiştirilip olamay.",
        "movelogpage": "Ad deñiştirilmeleri jurnalı",
        "movelogpagetext": "Aşağıda bulunğan cedvel adı deñiştirilgen saifelerni köstere",
        "allmessagesdefault": "Original metin",
        "allmessagescurrent": "Şimdi qullanılğan metin",
        "allmessagestext": "İşbu cedvel MediaWikide mevcut olğan bütün sistema beyanatlarınıñ cedvelidir.\nMediaWiki interfeysiniñ çeşit tillerge tercime etüvde iştirak etmege isteseñiz [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation MediaWiki Localisation] ve [//translatewiki.net translatewiki.net] saifelerine ziyaret etiñiz.",
+       "allmessages-filter-legend": "Süzgüç",
+       "allmessages-language": "Til:",
        "thumbnail-more": "Büyüt",
        "filemissing": "Fayl tapılmadı",
        "thumbnail_error": "Kiçik resim (thumbnail) yaratılğanda bir hata çıqtı: $1",
        "tooltip-pt-watchlist": "Közetüvge alğan saifeleriñiz",
        "tooltip-pt-mycontris": "Qoşqan isseleriñizniñ cedveli",
        "tooltip-pt-login": "Oturım açmañız tevsiye olunır amma mecbur degilsiñiz.",
-       "tooltip-pt-logout": "Sistemadan çıquv",
+       "tooltip-pt-logout": "Çıqış",
        "tooltip-ca-talk": "Saifedeki malümatnen bağlı muzakere",
        "tooltip-ca-edit": "Bu saifeni deñiştirip olasıñız. Saqlamazdan evel baqıp çıqmağa unutmañız.",
        "tooltip-ca-addsection": "Yañı bölükni açuv",
        "spambot_username": "Spamdan temizlev",
        "spam_reverting": "$1 saytına bağlantısı olmağan soñki versiyağa keri ketirüv",
        "spam_blanking": "Bar olğan versiyalarda $1 saytına bağlantılar bar, temizlev",
+       "pageinfo-language": "Saife içindekisiniñ tili",
        "patrol-log-page": "Teşkerüv jurnalı",
        "log-show-hide-patrol": "Teşkerüv jurnalını $1",
        "deletedrevision": "$1 sayılı eski versiya yoq etildi.",
        "exif-gpsaltitude": "Yükseklik",
        "exif-gpstimestamp": "GPS saatı (atom saatı)",
        "exif-gpssatellites": "Ölçemek içün qullanğanı sputnikler",
+       "exif-languagecode": "Til",
        "exif-compression-1": "Sıqıştırılmağan",
        "exif-orientation-3": "180° aylandırılğan",
        "exif-exposureprogram-1": "Elnen",
        "specialpages": "Mahsus saifeler",
        "specialpages-group-maintenance": "Baqım esabatları",
        "specialpages-group-other": "Diger mahsus saifeler",
-       "specialpages-group-login": "Kiriş / Qayd oluv",
+       "specialpages-group-login": "Kiriş / qayd oluv",
        "specialpages-group-changes": "Soñki deñişiklikler ve jurnallar",
        "specialpages-group-media": "Fayl esabatları ve yükleme",
        "specialpages-group-users": "Qullanıcılar ve aqları",
        "specialpages-group-spam": "Spamğa qarşı aletler",
        "blankpage": "Boş saife",
        "intentionallyblankpage": "Bu saife aselet boş qaldırılğan",
+       "tag-filter": "[[Special:Tags|Belgi]] süzgüçi:",
+       "tag-filter-submit": "Süz",
+       "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Belgi|Belgiler}}]]: $2)",
+       "tags-title": "Belgiler",
        "comparepages": "Saifelerni teñeştirüv",
        "compare-submit": "Teñeştir",
        "htmlform-reset": "Deñişikliklerni keri al",
-       "searchsuggest-containing": "içinde bu olğan..."
+       "logentry-move-move": "$1 adlı qulanıcı $3 saifesiniñ adını $4 dep {{GENDER:$2|deñiştirildi}}.",
+       "logentry-move-move-noredirect": "$1 adlı qullanıcı $3 saifesiniñ adını yönetme qaldırmayıp $4 dep {{GENDER:$2|deñiştirdi}}",
+       "logentry-move-move_redir": "$1, $3 saifesiniñ adını yönetme üzerinden $4 dep {{GENDER:$2|deñiştirdi}}",
+       "logentry-move-move_redir-noredirect": "$1 adlı qullanıcı $3 saifesiniñ adını yönetme üzerinden yañı bir yönetme qaldırmayıp $4 dep {{GENDER:$2|deñiştirdi}}",
+       "searchsuggest-search": "Qıdır",
+       "searchsuggest-containing": "içinde bu olğan...",
+       "pagelang-language": "Til"
 }
index fdde405..22ae45c 100644 (file)
        "editundo": "rückgängig machen",
        "diff-empty": "(kein Unterschied)",
        "diff-multi-sameuser": "({{PLURAL:$1|Eine dazwischenliegende Version desselben Benutzers wird|$1 dazwischenliegende Versionen desselben Benutzers werden}} nicht angezeigt)",
-       "diff-multi-otherusers": "({{PLURAL:$1|Eine dazwischenliegende Version|$1 dazwischenliegende Versionen}} von {{PLURAL:$2|einem anderen Benutzer|$2 Benutzern}} werden nicht angezeigt)",
+       "diff-multi-otherusers": "({{PLURAL:$1|Eine dazwischenliegende Version|$1 dazwischenliegende Versionen}} von {{PLURAL:$2|einem anderen Benutzer|$2 Benutzern}} {{PLURAL:$1|wird|werden}} nicht angezeigt)",
        "diff-multi-manyusers": "({{PLURAL:$1|$1 dazwischenliegende Versionen}} von mehr als {{PLURAL:$2|$2 Benutzern}}, die nicht angezeigt werden)",
        "difference-missing-revision": "{{PLURAL:$2|Eine Version|$2 Versionen}} dieser Unterschiedsanzeige ($1) {{PLURAL:$2|wurde|wurden}} nicht gefunden.\n\nDieser Fehler wird normalerweise von einem veralteten Link zur Versionsgeschichte einer Seite verursacht, die zwischenzeitlich gelöscht wurde.\nEinzelheiten sind im [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} Lösch-Logbuch] vorhanden.",
        "searchresults": "Suchergebnisse",
        "ipboptions": "2 Stunden:2 hours,1 Tag:1 day,3 Tage:3 days,1 Woche:1 week,2 Wochen:2 weeks,1 Monat:1 month,3 Monate:3 months,6 Monate:6 months,1 Jahr:1 year,unbeschränkt:infinite",
        "ipbhidename": "Benutzername in Bearbeitungen und Listen verstecken",
        "ipbwatchuser": "Benutzer(diskussions)seite beobachten",
-       "ipb-disableusertalk": "Diesen Benutzer daran hindern seine eigene Diskussionsseite zu bearbeiten, solange er gesperrt ist",
+       "ipb-disableusertalk": "Diesen Benutzer daran hindern, seine eigene Diskussionsseite zu bearbeiten, solange er gesperrt ist",
        "ipb-change-block": "Sperre mit diesen Sperrparametern erneuern",
        "ipb-confirm": "Sperrung bestätigen",
        "badipaddress": "Die IP-Adresse hat ein falsches Format.",
index 5d4a2bf..b607abb 100644 (file)
@@ -83,7 +83,7 @@
        "tog-prefershttps": "Να γίνεται πάντα χρήση ασφαλούς σύνδεσης όταν ο χρήστης είναι συνδεδεμένος",
        "underline-always": "Πάντα",
        "underline-never": "Ποτέ",
-       "underline-default": "Προεπιλογή από το skin ή από τον περιηγητή",
+       "underline-default": "Προεπιλογή από το θέμα ή από τον περιηγητή",
        "editfont-style": "Στυλ γραμματοσειράς της περιοχής επεξεργασίας:",
        "editfont-default": "Προεπιλογή περιηγητή",
        "editfont-monospace": "Γραμματοσειρά με σταθερό πλάτος χαρακτήρων",
        "filerenameerror": "Δεν είναι δυνατή η μετονομασία του αρχείου «$1» σε «$2».",
        "filedeleteerror": "Δεν ήταν δυνατή η διαγραφή του αρχείου «$1».",
        "directorycreateerror": "Δεν μπορούσε να δημιουργηθεί η κατηγορία «$1».",
+       "directoryreadonlyerror": "Ο κατάλογος «$1» είναι μόνο για ανάγνωση.",
+       "directorynotreadableerror": "Ο κατάλογος «$1» δεν είναι αναγνώσιμος.",
        "filenotfound": "Δεν είναι δυνατή η ανεύρεση του αρχείου «$1».",
        "unexpected": "Μη προσδοκώμενη τιμή: «$1»=«$2».",
        "formerror": "Σφάλμα: Δεν ήταν δυνατή η υποβολή της φόρμας!",
        "duplicate-args-category": "Σελίδες που χρησιμοποιούν διπλές παραμέτρους σε κλήσεις προτύπων",
        "duplicate-args-category-desc": "Η σελίδα περιέχει κλήσεις πρότυπων που χρησιμοποιούν διπλές παραμέτρους, όπως <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> or <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "expensive-parserfunction-warning": "Προειδοποίηση: Αυτή η σελίδα περιέχει πάρα πολύ ακριβό αναλυτή λειτουργικών κλήσεων.\n\nΠρέπει να περιέχει λιγότερες από $2 {{PLURAL:$2|κλήση|κλήσεις}}, τώρα {{PLURAL:$1|υπάρχει $1 κλήση|υπάρχουν $1 κλήσεις}}.",
-       "expensive-parserfunction-category": "ΣελίδεÏ\82 Î¼Îµ Ï\80άÏ\81α Ï\80ολλέÏ\82 Î±ÎºÏ\81ιβέÏ\82 Î»ÎµÎ¾Î¹Î±Î½Î±Î»Ï\85Ï\84ικέÏ\82 Î»ÎµÎ¹Ï\84οÏ\85Ï\81γικέÏ\82 ÎºÎ»ήσεις",
+       "expensive-parserfunction-category": "ΣελίδεÏ\82 Î¼Îµ Ï\80άÏ\81α Ï\80ολλέÏ\82 Î±ÎºÏ\81ιβέÏ\82 ÎºÎ»Î®Ï\83ειÏ\82 Ï\83ε Î»ÎµÎ¾Î¹Î±Î½Î±Î»Ï\85Ï\84ικέÏ\82 Ï\83Ï\85ναÏ\81Ï\84ήσεις",
        "post-expand-template-inclusion-warning": "'''Προειδοποίηση:''' Το μέγεθος συμπερίληψης προτύπων είναι πολύ μεγάλο.\nΚάποια πρότυπα δεν θα συμπεριληφθούν.",
-       "post-expand-template-inclusion-category": "Σελίδες όπου ο υπερβαίνεται το όριο μεγέθους συμπερίληψης προτύπων",
+       "post-expand-template-inclusion-category": "Σελίδες όπου υπερβαίνεται το όριο μεγέθους συμπερίληψης προτύπων",
        "post-expand-template-argument-warning": "'''Προειδοποίηση:''' Αυτή η σελίδα περιέχει τουλάχιστον μια παράμετρο προτύπου η οποία έχει πολύ μεγάλο μέγεθος ανάπτυξης.\nΑυτές οι παράμετροι έχουν παραλειφθεί.",
        "post-expand-template-argument-category": "Σελίδες που περιέχουν παραλειπόμενες παραμέτρους προτύπων",
        "parser-template-loop-warning": "Εντοπίστηκε πρότυπο σε βρόχο: [[$1]]",
        "mypreferences": "Προτιμήσεις",
        "prefs-edits": "Αριθμός επεξεργασιών:",
        "prefsnologintext2": "Παρακαλώ συνδεθείτε για να αλλάξετε τις προτιμήσεις σας.",
-       "prefs-skin": "Î\9fÏ\80Ï\84ική Î¿Ï\81γάνÏ\89Ï\83η (skin)",
+       "prefs-skin": "Î\98έμα ÎµÎ¼Ï\86άνιÏ\83ηÏ\82",
        "skin-preview": "Προεπισκόπηση",
        "datedefault": "Χωρίς προτίμηση",
        "prefs-labs": "Λειτουργίες των Labs",
        "prefs-files": "Αρχεία",
        "prefs-custom-css": "Προκαθορισμένη CSS",
        "prefs-custom-js": "Προκαθορισμένη JS",
-       "prefs-common-css-js": "Κοινά CSS/JS για όλα τα skins:",
+       "prefs-common-css-js": "Κοινά CSS/JS για όλα τα θέματα:",
        "prefs-reset-intro": "Μπορείτε να χρησιμοποιήσετε αυτήν την σελίδα για να επαναρρυθμίσετε τις προτιμήσεις σας στις προεπιλογές του ιστότοπου. Αυτό δεν μπορεί να αναστρεφθεί.",
        "prefs-emailconfirm-label": "Επιβεβαίωση e-mail:",
        "youremail": "Διεύθυνση ηλεκτρονικού ταχυδρομείου:",
        "right-move-categorypages": "Μετακίνηση σελίδων κατηγοριών",
        "right-movefile": "Μετακίνηση αρχείων",
        "right-suppressredirect": "Μη δημιουργία ανακατεύθυνσης από το παλιό όνομα κατά τη μετακίνηση μιας σελίδας",
-       "right-upload": "Î\95Ï\80ιÏ\86Ï\8cÏ\81Ï\84Ï\89Ï\83η αρχείων",
+       "right-upload": "Î\91νέβαÏ\83μα αρχείων",
        "right-reupload": "Αντικατάσταση ενός ήδη υπάρχοντος αρχείου",
        "right-reupload-own": "Αντικατάσταση ενός ήδη υπάρχοντος αρχείου που έχει ανέβει από κάποιον",
        "right-reupload-shared": "Τοπική υπερκάλυψη αρχείων στο κοινό αποθηκευτήριο πολυμέσων",
        "uploaddisabledtext": "Το ανέβασμα αρχείων είναι απενεργοποιημένο.",
        "php-uploaddisabledtext": "Οι επιφορτώσεις αρχείων ειναι απενεργοποιημένες στην PHP. Παρακαλούμε, ελέγξτε την ρύθμιση file_uploads.",
        "uploadscripted": "Αυτό το αρχείο περιέχει κώδικα HTML ή script που μπορεί να παρερμηνευθεί από μερικούς browser.",
+       "uploadscriptednamespace": "Αυτό το αρχείο SVG περιέχει έναν μη αποδεκτό ονοματοχώρο \"$1\".",
        "uploadinvalidxml": "Δεν ήταν δυνατή η ανάλυση του κώδικα XML στο αρχείο.",
        "uploadvirus": "Το αρχείο περιέχει ιό! Λεπτομέρειες: $1",
        "uploadjava": "Το αρχείο είναι αρχείο ZIP, το οποίο περιέχει ένα αρχείο .class της γλώσσας Java.\nΔεν επιτρέπεται η αποστολή αρχείων Java, επειδή μπορούν να προκαλέσουν παράκαμψη των περιορισμών ασφαλείας του συστήματος.",
        "license": "Αδειοδότηση:",
        "license-header": "Αδειοδότηση",
        "nolicense": "Καμία επιλεγμένη",
+       "licenses-edit": "Επιλογές επεξεργασίας άδειας",
        "license-nopreview": "(Μη διαθέσιμη προεπισκόπηση)",
        "upload_source_url": "(το επιλεγμένο σας αρχείο από μια έγκυρη, δημόσια προσβάσιμη διεύθυνση URL)",
        "upload_source_file": "(το επιλεγμένο αρχείο από τον υπολογιστή σας)",
        "filedelete-maintenance": "Η διαγραφή κι η επαναφορά αρχείων είναι προσωρινά αδύνατη λόγω συντήρησης.",
        "filedelete-maintenance-title": "Αδύνατη η διαγραφή αρχείου",
        "mimesearch": "Αναζήτηση MIME",
-       "mimesearch-summary": "Αυτή η σελίδα ενεργοποιεί το φιλτράρισμα αρχείων σύμφωνα με τον τύπο MIME τους. Είσοδος: contenttype/subtype, π.χ. <code>image/jpeg</code>.",
+       "mimesearch-summary": "Αυτή η σελίδα ενεργοποιεί το φιλτράρισμα αρχείων σύμφωνα με τον τύπο MIME τους. Είσοδος: contenttype/subtype ή contenttype/*, π.χ. <code>image/jpeg</code>.",
        "mimetype": "Τύπος MIME:",
        "download": "λήψη",
        "unwatchedpages": "Μη παρακολουθούμενες σελίδες",
        "wantedtemplates": "Ζητούμενα πρότυπα",
        "mostlinked": "Σελίδες με τους περισσότερους συνδέσμους προς αυτές",
        "mostlinkedcategories": "Περισσότερο χρησιμοποιούμενες κατηγορίες",
-       "mostlinkedtemplates": "ΠεÏ\81ιÏ\83Ï\83Ï\8cÏ\84εÏ\81ο Ï\87Ï\81ηÏ\83ιμοÏ\80οιοÏ\8dμενα Ï\80Ï\81Ï\8cÏ\84Ï\85Ï\80α",
+       "mostlinkedtemplates": "ΣελίδεÏ\82 Ï\80οÏ\85 Î­Ï\87οÏ\85ν ÎµÎ½Ï\83Ï\89μαÏ\84Ï\89θεί Ï\80εÏ\81ιÏ\83Ï\83Ï\8cÏ\84εÏ\81ο Î±Ï\80Ï\8c Ï\8cλεÏ\82 Ï\84ιÏ\82 Î¬Î»Î»ÎµÏ\82",
        "mostcategories": "Σελίδες με τις περισσότερες κατηγορίες",
        "mostimages": "Περισσότερο χρησιμοποιούμενα αρχεία",
        "mostinterwikis": "Σελίδες με τους περισσότερους διαγλωσσικούς συνδέσμους",
        "pager-older-n": "{{PLURAL:$1|1 παλαιότερο|$1 παλαιότερα}}",
        "suppress": "Παρόραμα",
        "querypage-disabled": "Αυτή η ειδική σελίδα είναι απενεργοποιημένη για λόγους απόδοσης.",
+       "apihelp": "Βοήθεια API",
+       "apihelp-no-such-module": "Το Module \"$1\" δεν βρέθηκε.",
        "booksources": "Πηγές βιβλίων",
        "booksources-search-legend": "Αναζήτηση για πηγές βιβλίων",
        "booksources-isbn": "ISBN:",
        "listgrouprights-namespaceprotection-header": "Περιορισμοί ονοματοχώρων",
        "listgrouprights-namespaceprotection-namespace": "Ονοματοχώρος",
        "listgrouprights-namespaceprotection-restrictedto": "Δικαίωμα(τα) που επιτρέπει(ουν) σε χρήστη να επεξεργαστεί",
-       "trackingcategories": "Παρακολούθηση κατηγοριών",
+       "trackingcategories": "Κατηγορίες παρακολούθησης",
+       "trackingcategories-summary": "Αυτή η σελίδα εμφανίζει τις κατηγορίες παρακολούθησης το περιεχόμενο των οποίων συμπληρώνεται αυτόματα από το λογισμικό MediaWiki. Τα ονόματά τους μπορεί να αλλαχθούν με την αλλαγή των σχετικών μηνυμάτων συστήματος στον ονοματοχώρο {{ns:8}}.",
        "trackingcategories-msg": "Κατηγορία παρακολούθησης",
        "trackingcategories-name": "Όνομα μηνύματος",
-       "trackingcategories-desc": "Κριτήρια συμπερίληψης κατηγορίας",
+       "trackingcategories-desc": "Κριτήρια συμπερίληψης στην κατηγορία",
        "noindex-category-desc": "Η σελίδα δεν καταλογογραφείται από ρομπότ, επειδή έχει τη μαγική λέξη <code><nowiki>__NOINDEX__</nowiki></code> σε αυτή και είναι σε ένα χώρο ονομάτων όπου αυτή η ετικέτα επιτρέπεται.",
-       "index-category-desc": "Η σελίδα έχει ένα <code><nowiki>__INDEX__</nowiki></code> (και είναι σε ένα χώρο ονομάτων όπου η ετικέτα επιτρέπεται), και ως εκ τούτου καταλογογραφείται από ρομπότ, ενώ κανονικά δεν θα γινόταν.",
+       "index-category-desc": "Η σελίδα περιέχει στον κώδικά της ένα <code><nowiki>__INDEX__</nowiki></code> (και βρίσκεται σε έναν ονοματοχώρο όπου αυτή η σήμανση επιτρέπεται) και ως εκ τούτου καταλογογραφείται από ρομπότ, ενώ κανονικά δεν θα καταλογογραφείτο.",
        "post-expand-template-inclusion-category-desc": "Το μέγεθος της σελίδας είναι μεγαλύτερο από <code>$wgMaxArticleSize</code> μετά την επέκταση όλων των προτύπων, έτσι ώστε ορισμένα πρότυπα δεν έχουν αναπτυχθεί.",
        "post-expand-template-argument-category-desc": "Η σελίδα είναι μεγαλύτερη από <code>$wgMaxArticleSize</code> μετά την επέκταση της μεταβλητής ενός προτύπου (κάτι σε τρίπλές αγκύλες, όπως <code>{{{Foo}}}</code>).",
        "broken-file-category-desc": "Η σελίδα περιέχει ένα σπασμένο σύνδεσμο αρχείου (σύνδεσμο για να ενσωμάτωση ενός αρχείου, ενώ το αρχείο δεν υπάρχει).",
        "restriction-edit": "Επεξεργασία",
        "restriction-move": "Μετακίνηση",
        "restriction-create": "Δημιουργήστε",
-       "restriction-upload": "Î\95Ï\80ιÏ\86Ï\8cÏ\81Ï\84Ï\89Ï\83η",
+       "restriction-upload": "Î\91νέβαÏ\83μα Î±Ï\81Ï\87είοÏ\85",
        "restriction-level-sysop": "πλήρως προστατευμένη",
        "restriction-level-autoconfirmed": "ημιπροστατευμένη",
        "restriction-level-all": "οποιοδήποτε επίπεδο",
        "importsuccess": "Η εισαγωγή πέτυχε!",
        "importnosources": "Δεν έχουν καθοριστεί πηγές για την εισαγωγή από άλλο Wiki και η απευθείας φόρτωση στο ιστορικό έχει απενεργοποιηθεί.",
        "importnofile": "Δεν επιφορτώθηκε κανένα αρχείο εισαγωγής.",
-       "importuploaderrorsize": "Î\97 ÎµÏ\80ιÏ\86Ï\8cÏ\81Ï\84Ï\89Ï\83η Ï\84οÏ\85 ÎµÎ¹Ï\83αγÏ\8cμενοÏ\85 Î±Ï\81Ï\87είοÏ\85 απέτυχε. Το μέγεθος του αρχείου ξεπερνά το επιτρεπόμενο όριο.",
-       "importuploaderrorpartial": "Î\97 ÎµÏ\80ιÏ\86Ï\8cÏ\81Ï\84Ï\89Ï\83η Ï\84οÏ\85 ÎµÎ¹Ï\83αγÏ\8cμενοÏ\85 Î±Ï\81Ï\87είοÏ\85 Î±Ï\80έÏ\84Ï\85Ï\87ε. Î¤Î¿ Î±Ï\81Ï\87είο ÎµÏ\80ιÏ\86οÏ\81Ï\84Ï\8eθηκε μόνο εν μέρει.",
-       "importuploaderrortemp": "Î\97 ÎµÏ\80ιÏ\86Ï\8cÏ\81Ï\84Ï\89Ï\83η Ï\84οÏ\85 ÎµÎ¹Ï\83αγÏ\8cμενοÏ\85 Î±Ï\81Ï\87είοÏ\85 απέτυχε. Λείπει ένας προσωρινός φάκελος.",
+       "importuploaderrorsize": "Το Î±Î½Î­Î²Î±Ï\83μα Ï\84οÏ\85 Î±Ï\81Ï\87είοÏ\85 ÎµÎ¹Ï\83αγÏ\89γήÏ\82 απέτυχε. Το μέγεθος του αρχείου ξεπερνά το επιτρεπόμενο όριο.",
+       "importuploaderrorpartial": "Το Î±Î½Î­Î²Î±Ï\83μα Ï\84οÏ\85 Î±Ï\81Ï\87είοÏ\85 ÎµÎ¹Ï\83αγÏ\89γήÏ\82 Î±Ï\80έÏ\84Ï\85Ï\87ε. Î¤Î¿ Î±Ï\81Ï\87είο Î±Î½Î­Î²ηκε μόνο εν μέρει.",
+       "importuploaderrortemp": "Το Î±Î½Î­Î²Î±Ï\83μα Ï\84οÏ\85 Î±Ï\81Ï\87είοÏ\85 ÎµÎ¹Ï\83αγÏ\89γήÏ\82 απέτυχε. Λείπει ένας προσωρινός φάκελος.",
        "import-parse-failure": "Σφάλμα παραμέτρου XML κατά την  εισαγωγή",
        "import-noarticle": "Καμία σελίδα για εισαγωγή!",
        "import-nonewrevisions": "Καμία αναθεώρηση δεν εισήχθει (όλες είτε ήταν ήδη παρούσες, ή παραλήφθηκαν λόγω σφαλμάτων).",
        "xml-error-string": "$1 στη γραμμή $2, στήλη $3 (byte $4): $5",
-       "import-upload": "Î\95Ï\80ιÏ\86Ï\8cÏ\81Ï\84Ï\89Ï\83η δεδομένων XML",
+       "import-upload": "Î\91νέβαÏ\83μα δεδομένων XML",
        "import-token-mismatch": "Απώλεια των στοιχείων της συνόδου. Παρακαλούμε προσπαθήστε ξανά.",
        "import-invalid-interwiki": "Δεν είναι δυνατή η εισαγωγή από το καθορισμένο wiki.",
        "import-error-edit": "Η σελίδα «$1» δεν εισήχθη επειδή δεν σας επιτρέπεται να την επεξεργαστείτε.",
        "tooltip-feed-atom": "Ροή Atom για αυτήν τη σελίδα",
        "tooltip-t-contributions": "Λίστα με τις συνεισφορές αυτού του χρήστη",
        "tooltip-t-emailuser": "Αποστολή μηνύματος ηλεκτρονικής αλληλογραφίας σε αυτόν το χρήστη",
+       "tooltip-t-info": "Περισσότερες πληροφορίες σχετικά με αυτήν τη σελίδα",
        "tooltip-t-upload": "Ανέβασμα αρχείων",
        "tooltip-t-specialpages": "Η λίστα με όλες τις ειδικές σελίδες",
        "tooltip-t-print": "Εκτυπώσιμη έκδοση αυτής της σελίδας",
        "watchlistedit-raw-done": "Η λίστα παρακολούθησής σας ενημερώθηκε.",
        "watchlistedit-raw-added": "{{PLURAL:$1|1 σελίδα|$1 σελίδες}} προστέθηκαν:",
        "watchlistedit-raw-removed": "{{PLURAL:$1|1 σελίδα|$1 σελίδες}} αφαιρέθηκαν:",
+       "watchlistedit-clear-title": "Εκκαθαρισμένη λίστα παρακολούθησης",
+       "watchlistedit-clear-legend": "Εκκαθάριση λίστας παρακολούθησης",
        "watchlistedit-clear-explain": "Όλοι οι τίτλοι θα αφαιρεθούν από τη λίστα παρακολούθησής σας",
        "watchlistedit-clear-titles": "Τίτλοι:",
        "watchlistedit-clear-submit": "Καθαρίστε τη λίστα παρακολούθησης (αυτό είναι μόνιμο!)",
        "watchlistedit-clear-done": "Η λίστα παρακολούθησής σας έχει καθαριστεί.",
        "watchlistedit-clear-removed": "{{PLURAL:$1|1 τίτλος αφαιρέθηκε|$1 τίτλοι αφαιρέθηκαν}}:",
        "watchlistedit-too-many": "Υπάρχουν υπερβολικά πολλές σελίδες και δεν μπορούν να εμφανιστούν εδώ.",
+       "watchlisttools-clear": "Εκκαθάριση της λίστας παρακολούθησης",
        "watchlisttools-view": "Προβολή σχετικών αλλαγών",
        "watchlisttools-edit": "Προβολή και επεξεργασία λίστας παρακολούθησης",
        "watchlisttools-raw": "Επεξεργασία πρωτογενούς λίστας παρακολούθησης",
        "duplicate-defaultsort": "'''Προειδοποίηση:''' Το προεπιλεγμένο κλειδί ταξινόμησης «$2» υπερισχύει του προηγούμενου προεπιλεγμένου κλειδιού «$1».",
        "version": "Έκδοση",
        "version-extensions": "Εγκαταστημένες επεκτάσεις",
-       "version-skins": "ΠÏ\81οÏ\83Ï\8cÏ\88εις",
+       "version-skins": "Î\95γκαÏ\84εÏ\83Ï\84ημένα Î¸Î­Î¼Î±Ï\84α ÎµÎ¼Ï\86άνιÏ\83ης",
        "version-specialpages": "Ειδικές σελίδες",
        "version-parserhooks": "Άγκιστρα του συντακτικού αναλυτή",
        "version-variables": "Παράμετροι",
        "version-license": "Άδεια MediaWiki",
        "version-ext-license": "Άδεια χρήσης",
        "version-ext-colheader-name": "Επέκταση",
+       "version-skin-colheader-name": "Θέμα εμφάνισης",
        "version-ext-colheader-version": "Έκδοση",
        "version-ext-colheader-license": "Άδεια χρήσης",
        "version-ext-colheader-description": "Περιγραφή",
        "version-ext-colheader-credits": "Δημιουργοί",
        "version-license-title": "Άδεια χρήσης για $1",
+       "version-license-not-found": "Δεν βρέθηκαν αναλυτικές πληροφορίες αδειοδότησης για την επέκταση αυτή.",
+       "version-credits-title": "Εύσημα για $1",
+       "version-credits-not-found": "Δεν βρέθηκαν αναλυτικές πληροφορίες ευσήμων για την επέκταση αυτή.",
        "version-poweredby-credits": "Αυτό το wiki λειτουργεί με το λογισμικό '''[https://www.mediawiki.org/ MediaWiki]''', πνευματική ιδιοκτησία © 2001-$1 $2.",
        "version-poweredby-others": "άλλοι",
        "version-poweredby-translators": "translatewiki.net μεταφραστές",
        "pagelang-select-lang": "Επιλογή γλώσσας",
        "right-pagelang": "Αλλαγή γλώσσας σελίδας",
        "action-pagelang": "αλλαγή της γλώσσας σελίδας",
+       "logentry-pagelang-pagelang": "{{GENDER:$2|Ο|Η}} $1 άλλαξε τη γλώσσα σελίδας της σελίδας $3 από $4 σε $5.",
        "default-skin-not-found-row-enabled": "* <code>$1</code> /$2 (ενεργοποιημένο)",
        "default-skin-not-found-row-disabled": "* <code>$1</code> / $2 ('''απενεργοποιημένο''')",
        "mediastatistics": "Στατιστικά πολυμέσων",
+       "mediastatistics-summary": "Στατιστικά για τύπους ανεβασμένων αρχείων. Περιέχει μόνο την πλέον πρόσφατη έκδοση κάθε αρχείου. Δεν συμπεριλαμβάνονται παλιές ή διαγεγραμμένες εκδόσεις αρχείων.",
        "mediastatistics-table-mimetype": "Τύποι MIME",
+       "mediastatistics-table-extensions": "Πιθανές επεκτάσεις",
        "mediastatistics-table-count": "Αριθμός αρχείων",
+       "mediastatistics-table-totalbytes": "Συνολικό μέγεθος",
+       "mediastatistics-header-unknown": "Άγνωστα",
        "mediastatistics-header-bitmap": "Εικόνες bitmap",
        "mediastatistics-header-drawing": "Σχέδια (διανυσματικές εικόνες)",
        "mediastatistics-header-audio": "Ήχος",
-       "mediastatistics-header-office": "Γραφείο"
+       "mediastatistics-header-video": "Βίντεο",
+       "mediastatistics-header-multimedia": "Εμπλουτισμένα πολυμέσα",
+       "mediastatistics-header-office": "Γραφείο",
+       "mediastatistics-header-text": "Μορφές κειμένου",
+       "mediastatistics-header-executable": "Εκτελέσιμα",
+       "mediastatistics-header-archive": "Συμπιεσμένες μορφές",
+       "json-error-unknown": "Υπήρξε πρόβλημα με το JSON. Σφάλμα: $1",
+       "json-error-ctrl-char": "Σφάλμα χαρακτήρα ελέγχου, πιθανόν είναι εσφαλμένα κωδικοποιημένος.",
+       "json-error-syntax": "Συντακτικό λάθος",
+       "json-error-recursion": "Μία ή περισσότερες αναδρομικές αναφορές στην προς κωδικοποίηση τιμή.",
+       "json-error-inf-or-nan": "Μία ή περισσότερες τιμές NAN ή INF στην προς κωδικοποίηση τιμή.",
+       "json-error-unsupported-type": "Δόθηκε τιμή τύπου που δεν μπορεί να κωδικοποιηθεί."
 }
index 458a3ea..26801db 100644 (file)
@@ -55,6 +55,7 @@
        "tog-watchdefault": "Aldoni al mia atentaro paĝojn kaj dosierojn redaktitajn de mi",
        "tog-watchmoves": "Aldoni paĝojn kaj dosierojn, kiujn mi movas, al mia atentaro",
        "tog-watchdeletion": "Aldoni paĝojn kaj dosierojn, kiujn mi forigas, al mia atentaro",
+       "tog-watchrollback": "Aldoni paĝojn, kie mi amasmalfaris, al mia atentaro.",
        "tog-minordefault": "Marki defaŭlte ĉiujn redaktojn kiel etajn",
        "tog-previewontop": "Montri antaŭrigardon antaŭ redaktilo",
        "tog-previewonfirst": "Montri antaŭrigardon je unua redakto",
        "jumptonavigation": "navigado",
        "jumptosearch": "serĉi",
        "view-pool-error": "Bedaŭrinde la serviloj estas tro uzataj ĉi-momente.\nTro da uzantoj provas vidi ĉi tiun paĝon.\nBonvolu atendi iom antaŭ ol provi atingi ĝin denove.\n\n$1",
+       "generic-pool-error": "Bedaŭrinde la serviloj estas tro uzataj ĉi-momente.\nTro da uzantoj provas vidi ĉi tiun risurcon.\nBonvolu iom atendi antaŭ vi provos atingi ĝin denove.",
        "pool-timeout": "Tempolimo atingita dum atendo de ŝlosado",
        "pool-queuefull": "Atendovico de servilaro estas plena.",
        "pool-errorunknown": "Nekonata eraro",
+       "pool-servererror": "La servo manaĝanta aliron al serviloj ne estas disponebla ($1).",
        "aboutsite": "Pri {{SITENAME}}",
        "aboutpage": "Project:Enkonduko",
        "copyright": "La enhavo estas disponebla laŭ $1, se ne estas alia indiko.",
        "filerenameerror": "Ne eblis alinomigi dosieron \"$1\" al \"$2\".",
        "filedeleteerror": "Neeblis forigi dosieron \"$1\".",
        "directorycreateerror": "Ne povis krei dosierujon \"$1\".",
+       "directoryreadonlyerror": "Dosierujo \"$1\" estas nurlega.",
+       "directorynotreadableerror": "Dosierujo \"$1\" estas nelegebla.",
        "filenotfound": "Ne eblis trovi dosieron \"$1\".",
        "unexpected": "Neatendita valoro: \"$1\"=\"$2\".",
        "formerror": "Eraro: ne eblis liveri formulon",
        "viewyourtext": "Vi povas vidi kaj kopii la fonton de '''viaj redaktoj''' al ĉi tiu paĝo:",
        "protectedinterface": "Ĉi tiu paĝo provizas interfacan tekston por la programaro, kaj estas ŝlosita por malebligi misuzon.\nPor aldoni aŭ ŝanĝi tradukojn por ĉiuj vikioj, bonvolu uzi [//translatewiki.net/ translatewiki.net], la projekton por provizi tradukojn por MediaWiki.",
        "editinginterface": "<strong>Atentu:</strong> Vi redaktas paĝon, kiu provizas interfacan tekston por la programaro.\nŜanĝoj de ĉi tiu teksto ŝanĝos aspekton de la interfaco por aliaj uzantoj de ĉi tiu vikio.\nPor aldoni aŭ ŝanĝi tradukojn por ĉiuj vikioj, bonvolu uzi [//translatewiki.net/ translatewiki.net], la projekton por provizi tradukojn por MediaWiki.",
+       "translateinterface": "Por ŝanĝi tradukaĵojn por ĉiuj vikioj bonvolu uzi [//translatewiki.net/ translatewiki.net], la komunan tradukan projekton de MediaWiki.",
        "cascadeprotected": "Ĉi tiu paĝo estas protektita kontraŭ redaktado, ĉar ĝi estas inkludita en la {{PLURAL:$1|sekvan paĝon, kiu|sekvajn paĝojn, kiuj}} estas {{PLURAL:$1|protektata|protektataj}} kun la \"kaskada\" opcio ŝaltita sur:\n$2",
        "namespaceprotected": "Vi ne rajtas redakti paĝojn en la '''$1''' nomspaco.",
        "customcssprotected": "Vi ne rajtas redakti ĉi tiun CSS-paĝon, ĉar ĝi enhavas personajn alĝustigojn de alia uzanto.",
        "createaccount-text": "Iu kreis konton por via retadreso en {{SITENAME}} ($4) nomata \"$2\", kun pasvorto \"$3\". Vi ensalutu kaj ŝanĝu vian pasvorton nun.\n\nVi povas ignori ĉi tiun mesaĝon, se ĉi tiu konto estis kreita erare.",
        "login-throttled": "Vi ĵus tro ofte provis ensaluti.\nBonvolu ĝisatendi $1 antaŭ reprovi.",
        "login-abort-generic": "Via ensaluto malsukcesis - Ĉesigita",
+       "login-migrated-generic": "Via konto estis migrita kaj via uzantonomo ne plu ekzistas en tiu ĉi vikio.",
        "loginlanguagelabel": "Lingvo: $1",
        "suspicious-userlogout": "Via peto por elsaluti estis malpermesita, ĉar verŝajne ĝi estis sendita de trompita retumilo aŭ kaŝiĝanta prokura servilo.",
        "createacct-another-realname-tip": "La vera nomo estas nenecesa.\nSe vi decidas indiki ĝin, ĝi estos uzata por montri atribuadon de viaj kontribuoj.",
        "preview": "Antaŭrigardo",
        "showpreview": "Antaŭrigardo",
        "showdiff": "Montri ŝanĝojn",
+       "blankarticle": "<strong>Atentigo:</strong>La paĝo kiun vi kreas estas malplena.\nSe vi denove klakos al \"{{int:savearticle}}\" la paĝo estos konservita sen enhavo.",
        "anoneditwarning": "<strong>Averto:</strong> Vi ne estas ensalutinta.\nVia IP-adreso enregistriĝos en la redakta historio de tiu ĉi paĝo. Se vi <strong>[$1 ensalutas]</strong> aŭ <strong>[$2 kreas konton]</strong>, viaj redaktoj estos atribuitaj al via salutnomo, kune kun aliaj bonaĵoj.",
        "anonpreviewwarning": "''Vi ne estas ensalutita. La konservo de la paĝo registros vian IP-adreson en redakta historio de ĉi tiu paĝo.''",
        "missingsummary": "'''Rememorigilo:''' Vi ne provizis redaktan resumon. Se vi alklakos denove la konservan butonon, via redaktaĵo estos konservita sen resumo.",
        "permissionserrorstext": "Vi ne rajtas fari tion pro la {{PLURAL:$1|sekva kialo|sekvaj kialoj}}:",
        "permissionserrorstext-withaction": "Vi ne rajtas $2, pro la {{PLURAL:$1|jena kialo|jenaj kialoj}}:",
        "recreate-moveddeleted-warn": "'''Averto: Vi rekreas paĝon, kiu estis antaŭe forigita.'''\n\nVi konsideru, ĉu konvenas daŭre redakti ĉi tiun paĝon.\nJen la protokolo de forigoj kaj alinomigado por via oportuno:",
-       "moveddeleted-notice": "Ĉi tiu paĝo estis forigita.\nJen la protokolo pri forigado kaj alinomigado por via referenco.",
+       "moveddeleted-notice": "Ĉi tiu paĝo estis forigita.\nPliaj detaloj estas en protokolo pri forigado kaj alinomado de tiu ĉi paĝo.",
        "log-fulllog": "Vidi kompletan protokolon",
        "edit-hook-aborted": "Redakto estis ĉesigita per etendaĵo de la Vikia softvaro.\nĜi ne donis eksplikon.",
        "edit-gone-missing": "Ne eblis ĝisdatigi la paĝon.\nVerŝajne ĝi estis forigita.",
        "content-model-text": "ordinara teksto",
        "content-model-javascript": "JavaScript",
        "content-model-css": "CSS",
+       "duplicate-args-category": "Paĝoj kun pluroblaj argumentoj en ŝanblonvokoj",
+       "duplicate-args-category-desc": "La paĝo enhavas uzon de ŝablono kun pluroble uzitaj argumentoj, kiel ekzemple <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> aŭ <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "expensive-parserfunction-warning": "Averto: Ĉi tiu paĝo enhavas tro da multekostaj sintaksaj funkcio-vokoj.\n\nĜi havu malpli ol $2 {{PLURAL:$2|vokon|vokojn}}, sed nun estas $1 {{PLURAL:$1|voko|vokoj}}.",
        "expensive-parserfunction-category": "Paĝoj kun tro da multekostaj sintaksaj funkcio-vokoj",
        "post-expand-template-inclusion-warning": "Averto: Inkluziva pezo de ŝablonoj estas tro granda.\nIuj ŝablonoj ne estos inkluzivitaj.",
        "parser-template-recursion-depth-warning": "Ŝablona profundeco transpasis limon ($1)",
        "language-converter-depth-warning": "Profundo de lingvo-konvertilo preterpasis limon ($1)",
        "node-count-exceeded-category": "Paĝoj kie la nombro da nodoj estas preterpasita",
+       "node-count-exceeded-category-desc": "La paĝo superis maksimuman nombron de nodoj.",
        "node-count-exceeded-warning": "Paĝo preterpasis la nombron da nodoj.",
        "expansion-depth-exceeded-category": "Paĝoj en kiuj la ekpansiprofundo estas preterpasita",
+       "expansion-depth-exceeded-category-desc": "La paĝo superis maksimuman profundecon de ekspando.",
        "expansion-depth-exceeded-warning": "Paĝo preterpasis la ekpansiprofundon.",
        "parser-unstrip-loop-warning": "Cirkloreferencon detektis",
        "parser-unstrip-recursion-limit": "Rikurlimiton de analizopoj ($1) superis",
        "rev-deleted-event": "(protokola ago forigita)",
        "rev-deleted-user-contribs": "[salutnomo aŭ IP-adreso estis forigita - redakto estas kaŝita en kontribuoj]",
        "rev-deleted-text-permission": "Ĉi tiu revizio de la paĝo estis '''forigita'''.\nEble estas detaloj en la [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} protokolo pri forigado].",
+       "rev-suppressed-text-permission": "Ĉi tiu paĝa revizio estis '''kaŝita'''.\nDetaloj estas troveblaj en la [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} protokolo pri kaŝitoj revizioj].",
        "rev-deleted-text-unhide": "Ĉi tiu revizio de la paĝo estis '''forigita'''.\nDetaloj estas troveblaj en la [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} protokolo pri forigado].\nVi ankoraŭ povas [$1 vidi ĉi tiun revizion] se vi volas kontinui.",
        "rev-suppressed-text-unhide": "Ĉi tiu paĝa revizio estis '''kaŝita'''.\nDetaloj estas troveblaj en la [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} protokolo pri kaŝado].\nVi povas ankoraŭ [$1 rigardi ĉi tiun revizion] se vi volas daŭrigi.",
        "rev-deleted-text-view": "Ĉi tiu revizio de la paĝo estis '''forigita'''.\nVi povas rigardi ĝin; detaloj estas trovebla en la [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} protokolo pri forigado].",
        "search-result-category-size": "{{PLURAL:$1|1 membro|$1 membroj}} ({{PLURAL:$2|1 subkategorio|$2 subkategorioj}}, {{PLURAL:$3|1 dosiero|$3 dosieroj}})",
        "search-redirect": "(alidirektilo $1)",
        "search-section": "(sekcio $1)",
+       "search-category": "(kategorio $1)",
        "search-file-match": "(kongruas kun dosiera enhavo)",
        "search-suggest": "Ĉu vi intenciis: $1",
        "search-interwiki-caption": "Kunprojektoj",
        "gender-female": "Ina",
        "prefs-help-gender": "Nedeviga: uzita por sekseca salutado de la programaro. Ĉi tiu informo montriĝos publike.",
        "email": "Retadreso",
-       "prefs-help-realname": "* Vera nomo (opcia): se vi elektas sciigi ĝin, ĝi estos uzita por aŭtorigi vin pri viaj kontribuoj.",
+       "prefs-help-realname": "* Vera nomo estas nedeviga.\nSe vi elektas sciigi ĝin, ĝi estos uzita por aŭtorigi vin por viaj kontribuoj.",
        "prefs-help-email": "Retadreso estas nedeviga, sed ebligas ke via pasvorto estos reagordota, se vi estos forgesinta ĝin.",
        "prefs-help-email-others": "Vi povas elekti ke aliaj povas kontakti vin per via uzanto-paĝo aŭ parol-paĝo sen la neceso malkaŝi vian identecon.",
        "prefs-help-email-required": "Ret-adreso estas bezonata.",
        "right-browsearchive": "Serĉi forigitajn paĝojn",
        "right-undelete": "Restarigi paĝon",
        "right-suppressrevision": "Montri, kaŝi kaj malkaŝi specifajn paĝajn versiojn de ajna uzanto",
+       "right-viewsuppressed": "Vidi reviziojn kaŝite de iu ajn uzanto",
        "right-suppressionlog": "Vidi privatajn protokolojn",
        "right-block": "Forbari aliajn uzantoj de redaktado",
        "right-blockemail": "Forbari uzanton de retpoŝta sendado",
        "backend-fail-alreadyexists": "La dosiero \"$1\" jam ekzistas.",
        "backend-fail-store": "Ne povis konservi dosieron $1 ĉe $2.",
        "backend-fail-copy": "Ne povis kopii dosieron $1 al $2.",
-       "backend-fail-move": "Ne povis movi dosieron $1 al $2.",
+       "backend-fail-move": "Ne povis movi dosieron \"$1\" al \"$2\".",
        "backend-fail-opentemp": "Ne povis malfermi provizoran dosieron.",
        "backend-fail-writetemp": "Ne povis skribi intertempan dosieron.",
        "backend-fail-closetemp": "Ne povis fermi provizoran dosieron.",
        "wantedpages-badtitle": "Malvalida titolo en rezulta aro: $1",
        "wantedfiles": "Dezirataj dosieroj",
        "wantedfiletext-cat": "La jenaj dosieroj estas uzataj sed ne ekzistas. Dosieroj de eksteraj konservujoj eble estos listigita malgraŭ ne ekzistante. Tia malprave pozitivaj rezultoj estos <del>forstrekita</del>. Ankaŭ, paĝoj kiuj enmetas dosierojn kiuj ne ekzistas estas listigita en [[:$1]].",
-       "wantedfiletext-nocat": "La jenaj dosieroj estas uzataj sed ne ekzistas. Dosieroj de eksteraj dosierujoj eble estas listigitaj malgraŭ eksistado. Tia malprave pozitiva rezulto estos <del>forstrekita</del>.",
+       "wantedfiletext-cat-noforeign": "Jen dosieroj kiuj estas uzataj, sed mankas. Plue, paĝoj kiuj enmetas dosierojn mankantajn estas en [[:$1]].",
+       "wantedfiletext-nocat": "La jenaj dosieroj estas uzataj sed mankas. Dosieroj de eksteraj dosierujoj eble estas listigitaj malgraŭ eksistado. Tia malprave pozitiva rezulto estos <del>forstrekita</del>.",
+       "wantedfiletext-nocat-noforeign": "Jen dosieroj kiuj estas uzataj sed mankas.",
        "wantedtemplates": "Dezirataj ŝablonoj",
        "mostlinked": "Plej ligitaj paĝoj",
        "mostlinkedcategories": "Plej ligitaj kategorioj",
        "newpages": "Novaj paĝoj",
        "newpages-username": "Salutnomo:",
        "ancientpages": "Plej malnovaj artikoloj",
-       "move": "Alinomigi",
-       "movethispage": "Alinomigi ĉi tiun paĝon",
+       "move": "Alinomi",
+       "movethispage": "Alinomi ĉi tiun paĝon",
        "unusedimagestext": "La jenaj dosieroj ekzistas sed ne estas enmetas en iu ajn paĝo.\nBonvolu noti ke aliaj retejoj povas ligi dosieron kun rekta URL-o, kaj tial estas listebla ĉi tie malgraŭ estante aktive uzata.",
        "unusedcategoriestext": "La paĝoj de la sekvanta kategorio jam ekzistas, sed neniu alia artikolo aŭ kategorio rilatas al ĝi.",
        "notargettitle": "Sen celpaĝo",
        "pager-older-n": "{{PLURAL:$1|pli malnova 1|pli malnovaj $1}}",
        "suppress": "Superrigardo",
        "querypage-disabled": "Tiu ĉi speciala paĝo estas malfunkciigita pro rendimentaj kialoj.",
+       "apihelp": "Helpo pri API",
        "apihelp-no-such-module": "Modulo \"$1\" ne estis trovita.",
        "booksources": "Libroservoj",
        "booksources-search-legend": "Serĉi librofontojn",
        "mywatchlist": "Atentaro",
        "watchlistfor2": "Por $1 $2",
        "nowatchlist": "Vi ne jam elektis priatenti iun ajn paĝon.",
-       "watchlistanontext": "Bonvolu $1 por vidi aŭ redakti erojn en via atentaro.",
+       "watchlistanontext": "Bonvolu ensaluti por vidi aŭ redakti erojn en via atentaro.",
        "watchnologin": "Ne ensalutinta",
        "addwatch": "Aldoniĝi al atentaro",
        "addedwatchtext": "La paĝo \"[[:$1]]\" aldoniĝis al via [[Special:Watchlist|atentaro]]. Estontaj ŝanĝoj de tiu paĝo kaj de ĝia rilata diskutpaĝo aperos tie.",
+       "addedwatchtext-short": "La paĝo \"$1\" estis aldonita al via atento-listo.",
        "removewatch": "Forigi el atentaro",
        "removedwatchtext": "La paĝo \"[[:$1]]\" estas forigita el via [[Special:Watchlist|atentaro]].",
        "removedwatchtext-short": "La paĝo \"$1\" estis forigita el via atento-listo.",
        "protect-othertime": "Alia tempo:",
        "protect-othertime-op": "alia tempo",
        "protect-existing-expiry": "Ekzistanta protektdaŭro: $3, $2",
+       "protect-existing-expiry-infinity": "Ekzistanta protektdaŭro: senfina",
        "protect-otherreason": "Alia/plua kialo:",
        "protect-otherreason-op": "Alia/plua kialo",
        "protect-dropdown": "*Oftaj kialoj por protektado\n** Tro da vanadlismo\n** Tro da spamado\n** Malutila redakto-milito\n** Paĝo kun multo da trafiko",
        "maximum-size": "Maksimuma pezo:",
        "pagesize": "(bitokoj)",
        "restriction-edit": "Redakti",
-       "restriction-move": "Alinomigi",
+       "restriction-move": "Alinomi",
        "restriction-create": "Krei",
        "restriction-upload": "Alŝuti",
        "restriction-level-sysop": "plene protektita",
        "tooltip-invert": "Marku ĉi tiu skatolon por kaŝi ŝanĝoj al paĝoj en la elektita nomspaco (kaj la asocia nomspaco, se tiel markita)",
        "namespace_association": "Asociita nomspaco",
        "tooltip-namespace_association": "Marku ĉi tiu skatolo por inkluzivi la diskutan aŭ teman nomspacon asocie de la elekta nomspaco",
-       "blanknamespace": "(Artikoloj)",
+       "blanknamespace": "(Ĉefa)",
        "contributions": "Kontribuoj de {{GENDER:$1|uzanto|uzantino}}",
        "contributions-title": "Kontribuoj de uzanto $1",
        "mycontris": "Kontribuoj",
        "sp-contributions-newbies-sub": "Kontribuoj de novaj uzantoj. Forigitaj paĝoj ne estas montritaj.",
        "sp-contributions-newbies-title": "Kontribuoj de novaj uzantoj",
        "sp-contributions-blocklog": "protokolo de forbaroj",
+       "sp-contributions-suppresslog": "kaŝitaj kontribuoj de uzanto",
        "sp-contributions-deleted": "forigitaj kontribuoj de uzanto",
        "sp-contributions-uploads": "alŝutoj",
        "sp-contributions-logs": "protokoloj",
        "lockfilenotwritable": "La datumbaza dosiero pri ŝlosado ne estas skribebla. Por ŝlosi aŭ malŝlosi la datumbazon, ĉi devas esti skribebla de la TTT-servilo.",
        "databasenotlocked": "La datumbazo ne estas ŝlosita.",
        "lockedbyandtime": "(de {{GENDER:$1|$1}} je $2, $3)",
-       "move-page": "Alinomigi $1",
-       "move-page-legend": "Alinomigi paĝon",
+       "move-page": "Alinomi $1",
+       "move-page-legend": "Alinomi paĝon",
        "movepagetext": "Per la jena formulo vi povas ŝanĝi la nomon de iu paĝo, kunportante ĝian historion de redaktoj al la nova nomo.\nLa antaŭa titolo fariĝos alidirektilo al la nova titolo.\nVi povas ĝisdatigi alidirektilojn kiu indikas la originalan titolon aŭtomate.\nSe vi elektas ĝisdatigi permane, bonvolu kontroli [[Special:DoubleRedirects|duoblajn]] aŭ [[Special:BrokenRedirects|rompitajn alidirektilojn]].\nVi estas responsa por certigi ke ligilojn direktas fidinde.\n\nNotu, ke la paĝo '''ne''' estos movita se jam ekzistas paĝo ĉe la nova titolo, krom se tiu loko estas malplena aŭ alidirektilo al ĉi tiu paĝo, kaj sen antaŭa redaktohistorio.\nPro tio, vi ja povos removi la paĝon je la antaŭa titolo se vi mistajpus, kaj ne povas forviŝi ekzistantan paĝon per movo.\n\n'''AVERTO!'''\nTio povas esti drasta kaj neatendita ŝanĝo por populara paĝo;\nbonvolu certigi vin, ke vi komprenas ties konsekvencojn antaŭ ol vi antaŭeniru.",
        "movepagetext-noredirectfixer": "Per jena formularo vi povas alinomigi paĝon, kaj movi tutan ĝian redaktohistorion al la nova nomo. \nLa antaŭa titolo alidirektigos onin al la nova titolo.\nKontrolu pri [[Special:DoubleRedirects|duoblajn]] aŭ [[Special:BrokenRedirects|nefunkciantajn alidirektilojn]].\nVi respondecas pri tio ke ligoj restas montrantaj ĝustadirekten.\n\nKonsciu ke la paĝo '''ne'' estas movota se jam ekzistas paĝo havanta la novan titolon, krom se ĝi maplenas aŭ estas alidirektilo sen antaŭa redaktohistorio.\nTio ĉi signifas ke vi povas alinomigi paĝon reen al antaŭa nomo se vi eraras, kaj vi ke vi ne povas anstataŭigi ekzistantan paĝon.\n\n'''Averto!''\nEblas ke tio ĉi estas drasta kaj neatendita ŝanĝo de populara paĝo;\nAntaŭ daŭrigi, bonvolu certiĝi, ke vi komprenas la konsekvencojn de tiuj ĉi ŝanĝo.",
        "movepagetalktext": "La movo aŭtomate kunportos la diskuto-paĝon, se tia ekzistas, '''krom se:'''\n*Vi movas la paĝon tra nomspacoj (ekz de ''Nomo'' je ''User:Nomo''),\n*Ne malplena diskuto-paĝo jam ekzistas je la nova nomo, aŭ\n*Vi malelektas la suban ŝaltilon.\n\nTiujokaze, vi nepre permane kunigu la diskuto-paĝojn se vi tion deziras.",
-       "movearticle": "Alinomigi paĝon",
-       "moveuserpage-warning": "'''Averto:''' Vi preskaŭ alinomigas paĝon de uzanto. Bonvolu noti ke nur la paĝo estos alinomigita kaj la uzanto mem ''ne'' estos alinomigita.",
+       "movearticle": "Alinomi paĝon",
+       "moveuserpage-warning": "<strong>Averto:</strong> Vi preskaŭ alinomas paĝon de uzanto. Bonvolu noti ke nur la paĝo estos alinomita kaj la uzanto mem <em>ne</em> estos alinomita.",
        "movecategorypage-warning": "<strong>Averto:</strong> Vi baldaŭ movos kategorian paĝon. Bonvolu noti ke, nur la paĝo estos movita, kaj la paĝoj en la malnova kategorio <em>ne</em> transiros en la novan kategorion.",
        "movenologintext": "Vi nepre estu registrita uzanto kaj [[Special:UserLogin|ensalutu]] por rajti movi paĝojn.",
        "movenotallowed": "Vi ne rajtas movi paĝojn.",
-       "movenotallowedfile": "Vi ne havas rajton alinomigi dosierojn.",
+       "movenotallowedfile": "Vi ne havas rajton alinomi dosierojn.",
        "cant-move-user-page": "Vi ne rajtas movi radikajn uzanto-paĝojn.",
        "cant-move-to-user-page": "Vi ne rajtas movi paĝon al uzantopaĝo (krom al uzantosubpaĝo).",
        "cant-move-category-page": "Vi ne rajtas movi kategoriajn paĝojn.",
        "cant-move-to-category-page": "Vi ne rajtas movi paĝon al kategoria paĝo.",
        "newtitle": "Al nova titolo",
        "move-watch": "Atenti ĉi tiun paĝon",
-       "movepagebtn": "Alinomigi paĝon",
+       "movepagebtn": "Alinomi paĝon",
        "pagemovedsub": "Sukcesis alinomigo",
-       "movepage-moved": "'''\"$1\" estis alinomigita al \"$2\"'''",
+       "movepage-moved": "<strong>\"$1\" estis alinomita al \"$2\"</strong>",
        "movepage-moved-redirect": "Alidirektilo estis kreita.",
        "movepage-moved-noredirect": "La kreado de alidirektilo estis nuligita.",
        "articleexists": "Paĝo kun tiu nomo jam ekzistas, aŭ la nomo kiun vi elektis ne validas.\nBonvolu elekti alian nomon.",
        "cantmove-titleprotected": "Vi ne povas movi paĝo al ĉi loko, ĉar la nova titolo estis protektita kontraŭ kreado",
-       "movetalk": "Transigi ankaŭ la \"diskuto\"-paĝon, se ĝi ekzistas.",
-       "move-subpages": "Alinomigi ĉiujn subpaĝojn (maksimume $1)",
-       "move-talk-subpages": "Alinomigi subpaĝojn de diskuto-paĝo (ĝis $1)",
+       "movetalk": "Alinomi ankaŭ la diskutopaĝon.",
+       "move-subpages": "Alinomi ĉiujn subpaĝojn (maksimume $1)",
+       "move-talk-subpages": "Alinomi subpaĝojn de diskuto-paĝo (maksimume $1)",
        "movepage-page-exists": "La paĝo $1 jam ekzistas kaj ne povas esti aŭtomate anstataŭigita.",
        "movepage-page-moved": "La paĝo $1 estis alinomita al $2.",
-       "movepage-page-unmoved": "La paĝo $1 ne povas esti alinomigita al $2.",
+       "movepage-page-unmoved": "La paĝo $1 ne povas esti alinomita al $2.",
        "movepage-max-pages": "La maksimumo de $1 {{PLURAL:$1|paĝo|paĝoj}} estis {{PLURAL:$1|alinomita|alinomitaj}} kaj neniuj pliaj estos alinomitaj aŭtomate.",
        "movelogpage": "Protokolo pri paĝmovoj",
        "movelogpagetext": "Jen listo de movitaj paĝoj",
        "movenosubpage": "Ĉi tiu paĝo havas neniujn subpaĝojn.",
        "movereason": "Kialo:",
        "revertmove": "restarigi",
-       "delete_and_move": "Forigi kaj alinomigi",
+       "delete_and_move": "Forigi kaj alinomi",
        "delete_and_move_text": "==Forigo nepras==\n\nLa celartikolo \"[[:$1]]\" jam ekzistas. Ĉu vi volas forigi ĝin por krei spacon por la movo?",
        "delete_and_move_confirm": "Jes, forigu la paĝon",
        "delete_and_move_reason": "Forigita por ebligi movadon de \"[[$1]]\"",
        "imagetypemismatch": "La nova dosierfinaĵo ne kongruas ĝian dosiertipon.",
        "imageinvalidfilename": "La cela dosiernomo estas nevalida",
        "fix-double-redirects": "Ĝisdatigi iujn alidirektilojn kiuj direktas al la originala titolo",
-       "move-leave-redirect": "Forlasi kiel alidirektilon",
+       "move-leave-redirect": "Forlasi alidirektilon",
        "protectedpagemovewarning": "'''Averto:''' Ĉi tiu paĝo estis ŝlosita tiel nur uzantoj kun administranto-rajtoj povas movi ĝin.\nJen la lasta protokolero por via referenco:",
        "semiprotectedpagemovewarning": "'''Averto:''' Ĉi tiu paĝo estis ŝlosita tiel ĝi estas nur movebla de registritaj uzantoj.\nJen la lasta protokolero por via referenco:",
        "move-over-sharedrepo": "== Dosiero ekzistas ==\n[[:$1]] ekzistas en komuna dosierujo. Movante la dosieron al ĉi tiu titolo anstataŭigos la komunan dosieron.",
        "importlogpage": "Protokolo de importaĵoj",
        "importlogpagetext": "Administrantecaj importoj de paĝoj kun redakto-historio de aliaj vikioj.",
        "import-logentry-upload": "importita [[$1]] de dosiera alŝuto",
-       "import-logentry-upload-detail": "$1 {{PLURAL:$1|versio|versioj}}",
+       "import-logentry-upload-detail": "$1 {{PLURAL:$1|revizio importita|revizioj importitaj}}",
        "import-logentry-interwiki": "transvikiigita $1",
        "import-logentry-interwiki-detail": "Importis $1 {{PLURAL:$1|revizion|reviziojn}} de $2",
        "javascripttest": "Ĝavoskripta testado",
        "revdelete-uname-unhid": "salutnomo malkaŝita",
        "revdelete-restricted": "aplikis limojn al administrantoj",
        "revdelete-unrestricted": "forigis limojn por administrantoj",
+       "logentry-merge-merge": "$1 {{GENDER:$2|kunigis}} $3 en $4 (revizioj ĝis $5)",
        "logentry-move-move": "$1 movis paĝon $3 al $4",
        "logentry-move-move-noredirect": "$1 movis paĝon $3 al $4 ne lasante alidirektilon",
        "logentry-move-move_redir": "$1 movis paĝon $3 al $4 anstataŭigante alidirektilon",
        "api-error-overwrite": "Anstataŭigo de ekzistanta dosiero ne permesatas.",
        "api-error-stashfailed": "Interna eraro: la servilo malsukcesis stoki provizoran dosieron.",
        "api-error-publishfailed": "Interna eraro: Servilo malsukcesis eldoni provizoran dosieron.",
+       "api-error-stasherror": "Eraro okazis dum alŝutado de la dosiero al dosierujo.",
        "api-error-timeout": "La servilo ne respondis ene de la antaŭvidita tempo.",
        "api-error-unclassified": "Okazis nekonata eraro",
        "api-error-unknown-code": "Nekonata eraro: \"$1\"",
        "logentry-pagelang-pagelang": "$1 {{GENDER:$2|ŝanĝis}} la paĝan lingvon por $3 de $4 al $5.",
        "default-skin-not-found-row-enabled": "* <code>$1</code> / $2 (ŝalta)",
        "default-skin-not-found-row-disabled": "* <code>$1</code> / $2 ('''malŝalta''')",
+       "mediastatistics": "Statistikoj pri dosieroj",
        "mediastatistics-nbytes": "{{PLURAL:$1|$1 bitoko|$1 bitokoj}} ($2; $3%)",
        "mediastatistics-table-mimetype": "MIME-tipo",
+       "mediastatistics-table-extensions": "Eblaj dosier-sufiksoj",
        "mediastatistics-table-count": "Nombro de dosieroj",
        "mediastatistics-header-unknown": "Nekonata",
        "mediastatistics-header-bitmap": "Rastrumaj bildoj",
        "mediastatistics-header-office": "Oficejaj",
        "mediastatistics-header-text": "Tekstaj",
        "mediastatistics-header-executable": "Plenumeblaj dosieroj",
+       "json-error-state-mismatch": "JSON estas malvalida aŭ malformigita",
        "json-error-syntax": "Sintaksa eraro"
 }
index 4ce5532..9aaf950 100644 (file)
        "filerenameerror": "No se pudo renombrar el archivo «$1» a «$2».",
        "filedeleteerror": "No se pudo borrar el archivo «$1».",
        "directorycreateerror": "No se pudo crear el directorio «$1».",
+       "directoryreadonlyerror": "La carpeta «$1» es de solo lectura.",
+       "directorynotreadableerror": "La carpeta «$1» no es legible.",
        "filenotfound": "No se pudo encontrar el archivo «$1».",
        "unexpected": "Valor inesperado: «$1»=«$2».",
        "formerror": "Error: no se pudo enviar el formulario",
        "protectedpagewarning": "'''Aviso: Esta página ha sido protegida de manera que solo usuarios con permisos de administrador puedan editarla.'''\nA continuación se muestra la última entrada de registro para referencia:",
        "semiprotectedpagewarning": "'''Nota:''' Esta página ha sido protegida para que solo usuarios registrados puedan editarla.\nA continuación se provee la última entrada de registro para referencia:",
        "cascadeprotectedwarning": "'''Aviso:''' Esta página está protegida, solo los administradores pueden editarla porque está incluida en  {{PLURAL:$1|la siguiente página protegida|las siguientes páginas protegidas}} en cascada:",
-       "titleprotectedwarning": "'''Aviso: Esta página está protegida de modo que se necesitan [[Special:ListGroupRights|permisos especificos]] para crearla.'''\nA continuación se muestra la última entrada de registro para referencia:",
+       "titleprotectedwarning": "<strong>Aviso: esta página está protegida de modo que se necesitan [[Special:ListGroupRights|permisos específicos]] para crearla.</strong>\nA continuación se muestra la última entrada del registro como referencia:",
        "templatesused": "{{PLURAL:$1|Plantilla usada|Plantillas usadas}} en esta página:",
        "templatesusedpreview": "{{PLURAL:$1|Plantilla usada|Plantillas usadas}} en esta previsualización:",
        "templatesusedsection": "{{PLURAL:$1|Plantilla usada|Plantillas usadas}} en esta sección:",
        "content-model-text": "Texto sin formato",
        "content-model-javascript": "JavaScript",
        "content-model-css": "CSS",
+       "duplicate-args-category": "Páginas que usan argumentos duplicados en invocaciones de plantillas",
+       "duplicate-args-category-desc": "La página contiene invocaciones de plantillas que utilizan argumentos duplicados, como <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> o <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "expensive-parserfunction-warning": "Aviso: Esta página contiene demasiadas llamadas a funciones sintácticas costosas (#ifexist: y similares)\n\nTiene {{PLURAL:$1|una llamada|$1 llamadas}}, pero debería tener menos de $2.",
        "expensive-parserfunction-category": "Páginas con llamadas a funciones sintácticas demasiado costosas",
        "post-expand-template-inclusion-warning": "Aviso: El tamaño de las plantillas incluidas es muy grande.\nAlgunas plantillas no serán incluidas.",
        "tooltip-pt-mycontris": "Lista de tus contribuciones",
        "tooltip-pt-login": "Te recomendamos iniciar sesión, sin embargo no es obligatorio",
        "tooltip-pt-logout": "Salir de la sesión",
+       "tooltip-pt-createaccount": "Te recomendamos crear una cuenta e iniciar sesión; sin embargo, no es obligatorio",
        "tooltip-ca-talk": "Discusión acerca del artículo",
        "tooltip-ca-edit": "Puedes editar esta página. Utiliza el botón de previsualización antes de guardar",
        "tooltip-ca-addsection": "Iniciar una sección nueva",
index 1556350..6a80627 100644 (file)
        "unprotect": "Muuda kaitset",
        "unprotectthispage": "Muuda selle lehekülje kaitset",
        "newpage": "Uus lehekülg",
-       "talkpage": "Selle artikli arutelu",
+       "talkpage": "Selle lehekülje arutelu",
        "talkpagelinktext": "arutelu",
        "specialpage": "Erilehekülg",
        "personaltools": "Personaalsed tööriistad",
        "tooltip-pt-login": "See pole küll kohustuslik, aga sul tasub sisse logida.",
        "tooltip-pt-logout": "Logi välja",
        "tooltip-pt-createaccount": "See pole küll kohustuslik, aga sul tasub konto luua ja sisse logida.",
-       "tooltip-ca-talk": "Selle artikli arutelu",
+       "tooltip-ca-talk": "Arutelu selle lehekülje sisu kohta",
        "tooltip-ca-edit": "Sa saad seda lehekülge muuta. Palun kasuta enne salvestamist eelvaadet.",
        "tooltip-ca-addsection": "Lisa uus alaosa",
        "tooltip-ca-viewsource": "See lehekülg on kaitstud.\nSaad vaadata selle lähteteksti.",
index 294d230..72e65e1 100644 (file)
@@ -23,8 +23,8 @@
                        "Arkaitz Barnetik"
                ]
        },
-       "tog-underline": "Loturak azpimarratu:",
-       "tog-hideminor": "Azken aldaketetan aldaketa txikiak ezkutatu",
+       "tog-underline": "Azpimarratu loturak:",
+       "tog-hideminor": "Ezkutatu azken aldaketetan aldaketa txikiak",
        "tog-hidepatrolled": "Ezkutatu patruilatutako aldaketa azken aldaketetan",
        "tog-newpageshidepatrolled": "Ezkutatu patruilatutako orriak, orri-zerrenda berritik",
        "tog-extendwatchlist": "Jarraipen-zerrenda zabaldu aldaketa guztiak ikusteko, ez bakarrik azken aldaketak",
        "thu": "Osg",
        "fri": "Osr",
        "sat": "Lar",
-       "january": "Urtarrila",
-       "february": "Otsaila",
-       "march": "Martxoa",
-       "april": "Apirila",
-       "may_long": "Maiatza",
-       "june": "Ekaina",
-       "july": "Uztaila",
-       "august": "Abuztua",
-       "september": "Iraila",
-       "october": "Urria",
-       "november": "Azaroa",
-       "december": "Abendua",
-       "january-gen": "Urtarril",
-       "february-gen": "Otsail",
-       "march-gen": "Martxo",
-       "april-gen": "Apiril",
-       "may-gen": "Maiatz",
-       "june-gen": "Ekain",
-       "july-gen": "Uztail",
-       "august-gen": "Abuztu",
-       "september-gen": "Irail",
-       "october-gen": "Urri",
-       "november-gen": "Azaro",
-       "december-gen": "Abendu",
-       "jan": "Urt",
-       "feb": "Ots",
-       "mar": "Mar",
-       "apr": "Api",
-       "may": "Mai",
-       "jun": "Eka",
-       "jul": "Uzt",
-       "aug": "Abu",
-       "sep": "Ira",
-       "oct": "Urr",
-       "nov": "Aza",
-       "dec": "Abe",
-       "january-date": "Urtarrilaren $1",
-       "february-date": "Otsailaren $1",
-       "march-date": "Martxoaren $1",
-       "april-date": "Apirilaren $1",
-       "may-date": "Maiatzaren $1",
-       "june-date": "Ekainaren $1",
-       "july-date": "Uztailaren $1",
-       "august-date": "Abuztuaren $1",
-       "september-date": "Irailaren $1",
-       "october-date": "Urriaren $1",
-       "november-date": "Azaroaren $1",
-       "december-date": "Abenduaren $1",
+       "january": "urtarrila",
+       "february": "otsaila",
+       "march": "martxoa",
+       "april": "apirila",
+       "may_long": "maiatza",
+       "june": "ekaina",
+       "july": "uztaila",
+       "august": "abuztua",
+       "september": "iraila",
+       "october": "urria",
+       "november": "azaroa",
+       "december": "abendua",
+       "january-gen": "urtarrilak",
+       "february-gen": "otsailak",
+       "march-gen": "martxoak",
+       "april-gen": "apirilak",
+       "may-gen": "maiatzak",
+       "june-gen": "ekainak",
+       "july-gen": "uztailak",
+       "august-gen": "abuztuak",
+       "september-gen": "irailak",
+       "october-gen": "urriak",
+       "november-gen": "azaroak",
+       "december-gen": "abenduak",
+       "jan": "urt",
+       "feb": "ots",
+       "mar": "mar",
+       "apr": "api",
+       "may": "mai",
+       "jun": "eka",
+       "jul": "uzt",
+       "aug": "abu",
+       "sep": "ira",
+       "oct": "urr",
+       "nov": "aza",
+       "dec": "abe",
+       "january-date": "urtarrilak $1",
+       "february-date": "otsailak $1",
+       "march-date": "martxoak $1",
+       "april-date": "apirilak $1",
+       "may-date": "maiatzak $1",
+       "june-date": "ekainak $1",
+       "july-date": "uztailak $1",
+       "august-date": "abuztuak $1",
+       "september-date": "irailak $1",
+       "october-date": "urriak $1",
+       "november-date": "azaroak $1",
+       "december-date": "abenduak $1",
        "pagecategories": "{{PLURAL:$1|Kategoria|Kategoriak}}",
        "category_header": "«$1» kategoriako artikuluak",
        "subcategories": "Azpikategoriak",
        "content-failed-to-parse": "Ezin izan da $2(r)en edukia parseatu $1 modeloarentzat: $3",
        "invalid-content-data": "Eduki datu baliogabea",
        "content-not-allowed-here": "\"$1\" edukia ez dago baimendua [[$2]] orrialdean",
-       "editwarning-warning": "Orrialde honetatik irteten bazara, egindako aldaketak galdu egingo dira.\nSaioa hasi baduzu, mezu hau kendu dezakezu zure hobespenen orrialdeko \"Aldatzen\" atalean.",
+       "editwarning-warning": "Orri honetatik irteten bazara, egindako aldaketak galdu egingo dira, beharbada.\nSaioa hasi baduzu, mezu hau kendu dezakezu zure hobespenen orriko «{{int:prefs-editing}}» atalean.",
+       "editpage-notsupportedcontentformat-title": "Eduki formatu hori ez da onartzen",
        "content-model-wikitext": "wikitestua",
        "content-model-text": "testu laua",
        "content-model-javascript": "JavaScript",
index 743f91f..76840fb 100644 (file)
        "filerenameerror": "Det datei $1 küd ei efter $2 amnäämd wurd.",
        "filedeleteerror": "Det datei $1 küd ei stregen wurd.",
        "directorycreateerror": "Det fertiaknis \"$1\" küd ei iinracht wurd.",
+       "directoryreadonlyerror": "Det auersicht \"$1\" as seekert jin't skriiwen.",
+       "directorynotreadableerror": "Det auersicht \"$1\" koon ei leesen wurd.",
        "filenotfound": "Det datei $1 küd ei fünjen wurd.",
        "unexpected": "Mä di wäärs stemet wat ei: \"$1\"=\"$2\".",
        "formerror": "Feeler: Di iindrach küd ei ferwerket wurd.",
        "viewsourcetext": "Dü könst di kweltekst faan det sidj uunluke an ham uk kopiare:",
        "viewyourtext": "Dü könst di code faan '''din feranrang''' faan detdiar sidj uunluke an kopiare:",
        "protectedinterface": "Üüb detdiar sidj stäänt tekst för det software faan detheer wiki an as seekert wurden, am dat näämen diar wat feranert.\nDü könst [//translatewiki.net/ translatewiki.net] faan MediaWiki brük, am auersaatangen för ale wiki projekten tu maagin.",
-       "editinginterface": "'''Paase üüb:''' Üüb detdiar sidj stäänt tekst, diar faan't MediaWiki software brükt woort. Wan dü diar wat feranerst, feranerst dü di skak faan't Nordfriisk Wikipedia.\nWan dü wat auersaat wel, maage det mä [//translatewiki.net/ translatewiki.net], det as det MediaWiki lokalisiarangsprojekt.",
+       "editinginterface": "<strong>Paase üüb:</strong> Üüb detdiar sidj stäänt tekst, diar faan't MediaWiki software brükt woort. Wan dü diar wat feranerst, feranerst dü di skak faan't Nordfriisk Wikipedia.",
+       "translateinterface": "Am auersaatangen föör aal a Wikis föörtunemen, gung tu [//translatewiki.net/ translatewiki.net], det as det MediaWiki-lokalisiarangsprojekt.",
        "cascadeprotected": "Detdiar sidj koon ei bewerket wurd. Hat as uun {{PLURAL:$1|detdiar sidj|jodiar sidjen}}\niinbünjen, diar auer kaskaadenseekerhaid seekert {{PLURAL:$1|as|san}}:\n$2",
        "namespaceprotected": "Dü heest ei det brükerrocht, am sidjen uun di nöömrüm '''$1''' tu bewerkin.",
        "customcssprotected": "Dü mutst detheer CSS sidj ei bewerke, auer det hoker ööders hiart.",
        "content-model-text": "normool tekst",
        "content-model-javascript": "JavaScript",
        "content-model-css": "CSS",
+       "duplicate-args-category": "Sidjen, diar dobelt argumenten uun föörlaagen aprep.",
+       "duplicate-args-category-desc": "Detdair sidj rept föörlaagen ap, diar dobelt argumenten brük, so üs <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> of <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "expensive-parserfunction-warning": "'''Paase üüb:''' Detdiar sidj brükt tuföl widjloftag server-funktjuunen.\n\nDiar mut ei muar üs {{PLURAL:$2|1|$2}} brükt wurd. Nü {{PLURAL:$1|woort diar 1|wurd diar $1}} brükt.",
        "expensive-parserfunction-category": "Sidjen mä tuföl parser-funktjuunen.",
        "post-expand-template-inclusion-warning": "'''Paase üüb:''' Enkelt föörlaagen san tu grat, jo kön ei uun det sidj iinbünjen wurd.",
        "search-result-category-size": "{{PLURAL:$1|1 sidj|$1 sidjen}} ({{PLURAL:$2|1 onerkategorii|$2 onerkategoriin}}, {{PLURAL:$3|1 datei|$3 datein}})",
        "search-redirect": "(widjerfeerd faan „$1“)",
        "search-section": "(kirew $1)",
+       "search-category": "(kategorii $1)",
        "search-file-match": "(fünjen tekst)",
        "search-suggest": "Mendst dü „$1“?",
        "search-interwiki-caption": "Saster-projekten",
        "pager-older-n": "{{PLURAL:$1|1 ääler|$1 ääler}}",
        "suppress": "Oversight",
        "querypage-disabled": "Detdiar spezial-sidj as ei aktiif, am det süsteem ei tu auerläästin.",
+       "apihelp": "Halep för API",
+       "apihelp-no-such-module": "Moduul \"$1\" ei fünjen.",
        "booksources": "Schük efter ISBN-numer",
        "booksources-search-legend": "Schük efter bukloodens",
        "booksources-search": "Schük",
        "wlheader-enotif": "Di e-mail siinst as aktiif.",
        "wlheader-showupdated": "Nei feranert sidjen wurd '''fäät''' uunwiset.",
        "wlnote": "Diar {{PLURAL:$1|stäänt det leetst feranrang|stun a leetst <strong>$1</strong> feranrangen}} faan a leetst {{PLURAL:$2|stünj|<strong>$2</strong> stünjen}}. Stant: $3, klook $4.",
-       "wlshowlast": "Wise a feranrangen faan leetst $1 stünjen, $2 daar of .",
+       "wlshowlast": "Wise a feranrangen faan a leetst $1 stünjen, $2 daar.",
        "watchlist-options": "Iinstelangen för't uunwisin",
        "watching": "Uun't uug behual ...",
        "unwatching": "Ei uun't uug behual ...",
        "tooltip-pt-mycontris": "List mä aanj bidracher",
        "tooltip-pt-login": "Wan dü di uunmeldest, heest dü muar mögelkhaiden. Dü säärst det oober ei.",
        "tooltip-pt-logout": "Ufmelde",
+       "tooltip-pt-createaccount": "Wees so gud an racht en brükerkonto iin an melde di uun. Dü säärst det oober ei.",
        "tooltip-ca-talk": "Diskuschuun auer di artiikel",
        "tooltip-ca-edit": "Sidj bewerke. Luke di det iarst ans uun, iar dü det seekerst.",
        "tooltip-ca-addsection": "Nei kirew began",
        "tooltip-feed-atom": "Atom-feed för detdiar sidj",
        "tooltip-t-contributions": "List mä bidracher faan didiar brüker uunluke",
        "tooltip-t-emailuser": "En e-mail tu didiar brüker schüür",
+       "tooltip-t-info": "Muar auer detdiar sidj",
        "tooltip-t-upload": "Datein huuchschüür",
        "tooltip-t-specialpages": "Auersicht auer aal a spezial-sidjen",
        "tooltip-t-print": "Drükföörskau",
        "unknown_extension_tag": "Ünbekäänd ''tag'' „$1“",
        "duplicate-defaultsort": "'''Paase üüb:''' Di sortiarkai \"$2\" auerskraft di ual sortiarkai \"$1\"",
        "duplicate-displaytitle": "<strong>Paase üüb:</strong> Di uunwiset tiitel \"$2\" auerskraft di ual tiitel \"$1\".",
+       "invalid-indicator-name": "<strong>Feeler:</strong> Det atribut <code>name</code> faan di sidjenstaatusindikaator mut ei leesag wees.",
        "version": "Werjuun",
        "version-extensions": "Instaliaret ütjwidjangen",
        "version-skins": "Instaliaret brükerskaker",
        "revdelete-uname-unhid": "brükernööm weder tu sen",
        "revdelete-restricted": "mögelkhaiden för administratooren wechnimen",
        "revdelete-unrestricted": "mögelkhaiden för administratooren ütjwidjet",
+       "logentry-merge-merge": "$1 {{GENDER:$2|hää}} $3 mä det sidj „$4“ (werjuunen bit tu di $5) tuupfeerd",
        "logentry-move-move": "$1 {{GENDER:$2}} hää det sidj $3 efter $4 fersköwen.",
        "logentry-move-move-noredirect": "$1 {{GENDER:$2}} hää det sidj $3 efter $4 saner widjerfeerang fersköwen.",
        "logentry-move-move_redir": "$1 {{GENDER:$2}} hää det sidj $3 efter $4 fersköwen an diarbi en widjerfeerang auerskrewen.",
        "api-error-stashfailed": "Intern feeler: Di server küd nian tidjwiis datei seekre.",
        "api-error-publishfailed": "Intern feeler: Di server küd det tidjwiis datei ei widjer schüür.",
        "api-error-stasherror": "Bi't huuchschüüren faan detdiar datei as wat skiaf gingen.",
+       "api-error-stashedfilenotfound": "Det datei, wat al ans seekert wurden as, küd bi't huuchloosin ei fünjen wurd.",
+       "api-error-stashpathinvalid": "Det steed, huar det seekert datei fünjen wurd skul, jaft at ei.",
+       "api-error-stashfilestorage": "Bi't seekrin faan detdiar datei uun a spiiker as wat skiaf gingen.",
+       "api-error-stashzerolength": "Di server küd det datei ei seekre, auer hat man bluas nul bytes grat as.",
+       "api-error-stashnotloggedin": "Dü skel uunmeldet wees, wan dü datein uun a spiiker seekre wel.",
+       "api-error-stashwrongowner": "Det datei, huar dü uun a spiiker üüb tugrip wulst, hiart di ei.",
+       "api-error-stashnosuchfilekey": "Di datei-kai, huar dü uun a spiiker üüb tugrip wulst, as ei diar.",
        "api-error-timeout": "Di server hää ei rochttidjag swaaret (time-out).",
        "api-error-unclassified": "Diar as irgentwat skiaf gingen.",
        "api-error-unknown-code": "Ünbekäänd feeler: „$1“",
        "mediastatistics-header-text": "Tekst",
        "mediastatistics-header-executable": "Datein tu ütjfeeren",
        "mediastatistics-header-archive": "Komprimiaret formaaten",
+       "json-warn-trailing-comma": "{{PLURAL:$1|En bihinget koma as|$1 bihinget komas san}} faan JSON wechnimen wurden.",
        "json-error-unknown": "Diar ging wat skiaf mä't JSON. Feeler: $1",
        "json-error-depth": "Det staabeljipde as tu grat.",
        "json-error-state-mismatch": "Ferkiard JSON",
index 6adc493..f0fcaac 100644 (file)
        "protectedpagetext": "Dizze side is befeilige. Bewurkjen is net mooglik.",
        "viewsourcetext": "Jo kinne de boarnetekst fan dizze side besjen en kopiearje:",
        "protectedinterface": "Dizze side jout systeemteksten fan 'e software en is befeilige tsjin misbrûk. Asto oersettingen foar alle wiki's tafoegje of bewurkje wolst, kinsto [//translatewiki.net/ translatewiki.net] brûke.",
-       "editinginterface": "'''Tink derom;''' Jo bewurkje in side dy't brûkt wurdt foar systeemteksten foar de software. Bewurkings op dizze side beynfloedzje de brûkersynterface fan elkenien. Asto wol oersettingen tafoegje of bewurkje wolst kinsto  [//translatewiki.net/wiki/Main_Page?setlang=fy translatewiki.net] brûke, it oersetprojekt foar MediaWiki.",
+       "editinginterface": "<strong>Warskôging:</strong> Jo bewurkje in side dy't brûkt wurdt foar systeemteksten foar de software.\nBewurkings op dizze side beynfloedzje de brûkersynterface fan elkenien.",
        "cascadeprotected": "Dizze side is skoattele tsjin wizigjen, om't der in ûnderdiel útmakket fan de neikommende {{PLURAL:$1|side|siden}}, dy't skoattele {{PLURAL:$1|is|binne}} mei de \"ûnderlizzende siden\" opsje ynskeakele: $2",
        "namespaceprotected": "Jo hawwe gjin rjochten om siden yn'e nammerûmte '''$1''' te bewurkjen.",
        "ns-specialprotected": "Siden yn'e nammerûmte {{ns:special}} kinne net bewurke wurde.",
        "emailconfirmlink": "Befêstigje jo netpostadres.",
        "invalidemailaddress": "It e-mailadres is net akseptearre om't it in ûnjildige opmaak hat.\nJou beleaven in jildich e-mailadres op of lit it fjild leech.",
        "accountcreated": "Brûker oanmakke",
-       "accountcreatedtext": "De brûker $1 is oanmakke.",
+       "accountcreatedtext": "De brûkersaccount foar [[{{ns:User}}:$1|$1]] ([[{{ns:User talk}}:$1|oerlis]]) is oanmakke.",
        "createaccount-title": "Brûkers registrearje foar {{SITENAME}}",
        "createaccount-text": "Immen hat in brûker op {{SITENAME}} ($4) oanmakke mei de namme \"$2\" en jo e-mailadres. It wachtwurd foar \"$2\" is \"$3\". Meld jo oan en feroarje jo wachtwurd.\n\nNegearje it berjocht as dizze brûker sûnder jo meiwitten oanmakke is.",
        "login-throttled": "Jo hawwe koartlyn te faak besocht oan te melden mei in ûnkrekt wachtwurd.\nJo moatte efkes wachtsje foar't jo it op'e nij besykje kinne.",
        "newpassword": "Nij wachtwurd",
        "retypenew": "Nij wachtwurd (nochris)",
        "resetpass_submit": "Wachtwurd ynstelle en oanmelde",
-       "changepassword-success": "Jo wachtwurd is feroare. Dwaande mei oanmelden ...",
+       "changepassword-success": "Jo wachtwurd is feroare.",
        "resetpass_forbidden": "Wachtwurden kinne net feroare wurde",
        "resetpass-no-info": "Jo moatte oanmeld wêze foar't Jo dizze side brûke kinne.",
        "resetpass-submit-loggedin": "Wachtwurd feroarje",
        "headline_tip": "Underkopke",
        "nowiki_sample": "Foechje hjir platte tekst yn",
        "nowiki_tip": "Negearje it wiki formaat",
+       "image_sample": "Foarbyld.jpg",
        "image_tip": "Mediatriem",
        "media_tip": "Link nei triem",
        "sig_tip": "Jo hântekening mei dei en oere",
        "recentchanges-label-minor": "Dit is in tekstwiziging",
        "recentchanges-label-bot": "Dizze wiziging is troch in robot makke",
        "recentchanges-label-unpatrolled": "Dizze wiziging is noch net neisjûn",
+       "recentchanges-legend-heading": "'''Leginda:'''",
        "recentchanges-legend-newpage": "$1 - nije side",
        "rcnotefrom": "Dit binne de feroarings sûnt <b>$2</b> (maksimaal <b>$1</b>).",
        "rclistfrom": "Jou nije feroarings, begjinnende mei $3 $2",
        "filehist-nothumb": "Gjin miniatuerôfbylding",
        "filehist-user": "Meidogger",
        "filehist-dimensions": "Ofmjittings",
-       "filehist-filesize": "Triem grutte",
+       "filehist-filesize": "Triemgrutte",
        "filehist-comment": "Opmerkings",
        "imagelinks": "Ofbyldkeppelings",
        "linkstoimage": "Dizze {{PLURAL:$1|side is|$1 siden binne}} keppele oan it ôfbyld:",
        "sp-deletedcontributions-contribs": "bydragen",
        "linksearch": "Eksterne ferwizings sykje",
        "linksearch-pat": "Sykpatroan:",
-       "linksearch-ns": "Nammerûmte:",
+       "linksearch-ns": "Nammeromte:",
        "linksearch-ok": "Sykje",
        "linksearch-text": "Wildcards lykas \"*.wikipedia.org\" of \"*.org\" binne tastien.<br />\nStipe protokollen: <code>$1</code>",
        "linksearch-line": "$1 hat in ferwizing yn $2",
        "listgrouprights-removegroup": "Kin brûkers út dizze {{PLURAL:$2|groep|groepen}} fuorthelje: $1",
        "listgrouprights-addgroup-all": "Kin brûkers oan alle groepen tafoegje",
        "listgrouprights-removegroup-all": "Kin brûkers út alle groepen fuorthelje",
+       "listgrouprights-namespaceprotection-namespace": "Nammeromte",
        "mailnologin": "Gjin adres beskikber",
        "mailnologintext": "Jo moatte [[Special:UserLogin|oanmelden]] wêze, en in jildich e-postadres [[Special:Preferences|ynsteld]] hawwe, om oan oare meidoggers e-post stjoere te kinnen.",
        "emailuser": "Skriuw meidogger",
        "fileduplicatesearch-result-1": "De triem \"$1\" hat gjin duplikaten.",
        "fileduplicatesearch-result-n": "De triem \"$1\" hat {{PLURAL:$2|1 duplikaat|$2 duplikaten}}.",
        "specialpages": "Bysûndere siden",
+       "specialpages-note-top": "Leginda",
        "specialpages-note": "* Normale bysûndere siden.\n* <strong class=\"mw-specialpagerestricted\">Beheinde bysûndere siden.</strong>",
        "specialpages-group-maintenance": "Underhâld siden",
        "specialpages-group-other": "Oare bysûndere siden",
index 85fc18e..adae3ef 100644 (file)
        "shown-title": "הצגת {{PLURAL:$1|תוצאה אחת|$1 תוצאות}} בדף",
        "viewprevnext": "צפייה ב: ($1 {{int:pipe-separator}} $2) ($3)",
        "searchmenu-exists": "<strong>קיים דף בשם \"[[:$1]]\" באתר הוויקי הזה.</strong> {{PLURAL:$2|0=|ר' גם את הדפים האחרים שנמצאו בחיפוש.}}",
-       "searchmenu-new": "<strong>×\9c×\99צ×\99רת ×\94×\93×£ \"[[:$1]]\" ×\91×\90תר ×\94×\95×\95×\99ק×\99 ×\94×\96×\94.</strong> {{PLURAL:$2|0=|ר' ×\92×\9d ×\90ת ×\94×\93×£ ×©× ×\9eצ×\90 ×\91×\97×\99פ×\95ש.|ר' ×\92×\9d ×\90ת ×\94×\93פ×\99×\9d ×©× ×\9eצ×\90×\95 ×\91×\97×\99פ×\95ש.}}",
+       "searchmenu-new": "<strong>יצירת הדף \"[[:$1]]\" באתר הוויקי הזה.</strong> {{PLURAL:$2|0=|ר' גם את הדף שנמצא בחיפוש.|ר' גם את הדפים שנמצאו בחיפוש.}}",
        "searchprofile-articles": "דפי תוכן",
        "searchprofile-images": "מולטימדיה",
        "searchprofile-everything": "הכול",
index 2ee1fb0..6629e38 100644 (file)
        "recentchangeslinked": "Povezane stranice",
        "recentchangeslinked-feed": "Povezane stranice",
        "recentchangeslinked-toolbox": "Povezane stranice",
-       "recentchangeslinked-title": "Povezane promjene sa \"$1\"",
+       "recentchangeslinked-title": "Povezane promjene sa stranicom \"$1\"",
        "recentchangeslinked-summary": "Ova posebna stranica pokazuje nedavne promjene na povezanim stranicama (ili stranicama određene kategorije). Stranice koje su na [[Special:Watchlist|Vašem popisu praćenja]] su '''podebljane'''.",
        "recentchangeslinked-page": "Naslov stranice:",
        "recentchangeslinked-to": "Pokaži promjene na stranicama s poveznicom na ovu stranicu",
index d853049..860c23e 100644 (file)
        "filerenameerror": "Impossibile renominar file \"$1\" a \"$2\".",
        "filedeleteerror": "Impossibile deler file \"$1\".",
        "directorycreateerror": "Impossibile crear le directorio \"$1\".",
+       "directoryreadonlyerror": "Le directorio \"$1\" es protegite contra scriptura.",
+       "directorynotreadableerror": "Le directorio \"$1\" non es legibile.",
        "filenotfound": "Impossibile trovar file \"$1\".",
        "unexpected": "Valor impreviste: \"$1\"=\"$2\".",
        "formerror": "Error: impossibile submitter formulario",
index ce7f8db..7282150 100644 (file)
        "pageswithprop-text": "Halaman ini berisi daftar halaman yang menggunakan properti halaman tertentu.",
        "pageswithprop-prop": "Nama properti:",
        "pageswithprop-submit": "Lanjut",
-       "pageswithprop-prophidden-long": "nilai properti teks panjang tersembunyi ($1 kilobita)",
-       "pageswithprop-prophidden-binary": "nilai properti biner tersembunyi ($1 kilobita)",
+       "pageswithprop-prophidden-long": "nilai properti teks panjang tersembunyi ($1)",
+       "pageswithprop-prophidden-binary": "nilai properti biner tersembunyi ($1)",
        "doubleredirects": "Pengalihan ganda",
        "doubleredirectstext": "Halaman ini memuat daftar halaman yang dialihkan ke halaman pengalihan yang lain.\nSetiap baris memuat pranala ke pengalihan pertama dan pengalihan kedua serta target dari pengalihan kedua yang umumnya adalah halaman yang \"sebenarnya\". Halaman peralihan pertama seharusnya dialihkan ke halaman yang bukan merupakan halaman peralihan.\nNama yang telah <del>dicoret</del> berarti telah dibetulkan.",
        "double-redirect-fixed-move": "[[$1]] telah dipindahkan.\nKami telah memperbaruinya secara otomatis dan sekarang menjadi halaman peralihan ke [[$2]].",
index bd432a1..0cfd5f8 100644 (file)
        "logentry-rights-rights": "მომხმარებელმა $1 {{GENDER:$2|შეცვალა}} ჯგუფის წევრობა $3-თვის $4-დან $5-ზე",
        "logentry-rights-rights-legacy": "მომხმარებელმა $1 {{GENDER:$2|შეცვალა}} ჯგუფის წევრობა $3-თვის",
        "logentry-rights-autopromote": "მომხმარებელი $1 ავტომატურად იქნა {{GENDER:$2|გადაყვანილი}} $4–დან $5–ში",
-       "logentry-upload-overwrite": "$1 {{GENDER:$2|ატვირთა}} $3-ის ახალი ვერსია",
+       "logentry-upload-upload": "მომხმარებელმა $1 {{GENDER:$2|ატვირთა}} $3",
+       "logentry-upload-overwrite": "მომხმარებელმა $1 {{GENDER:$2|ატვირთა}} $3-ის ახალი ვერსია",
+       "logentry-upload-revert": "მომხმარებელმა $1 {{GENDER:$2|ატვირთა}} $3",
        "rightsnone": "(არცერთი)",
        "revdelete-summary": "ცვლილებების აღწერა",
        "feedback-bugornote": "თუ თქვენ მზად ხართ დეტალურად აღწეროთ ტექნიკური პრობლემა, გთხოვთ, [$1 შეგვატყობინეთ შეცდომის შესახებ].\nწინააღმდეგ შემთხვევაში თქვენ შეგიძლიათ ისარგებლოთ ამ მარტივი ფორმით. თქვენი კომენტარი დაემატება  „[$3 $2]“ გვერდზე თქვენი მომხმარებლის სახელთან და გამოყენებულ ბრაუზერთან ერთად.",
index fdf1330..6a49891 100644 (file)
        "viewyourtext": "Осы беттен <strong>өңдемелеріңіздің</strong> қайнарын қарай және көшіре аласыз.",
        "protectedinterface": "Бұл бет осы уикидің бағдарламалық жасақтамасы үшін интерфейс мәтінін қамтамасыз етеді және қиянаттауды болдырмау үшін қорғалған. Барлық уикилер үшін аудармаларды қосу немесе өзгерту үшін [//translatewiki.net/ translatewiki.net] MediaWiki жерсіндіру жобасын қолданыңыз.",
        "editinginterface": "<strong>Ескерту:</strong> Бағдарламалық жасақтаманың тілдесу мәтінін жетістіретін бетін өңдеп жатырсыз.\nБұл беттің өзгертілуі басқа қатысушыларға пайдаланушылық интерфейсін қалай көрінетіне әсер етеді.\nБарлық уикилер үшін аудармаларды өзгерту немесе қосу үшін [//translatewiki.net/ translatewiki.net] МедиаУики жерсіндіру жобасын пайдаланыңыз.",
+       "translateinterface": "Барлық уикилерге аудармаларды қосу немесе өзгерту үшін [//translatewiki.net/ translatewiki.net] МедиаУики жерсіндіру жобасын қолданыңыз.",
        "cascadeprotected": "Бұл бет өңдеуден қорғалған, себебі бұл келесі «баулы қорғауы» қосылған {{PLURAL:$1|бетке|беттерге}} кірістірілген:\n$2",
        "namespaceprotected": "<strong>$1</strong> есім кеңістігіндегі беттерді өңдеу рұқсатыңыз жоқ.",
        "customcssprotected": "Сіздің бұл CSS бетін өңдеуге рұқсатыңыз жоқ, себебі мұнда өзге қатысушының жеке баптауларынан тұрады.",
        "search-result-category-size": "{{PLURAL:$1|1 мүше|$1 мүше}} ({{PLURAL:$2|1 санатша|$2 санатша}}, {{PLURAL:$3|1 файл|$3 файл}})",
        "search-redirect": "(айдағыш $1)",
        "search-section": "(бөлім $1)",
+       "search-category": "(Санат:$1)",
        "search-suggest": "Мүмкін осы болар: $1",
        "search-interwiki-caption": "Бауырлас жобалар",
        "search-interwiki-default": "$1 дегеннен нәтиже:",
        "userrights-lookup-user": "Қатысушы топтарын реттеу",
        "userrights-user-editname": "Қатысушы атын енгізіңіз:",
        "editusergroup": "Қатысушы топтарын өңдеу",
-       "editinguser": "'''[[User:$1|$1]]''' $2 есімді қатысушының құқықтарын өзгерту",
+       "editinguser": "<strong>[[User:$1|$1]]</strong> $2 есімді қатысушының құқықтарын өзгерту",
        "userrights-editusergroup": "Қатысушы топтарын өңдеу",
        "saveusergroups": "Қатысушы топтарын сақтау",
        "userrights-groupsmember": "Мүшелігі:",
        "emailuser-title-notarget": "Қатысушы е-поштасы",
        "emailpage": "Қатысушыға хат жазу",
        "emailpagetext": "Төмендегі пішін арқылы бұл {{GENDER:$1|қатысушыға}} е-пошта хабарламасын жөнелтуге болады.\n[[Special:Preferences|Қатысушы баптауыңызда]] енгізген е-пошта мекенжайыңыз «Кімнен» деген бас жолағында көрінеді, сондықтан хат алушысы тура жауап бере алады.",
-       "defemailsubject": "\"$1\" есімді қатысушының {{SITENAME}} е-поштасының хаты",
+       "defemailsubject": "«$1» есімді қатысушының {{SITENAME}} е-поштасының хаты",
        "usermaildisabled": "Қатысушының электронды поштасы қосылмаған",
        "usermaildisabledtext": "Бұл уикиде басқа қатысушыларға хат жібере алмайсыз",
        "noemailtitle": "Еш е-пошта мекенжайы жоқ",
        "ipb-unblock-addr": "$1 дегенді бұғаттауынан босату",
        "ipb-unblock": "Қатысушы атын немесе IP мекенжайын бұғаттамау",
        "ipb-blocklist": "Бұғатталғандарды қарау",
-       "ipb-blocklist-contribs": "$1 есімді қатысушының үлесі",
+       "ipb-blocklist-contribs": "{{GENDER:$1|$1}} есімді қатысушының үлесі",
        "unblockip": "Қатысушыны бұғаттауынан босату",
        "unblockiptext": "Төмендегі форманы IP мекенжайымен не қатысушы есімімен алдын-ала бұғатталған қатысушыға жазу рұқсатын қалпына келтіріу үшін қолданыңыз.",
        "ipusubmit": "Осы бұғаттауды алып тастау",
        "blocklog-showlog": "Бұл қатысушы ұдайы бұғатталып отырған.\nДерек үшін төменде бұғатталу журналы берілген:",
        "blocklog-showsuppresslog": "Бұл қатысушы ұдайы жасырылып және бұғатталып отырған.\nДерек үшін төменде жасыру журналы берілген:",
        "blocklogentry": "[[$1]] дегенді $2 мерзімге бұғаттады $3",
+       "reblock-logentry": "[[$1]] дегеннің бұғатталу мерзімінің аяқталуын $2 $3 дегенге өзгертті.",
        "blocklogtext": "Бұл қатысушыларды бұғаттау және бұғаттауынан босату әрекеттерінің журналы.\nӨздіктік бұғатталған IP мекенжайлар тізімделмеген.\nҚазіргі уақыттағы белсенді тиымдар мен бұғаттауларды [[Special:BlockList|бұғаттау тізімінен]] қараңыз.",
        "unblocklogentry": "$1 есімді қатысушыны бұғаттауынан босатты",
        "block-log-flags-anononly": "тек аноним қатысушылар",
index 27d0d11..66599ff 100644 (file)
        "allmessages-filter-all": "ejaal",
        "allmessages-filter-modified": "heh em Wiki jeändert",
        "allmessages-prefix": "Name fängk aan met:",
-       "allmessages-language": "Schprooch:",
+       "allmessages-language": "Schprohch:",
        "allmessages-filter-submit": "Lohß Jonn!",
        "allmessages-filter-translate": "Övversäze!",
        "thumbnail-more": "Jrößer aanzeije",
        "exif-objectcycle": "De Daachszick, för wann dat Denge zom Verdeile jedaach es",
        "exif-contact": "Kuntak",
        "exif-writer": "Schriiver",
-       "exif-languagecode": "Schprooch",
+       "exif-languagecode": "Schprohch",
        "exif-iimversion": "Dem <i lang=\"en\">IIM</i> sing Version",
        "exif-iimcategory": "Saachjrupp udder Zoot",
        "exif-iimsupplementalcategory": "Extra Saachjroppe udder Zoote",
index 1cae24b..02a67f3 100644 (file)
        "mediastatistics-table-extensions": "Méiglech Erweiderungen",
        "mediastatistics-table-count": "Zuel vun de Fichieren",
        "mediastatistics-header-unknown": "Onbekannt",
+       "mediastatistics-header-bitmap": "Bitmap-Biller",
+       "mediastatistics-header-drawing": "Zeechnungen (Vektorbiller)",
        "mediastatistics-header-audio": "Audio",
        "mediastatistics-header-video": "Videoen",
        "mediastatistics-header-office": "Office",
index 576fedb..f609c72 100644 (file)
        "tog-editondblclick": "بلگيا نه وا دوبار پورنين ويرايشت بكيد",
        "tog-editsectiononrightclick": "بهر ویرایشت نه وا راس کلیک کردن د بهر عنوانیا فعال کو",
        "tog-watchcreations": "بلگیایی که مه راس کمه و فایلیایی که مه سوار کمه اضاف کو د سیل برگه مه",
-       "tog-watchdefault": "بلگیا و فایلایی که مه ویرایشت کمه اضاف کو د سیل برگم",
-       "tog-watchmoves": "بلگیاو فایلیایی که مه جاوه جا کمه د سیل برگم اضاف کو",
-       "tog-watchdeletion": "بلگیا و فایلایی که مه پاک کمه اضاف کو د سیل برگم",
+       "tog-watchdefault": "بلگیا و جانیایی که مه ویرایشت کمه اضاف کو د سیل برگم",
+       "tog-watchmoves": "بلگیاو جانیایی  که مه جاوه جا کمه د سیل برگم اضاف کو",
+       "tog-watchdeletion": "بلگیا و جانیایی که مه پاک کمه اضاف کو د سیل برگم",
        "tog-watchrollback": "همه بلگه یا نه د جایی که مه د سیل برگم می کم اضاف کو.",
        "tog-minordefault": "همه ویرایشتیا کؤچک نه وا پیش فرض بیئن نشو دار کو.",
        "tog-previewontop": "پیش سیل نه دما جعوه ویرایشت نشو بیئه",
        "tog-previewonfirst": "پیش سیل نه د اولین ویرایشت نشو بیئه",
        "tog-enotifwatchlistpages": "اوسه که یه گل بلگه یا فایلی د سیل برگ مه آلشت بوئه منه وا ایمیل خور کو",
        "tog-enotifusertalkpages": "وختی که بلگه گپسن کاریار آلشت پیدا کرد منه وا ایمیل خور کو",
-       "tog-enotifminoredits": "همچنو اوسه که ویرایشتیا کؤچکی د بلگیا یا فایلیا انجوم بوئه منه خور کو",
-       "tog-enotifrevealaddr": "نشونی ایمیل منه د ایمیل اشگار نشو بیه",
+       "tog-enotifminoredits": "همچنو اوسه که ویرایشتیا کؤچکی د بلگیا یا جانیایا انجوم بوئه منه خور کو",
+       "tog-enotifrevealaddr": "نشونی ایمیل منه د انجومانامه اشگار نشو بیه",
        "tog-shownumberswatching": "انازه کاریاریایی که د حالت دیئنن نشو بیه",
        "tog-oldsig": "امضايی هيئش:",
        "tog-fancysig": "وا امضا چی ویکی متن برخورد کو",
        "tog-watchlisthideown": "قام كو ويرايشت منه د",
        "tog-watchlisthidebots": "ویرایشت یا بوت نه د سیل برگ قام کو",
        "tog-watchlisthideminor": "قام كو ويرايشت کؤچک منه د",
-       "tog-watchlisthideliu": "ویرایشت یا کاریاریا وامئن سیستم نه د سیل برگ قام کو",
-       "tog-watchlisthideanons": "Ù\88Û\8cراÛ\8cشت Û\8cا Ú©Ø§Ø±Ù\88رÛ\8cا Ù\86اشÙ\86اس نه د سیل برگ قام کو",
+       "tog-watchlisthideliu": "ویرایشت یا کاریاریا وامئن سامونه نه د سیل برگ قام کو",
+       "tog-watchlisthideanons": "Ù\88Û\8cراÛ\8cشت Û\8cا Ú©Ø§Ø±Ù\88رÛ\8cا Ù\86ادÛ\8cار نه د سیل برگ قام کو",
        "tog-watchlisthidepatrolled": "ویرایش تیا د تی رس نه د سیل برگ قام کو",
        "tog-ccmeonemails": "کپی انجومانامه یا منه که سی کاریاریا تر می فرسنم سیم کل کو",
-       "tog-diffonly": "بÙ\84Ú¯Û\8cاÛ\8cÛ\8c Ú©Ù\87 Ø´Ù\88Ù\85Ù\84 فرخیا هارن نشون نیه",
+       "tog-diffonly": "بÙ\84Ú¯Û\8cاÛ\8cÛ\8c Ú©Ù\87 Ø¯ Ù\88ر Ú¯Ø±ØªÙ\87 فرخیا هارن نشون نیه",
        "tog-showhiddencats": "دسه يا قام بيئنه نشون بيه",
        "tog-norollbackdiff": "فرخیا نه د بین بوریت نها یه گل عقو گرد کردن",
-       "tog-useeditwarning": "وختی که آلشتیا ذخیره نبیه د بلگه ویرایشت وه جا می نم خورم کو",
-       "tog-prefershttps": "همیشه وختی که مه وامئن هئم د ارتواط امن استفاده کو",
+       "tog-useeditwarning": "د گاتی که آلشتیا ذخیره نبیه د بلگه ویرایشت وه جا می نم خورم کو",
+       "tog-prefershttps": "همیشه د گاتی که مه وامئن هئم د ارتواط امن استفاده کو",
        "underline-always": "هميشه",
        "underline-never": "هيژوخت",
-       "underline-default": "پوسه یا مرورگر پیش فرض",
+       "underline-default": "پوسه یا دوارته نیئر پیش فرض",
        "editfont-style": "راساگه فونت شلک نه ویرایشت کو",
-       "editfont-default": "مرورگر پیش بینی بیه",
+       "editfont-default": "دوارته نیئر پیش بینی بیه",
        "editfont-monospace": "فونت تک بلگه ای",
        "editfont-sansserif": "سان سریف فونت",
        "editfont-serif": "فونت سريف",
        "category_header": "بلگيا مئن دسه \"$1\"",
        "subcategories": "زيردسه يا",
        "category-media-header": "رسانه د دسه \"$1\"",
-       "category-empty": "اÛ\8c Ø¯Ø³Ù\87 Ù\88اÙ\82عÙ\86 Ø´Ù\88Ù\85Ù\84 Ù\87Û\8cÚ\98 Ø¨Ù\84Ú¯Ù\87 Ø§Û\8c Û\8cا Ø±Ø³Ø§Ù\86Ù\87 ای نی",
+       "category-empty": "اÛ\8c Ø¯Ø³Ù\87 Ù\88اÙ\82عÙ\86 Ø¯Ù\87 Ù\88ر Ú¯Ø±ØªÙ\87 Ù\87Û\8cÚ\98 Ø¨Ù\84Ú¯Ù\87 Ø§Û\8c Û\8cا Ù\88ارسگر ای نی",
        "hidden-categories": "{{PLURAL:$1|دسته قام بيه|دسته يا قام بيه}}",
        "hidden-category-category": "دسه یا قام بیه",
        "category-subcat-count": "{{جمی:$2|ای دسه فقط زیر دسه دینداگر هان دش .|ای دسه {{جمی:$1| زیردسه|$1 زیردسه یا}}هئ , خارج د $2 کل.}}",
-       "category-subcat-count-limited": "ای دسه وا دمال {{جمی:$1|زیردسه|$1زیردسه یا}} بوئه",
-       "category-article-count": "{{جÙ\85Û\8c:$2|اÛ\8c Ø¯Ø³Ù\87 Ø´Ù\88Ù\85Ù\84 بلگه نهاییه .| {{جمی:$1| بلگه هئ|$1 بلگیا هئن}} د ای دسه, خارج د $2 کل.}}",
+       "category-subcat-count-limited": "ای دسه وا دم {{جمی:$1|زیردسه|$1زیردسه یا}} بوئه",
+       "category-article-count": "{{جÙ\85Û\8c:$2|اÛ\8c Ø¯Ø³Ù\87 Ø¯Ù\87 Ù\88ر Ú¯Ø±ØªÙ\87 بلگه نهاییه .| {{جمی:$1| بلگه هئ|$1 بلگیا هئن}} د ای دسه, خارج د $2 کل.}}",
        "category-article-count-limited": "نها {{جمی:$1|بلگه هئ|$1بلگیا هئن}} د دسه ایسنی .",
        "category-file-count": "{{جمی:$2|ای دسه فقط شامل فایل نهایی هئ file.| نهایی {{جمی:$1|فایل هئ|$1 فایلیا هئن}} د ای دسه, وه در د کل $2 .}}",
        "category-file-count-limited": " {{جمی:$1|[جانیا هئ|1$جانیایا هئن}}نهایی هان د دسه ایسنی.",
        "broken-file-category": "بلگیایی که هوم پیوند فایلیا اشکسه دارن",
        "categoryviewer-pagedlinks": "($1) ($2)",
        "about": "دباره",
-       "article": "محتوا بلگه",
+       "article": "مینونه بلگه",
        "newwindow": "(نيمدری  تازه وا کو)",
        "cancel": "انجوم شیوسن",
        "moredotdotdot": "بيشتر",
-       "morenotlisted": "اÛ\8c Ù\84Ù\8aست كامل نبيه",
+       "morenotlisted": "اÛ\8c Ù\86Ù\88Ù\85جا كامل نبيه",
        "mypage": "بلگه",
        "mytalk": "چك چنه",
-       "anontalk": "دباره نشونی ای آی پی قصه بكيد",
-       "navigation": "ناوگشتن",
+       "anontalk": "دباره تیرنشون ای آی پی قصه بكيد",
+       "navigation": "ناوجوری",
        "and": "&#32;و",
        "qbfind": "فهمسن،جسن",
        "qbbrowse": "قرض گرتن",
        "searcharticle": "رو",
        "history": "ويرگار بلگه",
        "history_short": "ويرگار",
-       "updatedmarker": "د آخرین دیئن مه روزآمد کو",
+       "updatedmarker": "د آخرین دیئن مه وه هنگوم سازی کو",
        "printableversion": "نسقه چاپ بيئنی",
-       "permalink": "چسب ون هميشئی",
+       "permalink": "هوم پیوند هميشئی",
        "print": "چاپ كردن",
        "view": "ديئن",
        "view-foreign": "د $1 نه بوینیت",
        "editthispage": "ويرايشت ای بلگه",
        "create-this-page": "راس كردن ای بلگه",
        "delete": "حذف كردن",
-       "deletethispage": "ای بلگه نه حذف بكيد",
-       "undeletethispage": "ای بلگه نه حذف نكيد",
+       "deletethispage": "ای بلگه نه پاکسا بكيد",
+       "undeletethispage": "ای بلگه نه پاکسا نكيد",
        "undelete_short": "زنه کردن {{جمی:$1|یه گل ویرایشت|$1 ویرایشتیا}}",
        "viewdeleted_short": "بوینیت {{[جمی:$1|یه گل ویرایشت پاکسا بیه|$1ویرایشتیا پاکسا بیه}}",
-       "protect": "حمايت بكيد",
+       "protect": "پر و پیم بكيد",
        "protect_change": "آلشت بكيد",
        "protectthispage": "ای بلگه نه حفاظت بكيد",
        "unprotect": "حمايت آلشت بكيد",
        "redirectto": "واگردونی سی:",
        "lastmodifiedat": "ای بلگه تازه ايا وضع آلشت بيه د $1, د $2.",
        "viewcount": "ای بلگه قاول دسترسی بيه {{PLURAL:$1|once|$1 times}}.",
-       "protectedpage": "بلگه حفاظت بيه",
+       "protectedpage": "بلگه پر و پیم بيه",
        "jumpto": "پئرستن د",
-       "jumptonavigation": "ناوگشتن",
+       "jumptonavigation": "ناوجوری",
        "jumptosearch": "پی جوری",
        "view-pool-error": "د بدبختی،ایسنی سروریا فره شلوغ.\nکاریاریا فره زیادی میهان ای بلگه نه بوینن.\nیه گری صب بکیتو دما یه که میهات دوواره ای بلگه نه بوینیت.",
        "generic-pool-error": "د بدبختی،ایسنی سروریا فره شلوغ.\nکاریاریا فره زیادی میهان ای بلگه نه بوینن.\nیه گری صب بکیتو دما یه که میهات دوواره ای بلگه نه بوینیت.",
-       "pool-timeout": "وخت سی تیه وه ره منن سی قلف بیئن تموم بی",
-       "pool-queuefull": "ذخÛ\8cرÙ\87 گی گرتن پر بیه",
-       "pool-errorunknown": "خطا Ù\86اشÙ\86اس",
+       "pool-timeout": "گات سی تیه وه ره منن سی قلف بیئن تموم بی",
+       "pool-queuefull": "اÙ\85اÛ\8cÛ\8cÙ\87 Ú©Ø§Ø±Û\8c گی گرتن پر بیه",
+       "pool-errorunknown": "خطا Ù\86ادÛ\8cار",
        "pool-servererror": "پول سنتر خذمتگه د دسرس نئ($1).",
        "aboutsite": "دباره {{SITENAME}}",
        "aboutpage": "پروجه:دباره",
-       "copyright": "محتوا د دسرس هئ سی $1 مر وه شلک هنی نوشته بوئه",
+       "copyright": "مینونه د دسرس هئ سی $1 مر وه شلک هنی نوشته بوئه",
        "copyrightpage": "{{ان اس:پروجه}}:کپی رایت",
        "currentevents": "پيشومدل تازه باو",
        "currentevents-url": "پروجه:پيشومدل تازه باو",
-       "disclaimers": "منكرون",
+       "disclaimers": "منکریا",
        "disclaimerpage": "پروجه:منكر بيئن كاروريا",
        "edithelp": "هومياری سی ويرايشت",
        "mainpage": "سرآسونه",
        "privacypage": "پروجه: خط مشی راز واداشتن",
        "badaccess": "خطا :اجازه بئیر",
        "badaccess-group0": "شما اجازه انجوم کاری که حاستیت نارین",
-       "badaccess-groups": "ای کاری که شما هاستیته سی کاروریا د  {{جمی:$2|گرو|یکی د گرویا}}: $1 مئدود بیه",
-       "versionrequired": "یه نسقه د نیازمنیا ویکی رسانه\n$1",
-       "versionrequiredtext": "Ù\86سÙ\82Ù\87 $1 Ù\88Û\8cÚ©Û\8c Ù\85دÛ\8cا Ø³Û\8c Ù\88Ù\87 Ú©Ø§Ø± Ø¨Ø³تن د ای بلگه لازم هئی .\nوه نه بوینیت [[ویجه:نسقه|نسقه بلگه]].",
+       "badaccess-groups": "ای کاری که شما هاستیته سی کاریاریا د  {{جمی:$2|گرو|یکی د گرویا}}: $1 مئدود بیه",
+       "versionrequired": "یه نسقه د نیازمنیا ویکی وارسگر\n$1",
+       "versionrequiredtext": "Ù\86سÙ\82Ù\87 $1 Ù\88Û\8cÚ©Û\8c Ù\88ارسگر Ø³Û\8c Ù\88Ù\87 Ú©Ø§Ø± Ú¯Ø±تن د ای بلگه لازم هئی .\nوه نه بوینیت [[ویجه:نسقه|نسقه بلگه]].",
        "ok": "خوئه",
        "pagetitle": "$1 - {{SITENAME}}",
        "pagetitle-view-mainpage": "{{SITENAME}}",
        "backlinksubtitle": "← $1",
        "retrievedfrom": "بازيافته د\"$1\"",
        "youhavenewmessages": "شما داريت $1($2)",
-       "youhavenewmessagesfromusers": "{{جمی:$4|شما }} $1 د {{جمی:$3|کارور هنی|$3 کاروریا}}داریتو($2).",
-       "youhavenewmessagesmanyusers": "شما $1 د خيلی كاروريا داريت ($2).",
+       "youhavenewmessagesfromusers": "{{جمی:$4|شما }} $1 د {{جمی:$3|کاریار هنی|$3 کاریاریا}}داریتو($2).",
+       "youhavenewmessagesmanyusers": "شما $1 د خيلی کاریار داريت ($2).",
        "newmessageslinkplural": "{{جمی:$1|یه گل پیغوم تازه|999=پیغوم ئل تازه}}",
        "newmessagesdifflinkplural": "آخر {{جمی:$1|آلشت|آلشتیا}}",
        "youhavenewmessagesmulti": "شما یه گل پیغوم تازه د $1 داریتو",
        "viewsourcelink": "سرچشمه نه بوينيت",
        "editsectionhint": "ويرايشت يه بشق:$1",
        "toc": "مینونه یا",
-       "showtoc": "Ù\86Ø´Ù\88 Ø¯Ø§Ø¦Ù\86",
+       "showtoc": "نشو دئن",
        "hidetoc": "قام كردن",
        "collapsible-collapse": "جم كردن",
        "collapsible-expand": "وا كردن",
        "exif-source": "سرچشمه",
        "exif-urgency": "فوریت",
        "exif-fixtureidentifier": "نوم ثاوت",
+       "exif-contact": "دونسمنیا پیوند گرتن",
        "exif-writer": "نیسنه",
        "exif-languagecode": "زون",
        "exif-iimversion": "نسقه آی آی ام",
        "exif-giffilecomment": "ویر و باور فایل جی آی اف",
        "exif-intellectualgenre": "نوع مورد",
        "exif-contact-value": "$1\n\n$2\n<div class=\"adr\">\n$3\n\n$4, $5, $6 $7\n</div>\n$8",
+       "exif-copyrighted-true": "کپی رایت بیه",
+       "exif-copyrighted-false": "حال و بال کپی رایت میزوکاری نبیه",
        "exif-unknowndate": "گات نادیار",
        "exif-orientation-1": "عادی",
+       "exif-orientation-3": "180 گرینج لر دئه",
        "exif-componentsconfiguration-0": "نی یش",
        "exif-exposureprogram-1": "دسی",
+       "exif-exposureprogram-2": "برنامه عادی",
        "exif-subjectdistance-value": "$1 متر",
        "exif-meteringmode-0": "نادیار",
        "exif-meteringmode-1": "میانگین",
        "exif-lightsource-0": "نادیار",
        "exif-lightsource-1": "روشنایی روز",
        "exif-lightsource-2": "فلورسنت",
+       "exif-lightsource-3": "تنگستن",
        "exif-lightsource-4": "فلش",
        "exif-lightsource-9": "هوا خو",
        "exif-lightsource-10": "هوا اوری",
        "exif-lightsource-11": "سایه",
+       "exif-lightsource-17": "چرا استاندارد آ",
+       "exif-lightsource-18": "چرا استاندارد بی",
+       "exif-lightsource-19": "چرا استاندارد سی",
+       "exif-lightsource-255": "سرچشمه چرا هنی",
        "exif-flash-mode-3": "مد خودانجوم",
        "exif-focalplaneresolutionunit-2": "ائنج",
        "exif-sensingmethod-1": "نادیار",
+       "exif-filesource-3": "دیربین دیجیتالی",
        "exif-customrendered-0": "پردازشت خو",
        "exif-customrendered-1": "پردازشت همیشه ای",
        "exif-scenecapturetype-0": "استاندارد",
        "exif-gpsspeed-m": "مایل سی هر ساعت",
        "exif-gpsdestdistance-k": "کلومتر",
        "exif-gpsdestdistance-m": "مایل",
+       "exif-gpsdop-excellent": "عالیه($1)",
        "exif-gpsdop-good": "خو ($1)",
        "exif-gpsdop-fair": "د ری انصاف ($1)",
+       "exif-gpsdop-poor": "گن ($1)",
+       "exif-objectcycle-a": "فقط شو صو",
+       "exif-objectcycle-p": "فقط ایواره",
+       "exif-objectcycle-b": "هم شو صو و هم ایواره",
        "exif-dc-contributor": "هومیارا",
        "exif-dc-publisher": "درتیجن",
        "exif-dc-relation": "وارسگر مرتوط",
        "exif-isospeedratings-overflow": "گپتر د 65535",
        "exif-iimcategory-ace": "هنریا، رهزیشت و زیستگه",
        "exif-iimcategory-clj": "جرم و قانون",
+       "exif-iimcategory-dis": "بدبختیا و رخ ونیا",
        "exif-iimcategory-fin": "اموری و کسم کار",
        "exif-iimcategory-edu": "آموختاری",
        "exif-iimcategory-evn": "زئشت گه",
        "monthsall": "همه",
        "confirmemail": "پشت راس کردن تیرنشون انجومانامه",
        "confirmemail_send": "کل کردن رازینه پشت راس کاری",
+       "confirmemail_sent": "انجومانامه پشت راس کردن کل بیه.",
+       "confirmemail_subject": "{{SITENAME}} تیرنشون انجومانامه پشت راست کردن",
+       "confirmemail_invalidated": "پشت راس کنی انجومانامه انجوم شیو بیه",
        "invalidateemail": "انجومشیو کردن پشت راس کردن انجومانامه",
        "scarytranscludetoolong": "[یو آر ال فره گپه]",
        "recreate": "د نو راس کردن",
        "table_pager_prev": "بلگه دمايی",
        "table_pager_first": "سرآسونه",
        "table_pager_last": "بلگه آخری",
+       "table_pager_limit": "$1 سی هر بلگه نشو بیه",
        "table_pager_limit_label": "آیتم سی هر بلگه:",
        "table_pager_limit_submit": "رو",
        "table_pager_empty": "هیچ نتیجه ای نئ",
        "watchlistedit-clear-title": "سیل برگ دروس بیه",
        "watchlistedit-clear-legend": "پاک کردن سیل برگ",
        "watchlistedit-clear-titles": "داسون:",
+       "watchlistedit-clear-submit": "پاک کردن سیل برگ(وه سی همیشه هئ!)",
        "watchlistedit-clear-done": "سیل برگتون وه پاک بیه.",
+       "watchlistedit-too-many": "ایچه بلگه یا فره ای سی نشو دئن هئ.",
        "watchlisttools-clear": "پاک کردن سیل برگ",
        "watchlisttools-view": "آلشتیا مرتوط نه بوینیت",
        "watchlisttools-edit": "سیل برگ بوینیتو و ویرایشت بکید",
        "hijri-calendar-m2": "صفر",
        "hijri-calendar-m3": "ربیع الاول",
        "hijri-calendar-m4": "رجو",
+       "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|چک چنه]])",
        "timezone-utc": "UTC",
+       "unknown_extension_tag": "سردیس دمادیس نادیار \"$1\"",
        "duplicate-defaultsort": "زنهار کلیت پیش فرض جور بیه $2 تازه ای یا کلید پیش فرض جوربیه $1 رد بیه.",
        "version": "نسقه",
+       "version-extensions": "دمادیسیا پورسه",
        "version-specialpages": "بلگيا ويجه",
        "version-variables": "آلشت ونا",
        "version-antispam": "نهاگرتن هرزنومه",
        "version-hook-name": "نوم قلاو",
        "version-no-ext-name": "[بی نوم]",
        "version-ext-license": "ليسانس",
+       "version-ext-colheader-name": "دمادیس",
        "version-skin-colheader-name": "پوسه",
        "version-ext-colheader-version": "نسقه",
        "version-ext-colheader-license": "ليسانس",
        "api-error-filename-tooshort": "نوم جانیا فره کؤچکه.",
        "api-error-illegal-filename": "نوم جانیا اجازه دئه نئ.",
        "api-error-mustbeloggedin": "شما سی سوارکردن فایلیا با بیایت وامین",
+       "api-error-unclassified": "یه گل خطا نادیار ری ون کرده.",
+       "api-error-unknown-code": "خطا نادیار:\"$1\".",
+       "api-error-unknownerror": "خطا نادیار:\"$1\".",
        "duration-seconds": "$1 {{PLURAL:$1|ثانیه|ثانیه یا}}",
        "duration-minutes": "$1 {{PLURAL:$1|دیقه|دیقه یا}}",
        "duration-hours": "$1 {{PLURAL:$1|ساعت |ساعتیا}}",
        "duration-years": "$1{{جمی:$1| سال|سالیا}}",
        "duration-decades": "$1 {{PLURAL:$1|دهه|دهه یا}}",
        "duration-centuries": "$1 {{PLURAL:$1|سده|سده یا}}",
+       "limitreport-cputime-value": "$1 {{PLURAL:$1|ثانیه|ثانیه یا}}",
+       "limitreport-walltime": "زمون راستکی وه کار گرتن",
        "limitreport-ppvisitednodes-value": "$1/$2",
        "limitreport-ppgeneratednodes-value": "$1/$2",
        "limitreport-expansiondepth-value": "$1/$2",
        "limitreport-expensivefunctioncount-value": "$1/$2",
+       "expand_templates_input": "نیسسه درینده:",
        "expand_templates_output": "نتیجه",
        "expand_templates_xml_output": "درده ایکس ام ال",
        "expand_templates_ok": "خوئه",
        "mediastatistics-header-bitmap": "عسگیا بیت مپ",
        "mediastatistics-header-audio": "دنگ",
        "mediastatistics-header-video": "عسگ و فیلم",
+       "mediastatistics-header-multimedia": "وارسگر خو",
        "mediastatistics-header-office": "نوشتگه",
        "mediastatistics-header-text": "نیسسه دار",
        "json-error-syntax": "خطا دستوری"
index 706ffac..1faf2c7 100644 (file)
        "filerenameerror": "പ്രമാണം \"$1\", \"$2\" എന്ന തലക്കെട്ടിലേയ്ക്ക് മാറ്റാൻ സാധിച്ചില്ല.",
        "filedeleteerror": "\"$1\" നീക്കം ചെയ്യാൻ സാധിച്ചില്ല.",
        "directorycreateerror": "\"$1\" എന്ന ഡയറക്റ്ററി സൃഷ്ടിക്കാൻ സാധിച്ചില്ല.",
+       "directoryreadonlyerror": "\"$1\" എന്ന ഡയറക്ടറി വായിക്കാൻ-മാത്രം പറ്റുന്നതാണ്.",
+       "directorynotreadableerror": "\"$1\" എന്ന ഡയറക്ടറി വായിക്കാനാവുന്നില്ല.",
        "filenotfound": "\"$1\" എന്ന പ്രമാണം കണ്ടെത്താനായില്ല.",
        "unexpected": "പ്രതീക്ഷിക്കാത്ത മൂല്യം: \"$1\"=\"$2\".",
        "formerror": "പിഴവ്: ഫോം സമർപ്പിക്കുവാൻ പറ്റിയില്ല",
index 6ac9530..03f9923 100644 (file)
        "filerenameerror": "Il-fajl \"$1\" ma setax jiġi msemmi mill-ġdid għal \"$2\".",
        "filedeleteerror": "Il-fajl \"$1\" ma setax jiġi mħassar.",
        "directorycreateerror": "Id-direttorju \"$1\" ma setax jiġi maħluq.",
+       "directoryreadonlyerror": "Id-direttorju \"$1\" hu għall-qari biss",
+       "directorynotreadableerror": "Id-direttorju \"$1\" ma jistax jinqara.",
        "filenotfound": "Il-fajl \"$1\" ma nstabx.",
        "unexpected": "Valur mhux mistenni: \"$1\"=\"$2\".",
        "formerror": "Problema: il-formula ma setgħatx tiġi proċessata",
        "passwordsent": "Il-password il-ġdida ntbagħtet fl-indirizz tal-posta elettronika ta' \"$1\".\nJekk jogħġbok, għamel aċċess wara li tasallek.",
        "blocked-mailpassword": "L-indirizz tal-IP tiegħek huwa bblokkjat u miżmum milli jwettaq modifiki. Għaldaqstant, mhuwiex possibli għalik li tuża l-funzjoni sabiex iġġib lura l-password, u dan sabiex ma jkunx hemm abbużi.",
        "eauthentsent": "Intbagħtetlek konferma b'permezz ta' messaġġ elettroniku fl-indirizz speċifikat.\nQabel ma tinbagħat xi posta elettronika oħra fuq il-kont, trid issegwi l-istruzzjonijiet indikati fil-messaġġ, sabiex tikkonferma li l-kont huwa tassew tiegħek.",
-       "throttled-mailpassword": "Posta elettronika sabiex tfakrek il-password ġiet postjata, fl-aħħar {{PLURAL:$1|siegħa|$1 siegħat}}.\nSabiex jitnaqqas l-abbuż, waħda biss tista' tiġi postjata f'kull {{PLURAL:$1|siegħa|$1 siegħat}}.",
+       "throttled-mailpassword": "Diġà nbagħtitlek messaġġ elettroniku biex ifakkrek il-password, fl-aħħar {{PLURAL:$1|siegħa|$1 sigħat}}.\nSabiex jiġi evitat l-abbuż, password waħda biss tista' tinbagħat kull {{PLURAL:$1|siegħa|$1 sigħat}}.",
        "mailerror": "Problema bil-postar tal-messaġġ: $1",
        "acct_creation_throttle_hit": "L-utenti ta' din il-wiki li jużaw l-indirizz IP tiegħek ħolqu {{PLURAL:$1|kont|$1 kontijiet}} fl-aħħar ġurnata, li hu n-numru massimu permess f'dan il-perjodu ta' żmien.\nBħala riżultat, il-viżitaturi li jużaw dan l-IP ma jistgħux għall-mument, joħoloqu aktar kontijiet.",
-       "emailauthenticated": "L-indirizz tal-posta elettronika tiegħek ġiekonfermat nhar il-$2, fil-$3.",
-       "emailnotauthenticated": "L-indirizz tal-posta elettronika tiegħek għadu ma ġiex konfermat. L-ebda posta elettronika mhi se tintbagħat għall-ebda minn dawn il-funzjonijiet elenkati hawn taħt.",
+       "emailauthenticated": "L-indirizz tal-posta elettronika tiegħek ġie kkonfermat nhar il-$2, fil-$3.",
+       "emailnotauthenticated": "L-indirizz tal-posta elettronika tiegħek għadu ma ġiex konfermat. L-ebda posta elettronika mhi se tinbagħat għall-funzjonijiet elenkati hawn taħt.",
        "noemailprefs": "Speċifika indirizz ta' posta elettronika sabiex dawn il-faċċilitajiet jaħdmu.",
        "emailconfirmlink": "Ikkonferma l-indirizz tal-posta elettronika tiegħek",
        "invalidemailaddress": "L-indirizz tal-posta elettronika ma jistax jiġi aċċettat għax jidher li għandu format ħażin.\nJekk jogħġbok daħħal indirizz validu jew inkella ħassru.",
        "accountcreatedtext": "Il-kont tal-utent għal  [[{{ns:User}}:$1|$1]] ([[{{ns:User talk}}:$1|diskussjoni]]) ġie maħluq.",
        "createaccount-title": "Ħolqien tal-kont għal {{SITENAME}}",
        "createaccount-text": "Xi ħadd ħoloq kont għall-indirizz tal-posta elettronika tiegħek fuq {{SITENAME}} ($4) bl-isem \"$2\", bil-password: \"$3\".\nHuwa opportun li tidħol issa u tbiddel il-password tiegħek mill-ewwel.\n\nJekk trid tista' ma tagħtix każ dan il-messaġġ, jekk dan il-kont ġie maħluq bi żball.",
-       "login-throttled": "Saru ħafna tentattivi riċenti fuq il-password ta' dan il-kont.\nJekk jogħġbok stenna qabel ma terġa' tipprova.",
+       "login-throttled": "Ippruvajt tidħol fl-kont wisq drabi\nJekk jogħġbok stenna $1 qabel ma terġa' tipprova.",
        "login-abort-generic": "Il-login ma kienx suċċess - Imħassar",
+       "login-migrated-generic": "Il-kont tiegħek tmexxa u ismek ta' utent m'għadux jeżisti fuq dan il-wiki.",
        "loginlanguagelabel": "Lingwa: $1",
        "suspicious-userlogout": "Ir-rikjesta tiegħek li toħroġ barra mill-kont tiegħek ġiet miċħuda minħabba li jidher li din intbagħtet minn browser li ma jaħdimx jew minn proxy ta' caching.",
        "pt-login": "Idħol",
        "changeemail-submit": "Biddel l-indirizz elettroniku",
        "changeemail-throttled": "Ippruvajt tidħol wisq drabi.\nJekk jogħġbok stenna $1 qabel ma terġa' tipprova.",
        "resettokens": "Irrisettja t-tokens",
+       "resettokens-token-label": "$1 (valur attwali: $2)",
        "bold_sample": "Tipa ħoxna",
        "bold_tip": "Tipa ħoxna",
        "italic_sample": "Tipa korsiva",
        "preview": "Dehra proviżorja",
        "showpreview": "Dehra proviżorja",
        "showdiff": "Uri t-tibdiliet",
+       "blankarticle": "<strong>Attenzjoni:</strong> Il-paġna li qed toħloq vojta.\nMeta terġa' tikklikkja fuq \"{{int:savearticle}}\", il-paġna tinħoloq bla ebda kontenut.",
        "anoneditwarning": "'''Attenzjoni:''' Ma dħaltx f'kontok.\nL-indirizz tal-IP tiegħek se jkun jidher pubblikament meta tagħmel xi modifika. Jekk <strong>[$1 tidħol f'kontok]</strong> jew <strong>[$2 toħloq kont]</strong>, il-modifiki li tagħmel jiġu attribwiti lill-ismek ta' utent, flimkien ma benefiċċji oħra.",
        "anonpreviewwarning": "''Bħalissa mintix fil-kont tiegħek. Jekk issalva xi modifiki tiegħek, fil-kronoloġija tal-paġna se jiġi reġistrat l-indirizz IP tiegħek.''",
        "missingsummary": "'''Twissija:''' Ma pprovdejt l-ebda taqsira dwar il-modifika.\nJekk terġa' tagħfas Modifika, l-modifika se tiġi salvata mingħajr waħda.",
        "loginreqlink": "li tidħol fil-kont tiegħek",
        "loginreqpagetext": "Int trid ikollhok $1 sabiex tkun tista' tara paġni oħrajn.",
        "accmailtitle": "Il-password intbagħtet.",
-       "accmailtext": "Password ġenerata każwalment għal [[User talk:$1|$1]] intbagħtet lil $2.<br />\n\nIl-password għal dan il-kont il-ġdid tista' titbiddel fil-paġna għat-''[[Special:ChangePassword|tibdil tal-password]]''.",
+       "accmailtext": "Intbagħtet lil $2 password iġġenerata każwalment għal [[User talk:$1|$1]] .\nTista' tinbidel fuq il-paġna għat-<em>[[Special:ChangePassword|tibdil tal-password]]</em> wara d-dħul fil-kont.",
        "newarticle": "(Ġdid)",
        "newarticletext": "Inti segwejt link għal paġna li għadha ma ġietx maħluqa.\nSabiex toħloq il-paġna, ikteb fil-kaxxa li tinsab hawn taħt (ara [$1 paġna tal-għajnuna] għal aktar informazzjoni).\nJekk wasalt hawn biż-żball, agħfas il-buttuna '''lura''' (''back'') fuq il-browser tiegħek.",
        "anontalkpagetext": "----''Din hija l-paġna ta' diskussjoni ta' utent anonimu li għadu ma ħoloqx kont, jew inkella li ma jużahx.\nGħaldaqstant biex nidentifikawh ikollna nużaw l-indirizz tal-IP tiegħu/tagħha.\nL-istess indirizz tal-IP jista' jkun użat minn bosta utenti differenti.\nJekk int utent anonimu u tħoss li qiegħed tirċievi kummenti irrelevanti jew li ma jagħmlux sens, jekk jogħġbok [[Special:UserLogin|idħol fil-kont tiegħek]] jew [[Special:UserLogin/signup|oħloq wieħed]] sabiex tevita li fil-futur tiġi konfuż ma' utenti anonimi oħra.''",
        "noarticletext": "Bħalissa m'hemm l-ebda test f'din il-paġna.\nInti tista' [[Special:Search/{{PAGENAME}}|tfittex it-titlu ta' din il-paġna]] f'paġni oħra, jew <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} tfittex ir-reġistri relatati], jew [{{fullurl:{{FULLPAGENAME}}|action=edit}} timmodifika din il-paġna]</span>.",
-       "noarticletext-nopermission": "Bħalissa m'hemm l-ebda test f'din il-paġna. Inti tista' [[Special:Search/{{PAGENAME}}|tfittex għal dan it-titlu tal-paġna]] f'paġni oħra, jew <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} fittex ir-reġistri relatati]</span>.",
+       "noarticletext-nopermission": "Bħalissa m'hemm l-ebda test f'din il-paġna. Inti tista' [[Special:Search/{{PAGENAME}}|tfittex dan it-titlu tal-paġna]] f'paġni oħra, jew <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} tfittex ir-reġistri relatati]</span>, imma m'għandikx permess toħloq dil-paġna.",
        "missing-revision": "Ir-reviżjoni #$1 tal-paġna bl-isem \"{{FULLPAGENAME}}\" ma teżistix.\n\nDan ħafna drabi jiġri minħabba li tkun segwejt ħolqa lejn paġna mħassra, f'kronoloġija li mhix aġġornata.\nId-detallji tista' ssibhom fir-[{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} reġistru tat-tħassir].",
        "userpage-userdoesnotexist": "Il-kont tal-utent \"<nowiki>$1</nowiki>\" mhux reġistrat.\nJekk jogħġbok, ara jekk verament tridx toħloq/timodifika din il-paġna.",
        "userpage-userdoesnotexist-view": "Il-kont tal-utent \"$1\" mhuwiex reġistrat.",
        "copyrightwarning": "Jekk jogħġbok innota li kull kontribuzzjoni li tagħmel lil {{SITENAME}} hija konsidrata li ġiet postjata taħt l-$2 (ara $1 għal aktar informazzjoni).\nJekk inti tixtieq li l-kitba tiegħek ma tiġiex modifikata jew mqassma, jekk jogħġbok tagħmilx modifiki hawnhekk.<br />\nInti qiegħed ukoll qiegħed twiegħed li ktibt dan ix-xogħol int, jew ġibtu minn dominazzjoni pubblika jew resorsi b'xejn simili. <br />\n<br />\n'''TAGĦMILX MODIFIKI LI JINKLUDU XOGĦOL TA' ĦADDIEĦOR BLA PERMESS!'''",
        "copyrightwarning2": "Jekk jogħġbok innota li kull kontribuzzjoni li tagħmel lil {{SITENAME}} tista' tiġi modifikata, inbidla, jew imħassra minn kontributuri oħrajn.\nJekk inti tixtieq li l-kitba tiegħek ma tiġiex modifikata jew mqassma, jekk jogħġbok tagħmilx modifiki hawnhekk.<br />\nInti qiegħed ukoll qiegħed twiegħed li ktibt dan ix-xogħol int, jew ġibtu minn dominazzjoni pubblika jew resorsi b'xejn simili. (ara  $1 għal aktar informazzjoni) <br />\n<br />\n'''TAGĦMILX MODIFIKI LI JINKLUDU XOGĦOL TA' ĦADDIEĦOR BLA PERMESS!'''",
        "longpageerror": "'''PROBLEMA: Il-modifika li għamilt hija twila {{PLURAL:$1|kilobyte waħda|$1 kilobytes}}, li hija iktar mill-massimu ta' {{PLURAL:$1|kilobyte waħda|$2 kilobytes}}.''' Il-modifika ma tistax tiġi salvata.",
-       "readonlywarning": "'''TWISSIJA: Id-databażi ġiet imblukkata għall-manutenzjoni, u għaldaqstant m'huwiex possibbli li ssalva l-modifiki tiegħek dal-ħin. Biex ma titlifhomx, għalissa salva xogħlok ġo fajl u ġaladarba terġa' tinfetaħ id-databażi, ikkopja kollox. Grazzi.'''\n\nL-amministratur li mblokkaha offra din ir-raġuni: $1",
+       "readonlywarning": "<strong>Attenzjoni: Il-bażi tad-dejta ġiet imblukkata għall-manutenzjoni, allura ma tistax tissejvja l-modifiki bħalissa.</strong>\nBiex ma titlifhomx tista' tikkopja u tinkolla t-test tiegħek ġo fajl testwali u tissejvjah għal aktar tard.\n\nL-amministratur li mblokkaha offra din ir-raġuni: $1",
        "protectedpagewarning": "'''Twissija:  Din il-paġna ġiet imblukkata b'tali mod li l-utenti li għandhom il-privileġġi ta' amministratur biss jistgħu jimmodifikawha.'''<br/ >\nL-aħħar daħla fir-reġistru hija disponibbli hawn taħt għar-referenza:",
        "semiprotectedpagewarning": "'''Nota:''' Din il-paġna ġiet imblukkata b'tali mod li l-utenti reġistrati biss jistgħu jimmodifikawha. L-aħħar daħla fir-reġistru hija disponibbli hawn taħt bħala referenza:",
        "cascadeprotectedwarning": "'''Twissija:''' Din il-paġna ġiet imblukkata sabiex l-utenti li għandhom il-privileġġi ta' amministratur biss ikunu jistgħu jimmodifikawha, minħabba li hija inkluża fil-{{PLURAL:$1|paġna segwenti, li ġiet protetta|paġni segwenti li ġew protetti}}, bil-protezzjoni \"rikorsiva\" tiġi magħżula:",
        "edit-conflict": "Kunflitt tal-editjar.",
        "edit-no-change": "Il-modifika li għamilt ġiet injorata, minħabba li ebda bidla ma saret lejn it-test.",
        "postedit-confirmation-created": "Il-paġna ġiet maħluqa.",
+       "postedit-confirmation-restored": "Il-paġna ġġeddet.",
        "postedit-confirmation-saved": "Il-modifika tiegħek ġiet salvata.",
        "edit-already-exists": "Ma tistax tinħoloq din il-paġna.\nDin teżisti diġà.",
        "editwarning-warning": "Jekk tħalli din il-paġna jista' jwassal sabiex titlef kwalunkwe tibdil li tkun għamilt. Jekk int tinsab fil-kont tiegħek, tista' tneħħi dan l-avviż fis-sezzjoni \"Modifiki\" tal-preferenzi tiegħek.",
index f0b4437..4e0a23c 100644 (file)
        "edit": "Rediger",
        "edit-local": "Rediger lokal beskrivelse",
        "create": "Opprett",
-       "create-local": "Legg til lokal beskrivelse",
+       "create-local": "Opprett lokal beskrivelse",
        "editthispage": "Rediger siden",
        "create-this-page": "Opprett denne siden",
        "delete": "Slett",
        "default-skin-not-found-row-enabled": "* <code>$1</code> / $2 (slått på)",
        "default-skin-not-found-row-disabled": "* <code>$1</code> / $2 ('''slått av''')",
        "mediastatistics": "Mediestatistikk",
+       "mediastatistics-summary": "Statistikk over opplastede filtyper. Dette inkluderer bare den nyeste versjonen av hver fil. Eldre eller slettede versjoner av filene er eksludert.",
        "mediastatistics-nbytes": "{{PLURAL:$1|$1 byte}} ($2; $3 %)",
        "mediastatistics-table-mimetype": "MIME-type",
        "mediastatistics-table-extensions": "Mulige filtyper",
        "mediastatistics-header-executable": "Kjørbare filer",
        "mediastatistics-header-archive": "Komprimerte formater",
        "json-warn-trailing-comma": "$1 etterfølgende {{PLURAL:$1|komma|kommaer}} ble fjernet fra JSON",
+       "json-error-unknown": "Det var et problem med JSON. Feil: $1",
+       "json-error-depth": "Maksimal stakkdybde har blitt overskredet",
        "json-error-state-mismatch": "Ugyldig JSON",
-       "json-error-syntax": "Syntaksfeil"
+       "json-error-ctrl-char": "Kontrolltegnfeil, muligens feilaktig kodet",
+       "json-error-syntax": "Syntaksfeil",
+       "json-error-utf8": "Feilaktige UTF-8-tegn, muligens feilkodet",
+       "json-error-recursion": "En eller flere rekursive referanser i verdien som skal kodes",
+       "json-error-inf-or-nan": "En eller flere NAN- eller INF-verdier i verdien som skal kodes",
+       "json-error-unsupported-type": "En verdi av en type som ikke kan kodes ble angitt"
 }
index af62a95..c75a11d 100644 (file)
        "last": "ਪਿਛਲਾ",
        "page_first": "ਪਹਿਲਾਂ",
        "page_last": "ਆਖ਼ਰੀ",
-       "histlegend": "ਫ਼ਰà¨\95 à¨µà©\87à¨\96à©\8b:\nਮà©\81à¨\95ਾਬਲਾ à¨\95ਰਨ à¨²à¨\88 à¨°à©\80ਵਿà¨\9cਨਾà¨\82 à¨¦à©\87 à¨°à©\87ਡà©\80à¨\93 à¨¬à¨\9fਨਾà¨\82 à¨µà¨¿à©±à¨\9a à¨¨à¨¿à¨¸à¨¼à¨¾à¨¨ à¨²à¨¾à¨\93 à¨\85ਤà©\87 \"à¨\9cਾà¨\93\" à¨\9cਾà¨\82 à¨¸à¨­ à¨¤à©\8bà¨\82 à¨¥à©±à¨²à©\87 à¨µà¨¾à¨²à©\87 à¨¬à¨\9fਨ à¨¤à©\87 à¨\95ਲਿੱà¨\95 à¨\95ਰà©\8b। <br />\nਲà©\88à¨\9cà¨\85ੰਡ:\n'''({{int:cur}})''' = à¨¨à¨µà©\87à¨\82 à¨°à©\80ਵਿà¨\9cਨ à¨¨à¨¾à¨²à©\8bà¨\82 à¨«à¨¼à¨°à¨\95, '''({{int:last}})''' = à¨ªà¨¿à¨\9bਲà©\87 à¨°à©\80ਵਿà¨\9cਨ à¨¨à¨¾à¨²ੋਂ ਫ਼ਰਕ, '''({{int:minoreditletter}})''' = ਛੋਟੀ ਤਬਦੀਲੀ।",
+       "histlegend": "ਫ਼ਰà¨\95 à¨µà©\87à¨\96à©\8b:\nਮà©\81à¨\95ਾਬਲਾ à¨\95ਰਨ à¨²à¨\88 à¨¦à©\81ਹਰਾà¨\88à¨\86à¨\82 à¨¦à©\87 à¨°à©\87ਡà©\80à¨\93 à¨¬à¨\9fਨਾà¨\82 à¨µà¨¿à©±à¨\9a à¨¨à¨¿à¨¸à¨¼à¨¾à¨¨ à¨²à¨¾à¨\93 à¨\85ਤà©\87 \"à¨\9cਾà¨\93\" à¨\9cਾà¨\82 à¨¸à¨­ à¨¤à©\8bà¨\82 à¨¥à©±à¨²à©\87 à¨µà¨¾à¨²à©\87 à¨¬à¨\9fਨ à¨¨à©\82à©° à¨¨à©±à¨ªà©\8b। <br />\nà¨\9fà©\80à¨\95ਾ:\n'''({{int:cur}})''' = à¨¨à¨µà©\80à¨\82 à¨¦à©\81ਹਰਾà¨\88 à¨¨à¨¾à¨²à¨¼à©\8bà¨\82 à¨«à¨¼à¨°à¨\95, '''({{int:last}})''' = à¨ªà¨¿à¨\9bਲà©\80 à¨¦à©\81ਹਰਾà¨\88 à¨¨à¨¾à¨²à¨¼ੋਂ ਫ਼ਰਕ, '''({{int:minoreditletter}})''' = ਛੋਟੀ ਤਬਦੀਲੀ।",
        "history-fieldset-title": "ਬਰਾਊਜ਼ਰ ਅਤੀਤ",
        "history-show-deleted": "ਸਿਰਫ਼ ਮਿਟਾਏ ਗਏ",
        "histfirst": "ਸਭ ਤੋਂ ਪੁਰਾਣੇ",
index 4108b32..bf5682e 100644 (file)
        "longpages": "Najdłuższe strony",
        "deadendpages": "Strony bez linków wewnętrznych",
        "deadendpagestext": "Poniższe strony nie posiadają odnośników do innych stron znajdujących się w {{GRAMMAR:MS.lp|{{SITENAME}}}}.",
-       "protectedpages": "Strony zabezpieczone",
+       "protectedpages": "Zabezpieczone strony",
        "protectedpages-indef": "Tylko strony zabezpieczone na zawsze",
        "protectedpages-summary": "Ta strona zawiera listę stron, które są obecnie chronione. Aby uzyskać listę tytułów, których utworzenie jest zabronione, zobacz: [[{{#special:ProtectedTitles}}|{{int:protectedtitles}}]].",
        "protectedpages-cascade": "Tylko strony zabezpieczone rekursywnie",
index 4eb1009..a1d4b66 100644 (file)
        "log-name-pagelang": "Argistr dij cangiament ëd lenga",
        "log-description-pagelang": "Cost-sì a l'é n'argistr dij cangiament ant le lenghe dle pàgine.",
        "logentry-pagelang-pagelang": "$1 {{GENDER:$2|a l'ha cangià}} la lenga dla pàgina $3 da $4 a $5.",
-       "default-skin-not-found": "Tension! La pel predeterminà për soa wiki, definìa an <code dir=\"ltr\">$wgDefaultSkin</code> tanme <code>$1</code>, a l'é nen disponìbil.\n\nSoa anstalassion a smija anclude le pel sì-dapress. Ch'a vëdda [https://www.mediawiki.org/wiki/Manual:Skin_configuration ël manual ëd configurassion dle pel] për d'anformassion su coma abiliteje e serne col apredefinìa.\n\n$2\n\n; S'a l'ha pen-a anstalà MediaWiki:\n: A l'é probàbil che a l'abia anstalalo da git, o diretaman dal còdes sorgiss an n'àutra manera. A l'é normal. Ch'a preuva a anstalé dle pej da [https://www.mediawiki.org/wiki/Category:All_skins la lista dle pel Ëd mediawiki.org], parèj:\n:* Dëscariand l' [https://www.mediawiki.org/wiki/Download archivi tar ëd l'anstalador], ch'a comprend vàire pel e estension. A peul copié e ancolé la lista dle <code>pel/</code> d'ambelelà.\n:* Clonand un dij depòsit <code>mediawiki/skins/*</code> via git ant la lista <code dir=\"ltr\">skins/</code> ëd soa anstalassion ëd MediaWiki.\n: Sòn a dovrìa nen antërferì con sò depòsit git si chiel a l'é un dësvlupador ëd MediaWiki.\n\n; S'a l'ha pen-a agiornà MediaWiki:\n: MediaWiki 1.24 e pi neuv a përmet pi nen an automàtich le pel anstalà (ch'a vëdda [https://www.mediawiki.org/wiki/Manual:Skin_autodiscovery manual an sla dëscuverta automàtica dle pel]). A peul copié le linie sì-dapress an <code>LocalSettings.php</code> për abilité tute le pel ch'a son anstalà al moment:\n\n<pre dir=\"ltr\">$3</pre>\n\n; S'a l'ha pen-a modifivà <code>LocalSettings.php</code>:\n: Ch'a verìfica torna ël nòm ëd dle pej për evité ij boro.",
+       "default-skin-not-found": "Tension! La pel predeterminà për soa wiki, definìa an <code dir=\"ltr\">$wgDefaultSkin</code> tanme <code>$1</code>, a l'é nen disponìbil.\n\nSoa anstalassion a smija anclude le pel sì-dapress. Ch'a vëdda [https://www.mediawiki.org/wiki/Manual:Skin_configuration ël manual ëd configurassion dle pel] për d'anformassion su coma abiliteje e serne cola predefinìa.\n\n$2\n\n; S'a l'ha pen-a anstalà MediaWiki:\n: A l'é probàbil che a l'abia anstalalo da git, o diretaman dal còdes sorgiss an n'àutra manera. A l'é normal. Ch'a preuva a anstalé dle pej da [https://www.mediawiki.org/wiki/Category:All_skins la lista dle pel ëd mediawiki.org], parèj:\n:* Dëscariand l' [https://www.mediawiki.org/wiki/Download archivi tar ëd l'anstalador], ch'a comprend vàire pel e estension. A peul copié e ancolé la lista dle <code>pel/</code> d'ambelelà.\n:* Clonand un dij depòsit <code>mediawiki/skins/*</code> via git ant la lista <code dir=\"ltr\">skins/</code> ëd soa anstalassion ëd MediaWiki.\n: Sòn a dovrìa nen antërferì con sò depòsit git si chiel a l'é un dësvlupador ëd MediaWiki.\n\n; S'a l'ha pen-a agiornà MediaWiki:\n: MediaWiki 1.24 e pi neuv a përmet pi nen an automàtich le pel anstalà (ch'a vëdda [https://www.mediawiki.org/wiki/Manual:Skin_autodiscovery manual an sla dëscuverta automàtica dle pel]). A peul copié le linie sì-dapress an <code>LocalSettings.php</code> për abilité tute le pel ch'a son anstalà al moment:\n\n<pre dir=\"ltr\">$3</pre>\n\n; S'a l'ha pen-a modificà <code>LocalSettings.php</code>:\n: Ch'a verìfica torna ël nòm ëd dle pej për evité ij boro.",
        "default-skin-not-found-no-skins": "Darmagi! La pel dë stàndard për soa wiki, definìa da <code>$wgDefaultSkin</code> tanme <code>$1</code>, a l'é nen disponìbil.\n\nChiel a l'ha gnun-a pel anstalà.\n\n; S'a l'ha pen-a anstalà o agiornà MediaWiki:\n: A l'é probàbil ch'a l'abia falo da git, o diret dal còdes sorgiss an n'àutra manera. A l'é normal. MediaWiki 1.24 e pi recent doesn't a ancludo gnun-a pel ant ël depòsit prinsipal. Ch'a preuva a anstalé chèiche pel da [https://www.mediawiki.org/wiki/Category:All_skins la lista dle pel ëd mediawiki.org]:\n:* Dëscariand [https://www.mediawiki.org/wiki/Download l'archivi tar dl'anstalador], ch'a comprend vàire pel e estension. A peul copié e ancolé la lista <code>skins/</code> da là.\n:* Clonand un dij depòsit <code>mediawiki/skins/*</code> via git ant la lista <code dir=\"ltr\">skins/</code> ëd soa anstalassion ëd MediaWiki.\n: Fé sòn a dovrìa nen antërferì con sò depòsit git se chiel a l'é un dësvlupador ëd MediaWiki. Ch'a vëdda [https://www.mediawiki.org/wiki/Manual:Skin_configuration Manual: ël manual dla configurassion dle pel] për d'anformassion su coma ativé le pel e serne cola predefinìa.",
        "default-skin-not-found-row-enabled": "* <code>$1</code> / $2 (abilità)",
        "default-skin-not-found-row-disabled": "* <code>$1</code> / $2 ('''disabilità''')",
index e19a2b2..ce7e617 100644 (file)
        "download": "ښکته کول",
        "unwatchedpages": "ناکتلي مخونه",
        "listredirects": "د ورگرځېدنو لړليک",
+       "listduplicatedfiles": "د دوه گونو دوتنو لړليک",
        "unusedtemplates": "ناکارېدلې کينډۍ",
        "unusedtemplateswlh": "نور تړنونه",
        "randompage": "ناټاکلی مخ",
        "expand_templates_ok": "ښه",
        "expand_templates_remove_nowiki": "په پايلو کې د <nowiki> نښلنونه ځپل",
        "expand_templates_generate_rawhtml": "خام HTML ښکاره کول",
-       "expand_templates_preview": "مخکتنه"
+       "expand_templates_preview": "مخکتنه",
+       "mediastatistics": "د رسنيو شمار",
+       "mediastatistics-table-count": "د دوتنو شمېر",
+       "mediastatistics-header-audio": "غږ"
 }
index 88545d5..b9cdd8f 100644 (file)
@@ -74,7 +74,8 @@
                        "아라",
                        "Jefersonmoraes",
                        "Marcos dias de oliveira",
-                       "He7d3r"
+                       "He7d3r",
+                       "PauloEduardo"
                ]
        },
        "tog-underline": "Sublinhar links:",
        "autoblockid": "Autobloqueio #$1",
        "block": "Bloquear usuário",
        "unblock": "Desbloquear usuário",
-       "blockip": "Bloquear {{GENDER:$1|utilizador|utilizadora}}",
+       "blockip": "Bloquear {{GENDER:$1|usuário|usuária}}",
        "blockip-legend": "Bloquear usuário",
        "blockiptext": "Utilize o formulário abaixo para bloquear o acesso à escrita de um endereço específico de IP ou nome de usuário.\nIsto só deve ser feito para prevenir vandalismo, e de acordo com a [[{{MediaWiki:Policy-url}}|política]]. Preencha com um motivo específico a seguir (por exemplo, citando páginas que sofreram vandalismo).",
        "ipaddressorusername": "Endereço de IP ou nome de usuário:",
index 869cc5b..9ca2f88 100644 (file)
        "changepassword-summary": "{{ignored}}",
        "resetpass_announce": "Used in [[Special:UserLogin]].",
        "resetpass_text": "{{optional}}",
-       "resetpass_header": "Header on box on special page [[Special:ChangePassword]].\n\n{{Identical|Reset password}}",
+       "resetpass_header": "Header on box on special page [[Special:ChangePassword]].\n\n{{Identical|Change password}}",
        "oldpassword": "Used on the 'User profile' tab of 'my preferences'. This is the text next to an entry box for the old password in the 'change password' section.\n{{Identical|Old password}}",
        "newpassword": "{{Identical|New password}}",
        "retypenew": "Appears on the 'User profile' tab of the 'Preferences' special page in the 'Change password' section. It appears next to the text box for entering the new password a second time.",
        "img-auth-nopathinfo": "[[mw:Manual:Image Authorization|Manual:Image Authorization]]: Missing PATH_INFO - see english description\n{{Doc-important|This is plain text. Do not use any wiki syntax.}}",
        "img-auth-notindir": "[[mw:Manual:Image Authorization|Manual:Image Authorization]]: When the specified path is not in upload directory.",
        "img-auth-badtitle": "[[mw:Manual:Image Authorization|Manual:Image Authorization]]: Bad title, $1 is the invalid title",
-       "img-auth-nologinnWL": "[[mw:Manual:Image Authorization|Manual:Image Authorization]]: Logged in and file not whitelisted. $1 is the file not in whitelist.",
+       "img-auth-nologinnWL": "[[mw:Manual:Image Authorization|Manual:Image Authorization]]: Logged in and file not whitelisted.  $1 is the file not in whitelist.",
        "img-auth-nofile": "[[mw:Manual:Image Authorization|Manual:Image Authorization]]: Non existent file, $1 is the file that does not exist.",
        "img-auth-isdir": "[[mw:Manual:Image Authorization|Manual:Image Authorization]]: Trying to access a directory instead of a file, $1 is the directory.",
        "img-auth-streaming": "[[mw:Manual:Image Authorization|Manual:Image Authorization]]: Is now streaming file specified by $1.",
index 32cab6e..bf23508 100644 (file)
        "showhideselectedversions": "Показать/скрыть выбранные версии",
        "editundo": "отменить",
        "diff-empty": "(нет различий)",
-       "diff-multi-sameuser": "(не {{PLURAL:$1|показана Ð¾Ð´Ð½Ð° Ð¿Ñ\80омежÑ\83Ñ\82оÑ\87наÑ\8f Ð²ÐµÑ\80Ñ\81иÑ\8f|показанÑ\8b $1 Ð¿Ñ\80омежÑ\83Ñ\82оÑ\87нÑ\8bе Ð²ÐµÑ\80Ñ\81ии|показано $1 Ð¿Ñ\80омежÑ\83Ñ\82оÑ\87нÑ\8bÑ\85 Ð²ÐµÑ\80Ñ\81ии}} этого же участника)",
-       "diff-multi-otherusers": "(не {{PLURAL:$1|показана одна промежуточная версия|показано $1 промежуточных версии|показаны $1 промежуточные версии}} {{PLURAL:$2|ещё одного участника|$2 участников}})",
+       "diff-multi-sameuser": "(не {{PLURAL:$1|показана Ð¾Ð´Ð½Ð° Ð¿Ñ\80омежÑ\83Ñ\82оÑ\87наÑ\8f Ð²ÐµÑ\80Ñ\81иÑ\8f|показанÑ\8b $1 Ð¿Ñ\80омежÑ\83Ñ\82оÑ\87нÑ\8bе Ð²ÐµÑ\80Ñ\81ии|показано $1 Ð¿Ñ\80омежÑ\83Ñ\82оÑ\87нÑ\8bÑ\85 Ð²ÐµÑ\80Ñ\81ий}} этого же участника)",
+       "diff-multi-otherusers": "(не {{PLURAL:$1|показана одна промежуточная версия|показаны $1 промежуточные версии|показано $1 промежуточных версий}} {{PLURAL:$2|$2 участника|$2 участников}})",
        "diff-multi-manyusers": "({{PLURAL:$1|не показана $1 промежуточная версия, сделанная|не показаны $1 промежуточных версий, сделанных|не показаны $1 промежуточные версии, сделанные}} более чем {{PLURAL:$2|$2 участником|$2 участниками}})",
        "difference-missing-revision": "Не {{PLURAL:$2|1=найдена|найдены}} {{PLURAL:$2|$2 версия|$2 версий|$2 версии|1=одна из версий}} для этого сравнения ($1).\n\nТакое обычно случается при переходе по устаревшей ссылке сравнения версий для страницы, которая была удалена.\nПодробности могут быть в [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} журнале удалений].",
        "searchresults": "Результаты поиска",
index 1f216ea..b405148 100644 (file)
        "hidetoc": "fshih",
        "collapsible-collapse": "Ngushtoje",
        "collapsible-expand": "Zgjeroje",
+       "confirmable-yes": "PO",
+       "confirmable-no": "Jo",
        "thisisdeleted": "Shiko ose rikthe $1?",
        "viewdeleted": "Do ta shikosh $1?",
        "restorelink": "{{PLURAL:$1|një redaktim i fshirë|$1 redaktime të fshira}}",
        "filerenameerror": "I pamundur riemërtimi i skedës \"$1\" në \"$2\".",
        "filedeleteerror": "E pamundur fshirja e skedës \"$1\".",
        "directorycreateerror": "I pamundur krijimi i direktorisë \"$1\".",
+       "directoryreadonlyerror": "Direktoria \"<span class=\"notranslate\" translate=\"asnjë\">$1</span>\" është vetëm e lexueshme",
+       "directorynotreadableerror": "Direktoria \"<span class=\"notranslate\" translate=\"asnjë\">$1</span>\" nuk është e lexueshme.",
        "filenotfound": "E pamundur gjetja e skedës \"$1\".",
        "unexpected": "Vlerë e papritur: \"$1\"=\"$2\".",
        "formerror": "Gabim: Formulari nuk mund të dërgohet.",
        "viewyourtext": "Ju mund të shikoni dhe të kopjoni tekstin e '''ndryshimeve tuaja''' tek kjo faqe:",
        "protectedinterface": "Kjo faqe përmban tekstin e dritares së programit, për këtë arsye mbrohet për të shmangur abuzimet.",
        "editinginterface": "'''Kujdes:''' Po redaktoni një faqe që përdoret për tekstin dritares së programit. \nNdryshimet në këtë faqe do të ndikojnë pamjen e dritares për përdoruesit e tjerë.\nPër përkthime, ju lutem konsideroni përdorimin e [//translatewiki.net/wiki/Main_Page?setlang=en translatewiki.net], projektin e lokalizimit MediaWiki.",
+       "translateinterface": "Të shtoni ose të ndryshojë përkthime për të gjitha wikis, ju lutem përdorimin e [//translatewiki.net/ translatewiki.net], MediaWiki lokalizimin e projektit.",
        "cascadeprotected": "Kjo faqe është mbrojtur nga redaktimi pasi është përfshirë në {{PLURAL:$1|faqen|faqet}} e mëposhtme që {{PLURAL:$1|është|janë}} mbrojtur sipas metodës \"cascading\":\n$2",
        "namespaceprotected": "Nuk ju lejohet redaktimi i faqeve në hapsirën '''$1'''.",
        "customcssprotected": "Ju nuk keni leje për të redaktuar këtë faqe CSS, sepse ai përmban cilësimet personale tjetër user's.",
        "mailerror": "Gabim duke dërguar postën: $1",
        "acct_creation_throttle_hit": "Nuk lejoheni të krijoni më llogari pasi keni krijuar {{PLURAL:$1|1|$1}}.",
        "emailauthenticated": "Adresa juaj është vërtetuar më $2 $3.",
-       "emailnotauthenticated": "Adresa juaj <strong>nuk është vërtetuar</strong> akoma prandaj nuk mund të merrni e-mail.",
+       "emailnotauthenticated": "Adresa juaj email nuk është  konfirmuar ende.\nAsnjë email nuk do të dërgohet për ndonjë nga karakteristikat e mëposhtme.",
        "noemailprefs": "Detyrohet një adresë email-i për të përdorur këtë mjet.",
        "emailconfirmlink": "Vërtetoni adresën tuaj",
        "invalidemailaddress": "Posta elektronike nuk mund të pranohet kështu si është pasi ka format jo valid. Ju lutemi, vendoni një postë mirë të formatuar, ose zbrazeni fushën.",
        "createaccount-text": "Dikush ka përdorur adresën tuaj për të hapur një llogari tek {{SITENAME}} ($4) të quajtur \"$2\" me fjalëkalimin \"$3\".\nDuhet të hyni brenda dhe të ndërroni fjalëkalimin tani nëse ky person jeni ju. Përndryshe shpërfilleni këtë mesazh.",
        "login-throttled": "Keni bërë shumë tentime të njëpasnjëshme në fjalëkalimin e kësaj llogarie. Ju lutemi prisni para se te tentoni përsëri.",
        "login-abort-generic": "login juaj ishte i pasuksesshëm - Ndërpre",
+       "login-migrated-generic": "Llogaria juaj ka emigruar, dhe emri juaj nuk ekzistojnë më në këtë wiki.",
        "loginlanguagelabel": "Gjuha: $1",
        "suspicious-userlogout": "Kërkesa juaj për të shkëputet u mohua sepse duket sikur është dërguar nga një shfletues të thyer ose caching proxy.",
        "createacct-another-realname-tip": "* Emri i vërtetë nuk është i domosdoshëm: Nëse e jepni do të përmendeni si kontribues për punën që ke bërë.",
index 95c062a..a3ecde9 100644 (file)
        "zip-wrong-format": "Den angivna filen var inte en ZIP-fil.",
        "zip-bad": "Filen är en skadad eller annars oläsbar ZIP fil.\nDen kan inte säkerhetskontrolleras ordentligt.",
        "zip-unsupported": "Filen är en ZIP-fil som använder ZIP funktioner som inte stöds av MediaWiki.\nDen kan inte säkerhetskontrolleras ordentligt.",
-       "uploadstash": "Ladda upp stash",
+       "uploadstash": "Temporära lagringsytan för uppladdningar",
        "uploadstash-summary": "Denna sida ger tillgång till filer som är uppladdade (eller håller på att laddas upp) men som ännu inte är publicerade till wikin. Dessa filer är inte synliga för någon annan än den användare som laddade upp dem.",
-       "uploadstash-clear": "Rensa stashade filer",
-       "uploadstash-nofiles": "Du har inga stashade filer.",
+       "uploadstash-clear": "Rensa temporärt lagrade filer",
+       "uploadstash-nofiles": "Du har inga temporärt lagrade filer.",
        "uploadstash-badtoken": "Utförandet av den åtgärden misslyckades, kanske för att din redigeringsrättigheter löpt ut. Försök igen.",
        "uploadstash-errclear": "Rensning av filerna misslyckades.",
        "uploadstash-refresh": "Uppdatera listan över filer",
index 171415c..88defd0 100644 (file)
        "lockedbyandtime": "(โดย {{GENDER:$1|$1}} เมื่อวันที่ $2 เวลา $3)",
        "move-page": "ย้าย $1",
        "move-page-legend": "เปลี่ยนชื่อ",
-       "movepagetext": "à¸\81ารà¹\83à¸\8aà¹\89à¹\81à¸\9aà¸\9aà¸\94à¹\89าà¸\99ลà¹\88าà¸\87à¸\88ะสà¹\88à¸\87à¸\9cลà¹\80à¸\9bลีà¹\88ยà¸\99à¸\8aืà¹\88อหà¸\99à¹\89า à¹\81ละยà¹\89ายà¸\9bระวัà¸\95ิà¸\97ัà¹\89à¸\87หมà¸\94à¹\84à¸\9bยัà¸\87à¸\8aืà¹\88อà¹\83หมà¹\88\nà¸\8aืà¹\88อà¹\80รืà¹\88อà¸\87à¹\80à¸\81à¹\88าà¸\88ะà¸\81ลายà¹\80à¸\9bà¹\87à¸\99หà¸\99à¹\89าà¹\80à¸\9bลีà¹\88ยà¸\99à¸\97าà¸\87à¹\84à¸\9bยัà¸\87à¸\8aืà¹\88อà¹\80รืà¹\88อà¸\87à¹\83หมà¹\88\nà¸\84ุà¸\93สามารà¸\96à¸\9bรัà¸\9aà¹\83หà¹\89หà¸\99à¹\89าà¹\80à¸\9bลีà¹\88ยà¸\99à¸\97าà¸\87à¸\97ีà¹\88à¸\8aีà¹\89à¹\84à¸\9bยัà¸\87à¸\8aืà¹\88อà¹\80รืà¹\88อà¸\87à¹\80à¸\94ิมà¹\84à¸\94à¹\89อัà¸\95à¹\82à¸\99มัà¸\95ิ\nà¹\81à¸\95à¹\88หาà¸\81à¸\84ุà¸\93à¹\80ลือà¸\81à¹\84มà¹\88à¸\97ำà¹\80à¸\8aà¹\88à¸\99à¸\99ัà¹\89à¸\99 à¹\83หà¹\89à¹\81à¸\99à¹\88à¹\83à¸\88วà¹\88าà¸\95รวà¸\88สอà¸\9a[[Special:DoubleRedirects|หà¸\99à¹\89าà¹\80à¸\9bลีà¹\88ยà¸\99à¸\97าà¸\87à¸\8bà¹\89ำà¸\8bà¹\89อà¸\99]]หรือ[[Special:BrokenRedirects|หà¸\99à¹\89าà¹\80à¸\9bลีà¹\88ยà¸\99à¸\97าà¸\87à¸\97ีà¹\88à¹\80สีย]]\nà¸\84ุà¸\93à¹\80à¸\9bà¹\87à¸\99à¸\9cูà¹\89รัà¸\9aà¸\9cิà¸\94à¸\8aอà¸\9aà¹\80à¸\9eืà¹\88อà¹\83หà¹\89à¹\81à¸\99à¹\88à¹\83à¸\88วà¹\88าลิà¸\87à¸\81à¹\8cà¸\95à¹\88าà¸\87 à¹\86 à¸¢à¸±à¸\87à¸\8aีà¹\89à¹\84à¸\9bยัà¸\87à¸\97ีà¹\88à¸\97ีà¹\88สมà¸\84วร\n\nà¹\82à¸\9bรà¸\94à¸\97ราà¸\9aวà¹\88าหà¸\99à¹\89าà¸\94ัà¸\87à¸\81ลà¹\88าวà¸\88ะ'''à¹\84มà¹\88'''à¸\96ูà¸\81ยà¹\89าย à¸\96à¹\89ามีหà¸\99à¹\89าà¸\97ีà¹\88à¹\83à¸\8aà¹\89à¸\8aืà¹\88อà¹\80รืà¹\88อà¸\87à¹\83หมà¹\88อยูà¹\88à¹\81ลà¹\89ว à¹\80วà¹\89à¸\99à¹\81à¸\95à¹\88หà¸\99à¹\89าà¸\99ัà¹\89à¸\99à¹\80à¸\9bà¹\87à¸\99หà¸\99à¹\89าà¹\80à¸\9bลีà¹\88ยà¸\99à¸\97าà¸\87 à¹\81ละà¹\84มà¹\88มีà¸\9bระวัà¸\95ิà¸\81ารà¹\81à¸\81à¹\89à¹\84à¸\82à¹\83à¸\99อà¸\94ีà¸\95\nà¸\8bึà¹\88à¸\87หมายà¸\84วามวà¹\88า à¸\84ุà¸\93สามารà¸\96à¹\80à¸\9bลีà¹\88ยà¸\99à¸\8aืà¹\88อหà¸\99à¹\89าà¸\81ลัà¸\9aà¹\80à¸\9bà¹\87à¸\99à¸\8aืà¹\88อà¹\80à¸\94ิมà¹\84à¸\94à¹\89หาà¸\81à¸\84ุà¸\93à¸\97ำà¸\9cิà¸\94à¸\9eลาà¸\94 à¹\81ละà¸\84ุà¸\93à¹\84มà¹\88สามารà¸\96à¹\80à¸\82ียà¸\99à¸\97ัà¸\9aหà¸\99à¹\89าà¸\97ีà¹\88มีอยูà¹\88à¹\81ลà¹\89วà¹\84à¸\94à¹\89\n\n'''à¸\84ำà¹\80à¸\95ือà¸\99!'''\nสิà¹\88à¸\87à¸\99ีà¹\89อาà¸\88à¹\80à¸\9bà¹\87à¸\99à¸\81ารà¹\80à¸\9bลีà¹\88ยà¸\99à¹\81à¸\9bลà¸\87à¸\97ีà¹\88รุà¸\99à¹\81รà¸\87à¹\81ละà¹\84มà¹\88à¸\84าà¸\94à¸\84ิà¸\94สำหรัà¸\9aหà¸\99à¹\89าà¸\97ีà¹\88à¹\80à¸\9bà¹\87à¸\99à¸\97ีà¹\88à¸\99ิยม\nà¹\82à¸\9bรà¸\94à¹\81à¸\99à¹\88à¹\83à¸\88วà¹\88าà¸\84ุà¸\93à¹\80à¸\82à¹\89าà¹\83à¸\88à¸\96ึà¸\87à¸\9cลลัà¸\9eà¸\98à¹\8cà¸\99ีà¹\89à¸\81à¹\88อà¸\99à¸\97ีà¹\88à¸\88ะà¸\94ำà¹\80à¸\99ิà¸\99à¸\81ารà¸\95à¹\88อà¹\84à¸\9b",
+       "movepagetext": "à¸\81ารà¹\83à¸\8aà¹\89à¹\81à¸\9aà¸\9aà¸\94à¹\89าà¸\99ลà¹\88าà¸\87à¸\88ะà¹\80à¸\9bลีà¹\88ยà¸\99à¸\8aืà¹\88อหà¸\99à¹\89า à¹\81ละยà¹\89ายà¸\9bระวัà¸\95ิà¸\97ัà¹\89à¸\87หมà¸\94à¹\84à¸\9bยัà¸\87à¸\8aืà¹\88อà¹\83หมà¹\88\nà¸\8aืà¹\88อà¹\80รืà¹\88อà¸\87à¹\80à¸\81à¹\88าà¸\88ะà¸\81ลายà¹\80à¸\9bà¹\87à¸\99หà¸\99à¹\89าà¹\80à¸\9bลีà¹\88ยà¸\99à¸\97าà¸\87à¹\84à¸\9bยัà¸\87à¸\8aืà¹\88อà¹\80รืà¹\88อà¸\87à¹\83หมà¹\88\nà¸\84ุà¸\93สามารà¸\96à¸\9bรัà¸\9aà¸\81ารà¹\80à¸\9bลีà¹\88ยà¸\99à¸\97าà¸\87à¸\8bึà¹\88à¸\87à¸\8aีà¹\89à¹\84à¸\9bยัà¸\87à¸\8aืà¹\88อà¹\80รืà¹\88อà¸\87à¹\80à¸\94ิมà¹\84à¸\94à¹\89อัà¸\95à¹\82à¸\99มัà¸\95ิ\nà¹\81à¸\95à¹\88หาà¸\81à¸\84ุà¸\93à¹\80ลือà¸\81à¹\84มà¹\88à¸\97ำà¹\80à¸\8aà¹\88à¸\99à¸\99ัà¹\89à¸\99 à¹\83หà¹\89à¹\81à¸\99à¹\88à¹\83à¸\88วà¹\88าà¸\95รวà¸\88สอà¸\9a[[Special:DoubleRedirects|หà¸\99à¹\89าà¹\80à¸\9bลีà¹\88ยà¸\99à¸\97าà¸\87à¸\8bà¹\89ำà¸\8bà¹\89อà¸\99]]หรือ[[Special:BrokenRedirects|หà¸\99à¹\89าà¹\80à¸\9bลีà¹\88ยà¸\99à¸\97าà¸\87à¹\80สีย]]\nà¸\84ุà¸\93à¹\80à¸\9bà¹\87à¸\99à¸\9cูà¹\89รัà¸\9aà¸\9cิà¸\94à¸\8aอà¸\9aà¹\80à¸\9eืà¹\88อà¹\83หà¹\89à¹\81à¸\99à¹\88à¹\83à¸\88วà¹\88าลิà¸\87à¸\81à¹\8cà¸\95à¹\88าà¸\87 à¹\86 à¸¢à¸±à¸\87à¸\8aีà¹\89à¹\84à¸\9bยัà¸\87à¸\97ีà¹\88à¸\97ีà¹\88สมà¸\84วร\n\nà¹\82à¸\9bรà¸\94à¸\97ราà¸\9aวà¹\88าหà¸\99à¹\89าà¸\94ัà¸\87à¸\81ลà¹\88าวà¸\88ะ<strong>à¹\84มà¹\88</strong>à¸\96ูà¸\81ยà¹\89าย à¸\96à¹\89ามีหà¸\99à¹\89าà¸\97ีà¹\88à¹\83à¸\8aà¹\89à¸\8aืà¹\88อà¹\80รืà¹\88อà¸\87à¹\83หมà¹\88à¹\81ลà¹\89ว à¹\80วà¹\89à¸\99à¹\81à¸\95à¹\88หà¸\99à¹\89าà¸\99ัà¹\89à¸\99à¹\80à¸\9bà¹\87à¸\99หà¸\99à¹\89าà¹\80à¸\9bลีà¹\88ยà¸\99à¸\97าà¸\87 à¹\81ละà¹\84มà¹\88มีà¸\9bระวัà¸\95ิà¸\81ารà¹\81à¸\81à¹\89à¹\84à¸\82à¹\83à¸\99อà¸\94ีà¸\95\nà¸\8bึà¹\88à¸\87หมายà¸\84วามวà¹\88า à¸\84ุà¸\93สามารà¸\96à¹\80à¸\9bลีà¹\88ยà¸\99à¸\8aืà¹\88อหà¸\99à¹\89าà¸\81ลัà¸\9aà¹\80à¸\9bà¹\87à¸\99à¸\8aืà¹\88อà¹\80à¸\94ิมà¹\84à¸\94à¹\89หาà¸\81à¸\84ุà¸\93à¸\97ำà¸\9cิà¸\94à¸\9eลาà¸\94 à¹\81ละà¸\84ุà¸\93à¹\84มà¹\88สามารà¸\96à¹\80à¸\82ียà¸\99à¸\97ัà¸\9aหà¸\99à¹\89าà¸\97ีà¹\88มีอยูà¹\88à¹\81ลà¹\89วà¹\84à¸\94à¹\89\n\n<strong>à¸\84ำà¹\80à¸\95ือà¸\99!</strong>\nสิà¹\88à¸\87à¸\99ีà¹\89อาà¸\88à¹\80à¸\9bà¹\87à¸\99à¸\81ารà¹\80à¸\9bลีà¹\88ยà¸\99à¹\81à¸\9bลà¸\87à¸\97ีà¹\88รุà¸\99à¹\81รà¸\87à¹\81ละà¹\84มà¹\88à¸\84าà¸\94à¸\84ิà¸\94สำหรัà¸\9aหà¸\99à¹\89าà¸\97ีà¹\88à¹\80à¸\9bà¹\87à¸\99à¸\97ีà¹\88à¸\99ิยม\nà¹\82à¸\9bรà¸\94à¹\83หà¹\89à¹\81à¸\99à¹\88à¹\83à¸\88วà¹\88าà¸\84ุà¸\93à¹\80à¸\82à¹\89าà¹\83à¸\88à¸\9cลลัà¸\9eà¸\98à¹\8cà¸\99ีà¹\89à¸\81à¹\88อà¸\99à¸\94ำà¹\80à¸\99ิà¸\99à¸\81าร",
        "movepagetext-noredirectfixer": "การใช้แบบด้านล่างจะเปลี่ยนชื่อหน้า ซึ่งจะทำให้ประวัติทั้งหมดย้ายไปยังชื่อใหม่\nชื่อเรื่องเก่าจะกลายเป็นหน้าเปลี่ยนทางไปยังชื่อเรื่องใหม่\nให้แน่ใจว่า ตรวจสอบ[[Special:DoubleRedirects|หน้าเปลี่ยนทางซ้ำซ้อน]]หรือ[[Special:BrokenRedirects|หน้าเปลี่ยนทางที่เสีย]]\nคุณจะเป็นผู้รับผิดชอบเพื่อให้แน่ใจว่าลิงก์ต่าง ๆ ยังชี้ไปยังที่ที่สมควร\n\nโปรดทราบว่าหน้าดังกล่าวจะ'''ไม่'''ถูกย้าย ถ้ามีหน้าที่ใช้ชื่อเรื่องใหม่อยู่แล้ว เว้นแต่เป็นหน้าว่างหรือหน้าเปลี่ยนทาง และไม่มีประวัติการแก้ไขในอดีต\nซึ่งหมายความว่า คุณสามารถเปลี่ยนชื่อหน้ากลับเป็นชื่อเดิมได้หากคุณทำผิดพลาด และคุณไม่สามารถเขียนทับหน้าที่มีอยู่แล้วได้\n\n'''คำเตือน!'''\nสิ่งนี้อาจเป็นการเปลี่ยนแปลงที่รุนแรงและไม่คาดคิดสำหรับหน้าที่เป็นที่นิยม\nโปรดแน่ใจว่าคุณเข้าใจถึงผลลัพธ์นี้ก่อนที่จะดำเนินการต่อไป",
-       "movepagetalktext": "หน้าพูดคุยของหน้านี้จะถูกเปลี่ยนชื่อตามไปโดยอัตโนมัติ '''เว้นแต่:'''\n*มีหน้าพูดคุยภายใต้ชื่อใหม่อยู่แล้ว หรือ\n*คุณไม่เลือกกล่องด้านล่าง\n\nหากเกิดกรณีเหล่านี้ คุณจะต้องย้ายหรือรวมหน้าเองหากต้องการเปลี่ยนชื่อตามในภายหลัง",
+       "movepagetalktext": "หน้าพูดคุยของหน้านี้จะถูกเปลี่ยนชื่อตามไปโดยอัตโนมัติ<strong>เว้นแต่:</strong>\n*มีหน้าพูดคุยซึ่งไม่ว่างภายใต้ชื่อใหม่แล้ว หรือ\n*คุณไม่เลือกกล่องด้านล่าง\n\nในกรณีเหล่านี้ คุณจะต้องย้ายหรือรวมหน้าเองหากต้องการ",
        "movearticle": "เปลี่ยนชื่อ",
-       "moveuserpage-warning": "'''คำเตือน''' คุณกำลังย้ายหน้าผู้ใช้ โปรดทราบว่าหน้าผู้ใช้เท่านั้นที่จะถูกเปลี่ยนชื่อ แต่ผู้ใช้จะ'''ไม่'''ถูกเปลี่ยนชื่อ",
+       "moveuserpage-warning": "<strong>คำเตือน:</strong> คุณกำลังย้ายหน้าผู้ใช้ โปรดทราบว่าหน้าผู้ใช้เท่านั้นที่จะถูกเปลี่ยนชื่อ แต่ผู้ใช้จะ<em>ไม่</em>ถูกเปลี่ยนชื่อ",
        "movecategorypage-warning": "<strong>คำเตือน:</strong> คุณกำลังย้ายหน้าหมวดหมู่ โปรดทราบว่า จะย้ายเฉพาะหน้าและทุกหน้าในหมวดหมู่เก่าจะ<em>ไม่</em>ถูกจัดเข้าหมวดหมู่ใหม่",
        "movenologintext": "ถ้าต้องการเปลี่ยนชื่อหน้านี้ ต้องเป็นผู้ใช้ลงทะเบียนและ[[Special:UserLogin|ล็อกอิน]]",
        "movenotallowed": "คุณไม่มีสิทธิเปลี่ยนชื่อหน้า",
        "imageinvalidfilename": "ชื่อไฟล์เป้าหมายไม่ถูกต้อง",
        "fix-double-redirects": "ปรับทุกหน้าเปลี่ยนทางที่ชี้ไปยังชื่อเรื่องเดิม",
        "move-leave-redirect": "สร้างหน้าเปลี่ยนทางตามมา",
-       "protectedpagemovewarning": "'''คำเตือน:''' หน้านี้ถูกล็อก เฉพาะผู้ใช้ที่มีสิทธิผู้ดูแลระบบเท่านั้นที่ย้ายได้\nปูมการป้องกันล่าสุดถูกแสดงไว้ด้านล่างเพื่อการอ้างอิง:",
+       "protectedpagemovewarning": "<strong>คำเตือน:</strong>  หน้านี้ถูกล็อก เฉพาะผู้ใช้ที่มีสิทธิผู้ดูแลระบบเท่านั้นที่ย้ายได้\nปูมล่าสุดแสดงไว้ด้านล่างเพื่อการอ้างอิง:",
        "semiprotectedpagemovewarning": "'''หมายเหตุ:''' หน้านี้ถูกล็อก เฉพาะผู้ใช้ลงทะเบียนเท่านั้นที่ย้ายได้\nรายการปูมล่าสุดได้ถูกแสดงไว้ด้านล่างนี้เพื่อการอ้างอิง:",
        "move-over-sharedrepo": "== มีไฟล์เดิมปรากฏ ==\nไฟล์ [[:$1]] มีปรากฏเดิมอยู่แล้วในคลังเก็บภาพส่วนกลาง การย้ายไฟล์ที่มีชื่อเรื่องนี้อาจจะเป็นการเขียนทับไฟล์เดิมในคลังเก็บได้",
        "file-exists-sharedrepo": "ชื่อไฟล์นี้มีปรากฏเดิมอยู่แล้วในคลังเก็บภาพส่วนกลาง\nกรุณาเลือกชื่ออื่น",
index 879e8ef..346bf8d 100644 (file)
        "emailsenttext": "E-mail хатыгыз җиберелде.",
        "watchlist": "Күзәтү исемлеге",
        "mywatchlist": "Күзәтү исемлеге",
-       "watchlistfor2": "$1 $2 өчен",
+       "watchlistfor2": "$1 өчен $2",
        "nowatchlist": "Күзәтү исемлегегездә битләр юк.",
        "watchnologin": "Кермәдегез",
        "addedwatchtext": "\"[[:$1]]\" бите [[Special:Watchlist|күзәтү исемлегегезгә]] өстәлде.\nБу биттә һәм аның бәхәслегендә барлык булачак үзгәртүләр шунда күрсәтелер, һәм, [[Special:RecentChanges|соңгы үзгәртүләр]] исемлегендә бу битне җиңелрәк табу өчен, ул '''калын мәтен''' белән күрсәтелер.",
        "sp-contributions-talk": "бәхәс",
        "sp-contributions-search": "Кертемне эзләү",
        "sp-contributions-username": "Кулланучының IP адресы яки исеме:",
-       "sp-contributions-toponly": "Соңгы версия булган үзгәртүләрне генә күрсәтергә",
+       "sp-contributions-toponly": "Соңгы версия булган үзгәртүләрне генә күрсәтелсен",
        "sp-contributions-submit": "Эзләү",
        "whatlinkshere": "Бирегә нәрсә сылтый",
        "whatlinkshere-title": "$1 битенә сылтый торган битләр",
index 00047d7..34f12ac 100644 (file)
@@ -12,8 +12,8 @@
                ]
        },
        "tog-underline": "Холбааны шыяры:",
-       "tog-hideminor": "Сөөлгү өскерлиишкиннер арында бичии өскерлиишкиннерни чажырар",
-       "tog-hidepatrolled": "Ð\90мгÑ\8b Ó©Ñ\81кеÑ\80лииÑ\88киннеÑ\80 Ð°Ñ\80Ñ\8bнда Ð¸Ñ\81Ñ\82Ñ\8dÑ\8dн Ó©Ñ\81кеÑ\80лииÑ\88киннеÑ\80ни Ñ\87ажÑ\8bÑ\80аÑ\80Ñ\8b",
+       "tog-hideminor": "Сөөлгү өскерлиишкиннерниң биче эдиглерин чажырар",
+       "tog-hidepatrolled": "Чаа Ó©Ñ\81кеÑ\80лииÑ\88киннеÑ\80 Ð´Ð°Ò£Ð·Ñ\8bзÑ\8bнда Ñ\85Ñ\8bнаан Ó©Ñ\81кеÑ\80лииÑ\88киннеÑ\80ни Ñ\87ажÑ\8bÑ\80аÑ\80",
        "tog-newpageshidepatrolled": "Чаа арыннарның даңзындан истээн арыннарны чажырары",
        "tog-usenewrc": "Чаа өскерлиишкиннерниң өөделеттинген даңзызын ажыглаар (JavaScript херек)",
        "tog-numberheadings": "Эгелерин авто-санаар",
        "internalerror": "Иштики алдаг",
        "internalerror_info": "Иштики алдаг: $1",
        "badtitle": "Багай ат",
-       "badtitletext": "Негеттинип турар арын ады меге, куруг, чок болза дылдар аразында азы интервики ады шын эвес.\nАдында таарышпас демдектер бары чадапчок.",
+       "badtitletext": "Негеттинип турар арын ады меге, куруг, чок болза өске дылда азы интервикиде ады шын эвес айыттынган.\n\nАттарга ажыглавас ужурлуг демдектер, үжүктер бары чадапчок.",
        "viewsource": "Дөзүн көөрү",
        "actionthrottled": "Шеглээн дүрген",
        "exception-nologin": "Кирбес",
        "recentchanges-summary": "Бо агымда викиниң сөөлгү өскерлиишкиннерин көөрү.",
        "recentchanges-feed-description": "Бо агымда викиниң сөөлгү өскерлиишкиннерин көөрү.",
        "recentchanges-label-newpage": "Бо өскерлиишкин чаа арынны чогааткан.",
-       "recentchanges-label-minor": "Бо өскерлиишкин бичии-дир",
+       "recentchanges-label-minor": "Бо өскерлиишкин бичии",
        "recentchanges-label-bot": "Бо эдилгени робот күүсеткен.",
        "recentchanges-label-unpatrolled": "Бо өскертилге истетинмээн (патрульдаттынмаан)",
+       "recentchanges-label-plusminus": "Арынның сөзүглели бердинген түң байт-биле өскерилген",
        "recentchanges-legend-newpage": "$1 — чаа арын",
-       "rcnotefrom": "Адаанда <strong>$2</strong> тура (<strong>$1</strong> чедир) өскертилгелерни санаан.",
+       "rcnotefrom": "<strong>$2</strong> үеде <strong>$1</strong> чедир өскертилгелерни санаан.",
        "rclistfrom": "$3 $2 тура чаа өскерилгелерни көргүзер",
        "rcshowhideminor": "Бичии өскерлиишкиннерни $1",
+       "rcshowhideminor-show": "көргүзер",
+       "rcshowhideminor-hide": "чажырар",
        "rcshowhidebots": "Роботтарны $1",
        "rcshowhideliu": "Кирген киржикчилерни $1",
-       "rcshowhideanons": "Ады чок ажыглакчыларны $1",
+       "rcshowhideliu-show": "көргүзер",
+       "rcshowhideliu-hide": "чажырар",
+       "rcshowhideanons": "Адыжок киржикчилерни $1",
+       "rcshowhideanons-show": "көргүзер",
+       "rcshowhideanons-hide": "чажырар",
        "rcshowhidepatr": "истээн өскерлиишкиннерни $1",
+       "rcshowhidepatr-show": "көргүзер",
+       "rcshowhidepatr-hide": "чажырар",
        "rcshowhidemine": "Эдиглеримни $1",
+       "rcshowhidemine-show": "көргүзер",
+       "rcshowhidemine-hide": "чажырар",
        "rclinks": "Сөөлгү $2 хүн иштинде болган $1 өскерлиишкиннерни көргүзер<br />$3",
        "diff": "ылгал",
        "hist": "төөгү",
        "allarticles": "Шупту арыннар",
        "allpagessubmit": "Күүcедири",
        "categories": "Аңгылалдар",
-       "sp-deletedcontributions-contribs": "салыышкыннар",
+       "sp-deletedcontributions-contribs": "дадывыр",
        "linksearch": "Даштыкы холбааларга дилээри",
        "linksearch-ns": "Аттар делгеми:",
        "linksearch-ok": "Дилээри",
        "blanknamespace": "(Кол)",
        "contributions": "{{GENDER:$1|Ажыглакчының}} салыышкыннары",
        "contributions-title": "«$1» деп ажыглакчының салыышкыннары",
-       "mycontris": "СалÑ\8bÑ\8bÑ\88кÑ\8bннар",
+       "mycontris": "Ð\94адÑ\8bвÑ\8bр",
        "contribsub2": "$1 ($2)",
        "uctop": "(амгы)",
        "month": "Айдан:",
        "blocklink": "кызыгаарлаары",
        "unblocklink": "ажыдып хостаар",
        "change-blocklink": "кызыгаарлаашкынны өскертири",
-       "contribslink": "салыышкыннар",
+       "contribslink": "дадывыр",
        "blocklogpage": "Кызыгаарлаашкынның журналы",
        "blocklogentry": ", [[$1]] $2 дургузунда кызыгаарлаттынган: $3",
        "block-log-flags-anononly": "чүгле адыжок киржикчилер",
        "metadata-fields": "Бо даңзыда айыткан чурумалдар метаданныйларның кезектери чурумалдың арынынга көстүп кээр, метаданныйлар таблицазын дүрүп каан болур. \nАрткан кезектер аайлаан ёзугаар чажыт көстүр.\n* make\n* model\n* datetimeoriginal\n* exposuretime\n* fnumber\n* isospeedratings\n* focallength\n* artist\n* copyright\n* imagedescription\n* gpslatitude\n* gpslongitude\n* gpsaltitude",
        "exif-imagewidth": "Калбаа",
        "exif-imagelength": "Бедик",
-       "exif-imagedescription": "ЧÑ\83Ñ\80Ñ\83кÑ\82Ñ\83ң ады",
+       "exif-imagedescription": "ЧÑ\83Ñ\80Ñ\83малдÑ\8bң ады",
        "exif-artist": "Чогаадыкчы",
        "exif-usercomment": "Ажыглакчының тайылбырлары",
        "exif-jpegfilecomment": "JPEG фалй тайылбыры",
index a0071b8..e4ccd5e 100644 (file)
@@ -53,7 +53,8 @@
                        "Lamsec",
                        "Olion",
                        "Piramidion",
-                       "Andygol"
+                       "Andygol",
+                       "Ypryima"
                ]
        },
        "tog-underline": "Підкреслювання посилань:",
index 59d636c..4666189 100644 (file)
        "category-subcat-count-limited": "اِس زمرہ میں درج ذیل {{PLURAL:$1|ذیلی زمرہ ہے|$1 ذیلی زمرہ جات ہیں}}.",
        "category-article-count": "{{PLURAL:$2|اس زمرہ میں صرف یہ درج ذیل صفحہ مشمول ہے۔|اس زمرہ کے کل $2 صفحات میں سے $1 {{PLURAL:$1|صفحہ|صفحات}} درج ذیل {{PLURAL:$1|ہے|ہیں}}۔",
        "category-article-count-limited": "یہ درج ذیل {{PLURAL:$1|صفحہ|$1 صفحات}} اس زمرہ میں مشمول {{PLURAL:$1|ہے|ہیں}}۔",
+       "category-file-count-limited": "یہ درج ذیل {{PLURAL:$1|صفحہ|$1 صفحات}} اس زمرہ میں شامل {{PLURAL:$1|ہے|ہیں}}۔",
        "listingcontinuesabbrev": "۔جاری",
+       "index-category": "فہرست شدہ صفحات",
        "noindex-category": "غیر مندرج صفحات",
+       "broken-file-category": "صفحات بمعہ شکستہ فائل روابط",
+       "categoryviewer-pagedlinks": "($1) ($2)",
        "about": "تعارف",
        "article": "صفحۂ مشمول",
        "newwindow": "(نـئی ونـڈو میـں)",
        "actions": "ایکشنز",
        "namespaces": "جائے نام",
        "variants": "متغیرات",
+       "navigation-heading": "قائمہ رہنمائی",
        "errorpagetitle": "خطاء",
        "returnto": "واپس $1۔",
        "tagline": "{{SITENAME}} سے",
index 95e036b..ad7f250 100644 (file)
        "media_tip": "sumpay han paypay",
        "sig_tip": "Imo pirma nga may-ada marka hin oras",
        "hr_tip": "Patumba nga bagis (hinay-hinay la it paggamit)",
-       "summary": "Dalikyat nga sumat hiton pagliwat:",
+       "summary": "Halipotay nga masisiring:",
        "subject": "Katukiban:",
        "minoredit": "Gutiay ini nga pagliwat",
        "watchthis": "Bantayi ini nga pakli",
        "showdiff": "Igpakita an mga ginliwat",
        "anoneditwarning": "'''Pahimatngon:''' Diri ka pa naka log-in.\nAn imo IP address in maitatala ha kaagi hinin pakli han pagliwat.",
        "anonpreviewwarning": "''Diri ka naka-log in.  Mahisusurat an imo IP address ngada ha kanan pakli kaagi hit pagliwat kun igtipig nimo.''",
-       "missingsummary": "'''Pahinumdom:''' Waray ka nagbutang hin dalikyat nga sumat han pagliwat.\nKun pidliton mo an \"{{int:savearticle}}\" utro, an imo ginliwat in matitipig bisan waray hini.",
+       "missingsummary": "<strong>Pahinumdom:</strong> Waray ka humatag hin halipotay nga masisiring hiton pagliwat. Kun pidliton mo an \"{{int:savearticle}}\" utro, an imo ginliwat in matitipig bisan waray hini.",
        "missingcommenttext": "Alayon pagbutang hin komento ha ilarom.",
        "missingcommentheader": "'''Pahinumdom:''' Waray ka humatag hin subject/headline para hini nga komento.  Kun pinduton mo an \"{{int:savearticle}}\" utro, an imo pagliwat in matitipig bisan waray hini.",
        "summary-preview": "Pahiuna nga pagawas han dalikyat nga pulong:",
        "history-feed-description": "Kaagi han pagliwat para hini nga pakli ha wiki",
        "history-feed-item-nocomment": "$1 ha $2",
        "history-feed-empty": "An imo ginpaalayon nga pakli in waray dida.\nBangin ini napara tikang ha wiki, o ginngaranan hin iba.\n\n[[Special:Search|pamilnga ha wiki]] para han may pagkahisumpay nga bag-o nga pakli.",
-       "rev-deleted-comment": "(gintanggal an kaagi han dalikyat nga sumat)",
+       "rev-deleted-comment": "(gintanggal an halipotay nga masisiring hiton pagliwat)",
        "rev-deleted-user": "(gintanggal an agnay hiton gumaramit)",
        "rev-deleted-event": "(gintanggal an talaan han mga buhat)",
        "rev-deleted-user-contribs": "[gintanggal an agnay-hit-gumaramit o IP address - an pagliwat in gintago tikang han mga amot]",
        "revdelete-hide-text": "Rebisyon nga sinurat",
        "revdelete-hide-image": "Tagoon an sulod han paypay",
        "revdelete-hide-name": "Tagoon an buhat ngan kakadtoan",
-       "revdelete-hide-comment": "Dalikyat nga sumat hin pagliwat",
+       "revdelete-hide-comment": "Halipotay nga masisiring hiton pagliwat",
        "revdelete-radio-same": "(ayaw balyu-e)",
        "revdelete-radio-set": "Tinago",
        "revdelete-radio-unset": "Nakikit-an",
        "tooltip-rollback": "An \"libot-pabalik\" in nabalik han (mga) pagliwat hini nga pakli ngadto han kataposan nga nag-amot hin usa ka pidlit",
        "tooltip-undo": "\"Igpawara an ginbuhat (undo)\" in nagbabalik hinin nga pagliwat ngan nabuklad hin pagliwat nga porma ha pahiuna-nga-paggawas nga kahimtang.  Natugot liwat pagdugang hin katadungan ha dinalikyat nga sumat.",
        "tooltip-preferences-save": "Tipiga an mga karuyag",
-       "tooltip-summary": "Pagbutang hin dalikyat nga sumat",
+       "tooltip-summary": "Pagbutang hin halipotay nga masisiring hiton pagliwat",
        "interlanguage-link-title": "$1 – $2",
        "siteuser": "{{SITENAME}} gumaramit $1",
        "anonuser": "{{SITENAME}} waray nagpakilala nga gumaramit $1",
        "htmlform-reset": "Igbalik an mga pinamalyuan",
        "htmlform-selectorother-other": "iba",
        "revdelete-content-hid": "sulod nakatago",
-       "revdelete-summary-hid": "nakatago an dalikyat nga sumat han pagliwat",
+       "revdelete-summary-hid": "An halipotay nga masisiring hiton pagliwat in nakatago",
        "revdelete-uname-hid": "nakatago an agnay-hit-gumaramit",
        "logentry-newusers-newusers": "An gumaramit nga akawnt nga $1 {{GENDER:$2|ginhimo}}",
        "logentry-newusers-create": "An gumaramit nga akawnt nga $1 {{GENDER:$2|ginhimo}}",
index b0c2824..fbff85d 100644 (file)
        "autosumm-replace": "פֿאַרבײַט דעם בלאַט מיט '$1'",
        "autoredircomment": "ווייטערפירן צו [[$1]]",
        "autosumm-new": "געשאַפֿן בלאַט מיט '$1'",
+       "autosumm-newblank": "ליידיגן בלאט געשאפן",
        "watchlistedit-normal-title": "רעדאַקטירן די אויפֿפאַסונג ליסטע",
        "watchlistedit-normal-legend": "אַראָפנעמען בלעטער פון דער אויפֿפאסן ליסטע",
        "watchlistedit-normal-submit": "אַראָפנעמען בלעטער",
index 4cb1ff4..25de8f7 100644 (file)
        "nosuchaction": "无此操作",
        "nosuchactiontext": "URL所指定的操作无效。你所输入的URL地址可能有误,或是使用了错误的链接。这也可能表示{{SITENAME}}所使用软件之中存在漏洞。",
        "nosuchspecialpage": "此特殊页面不存在",
-       "nospecialpagetext": "<strong>您请求了一个无效的特殊页面。</strong>\n\n在[[Special:SpecialPages|{{int:specialpages}}]可以]找到有效的特殊页面的列表。",
+       "nospecialpagetext": "<strong>您请求了一个无效的特殊页面。</strong>\n\n在[[Special:SpecialPages|{{int:specialpages}}]]可以找到有效的特殊页面的列表。",
        "error": "错误",
        "databaseerror": "数据库错误",
        "databaseerror-text": "出现数据库查询错误。这可能表示软件中存在漏洞。",
index 3d27764..5b186fa 100644 (file)
        "searchprofile-advanced-tooltip": "搜尋自訂命名空間",
        "search-result-size": "$1 ($2 個字)",
        "search-result-category-size": "$1 位成員 ($2 個子分類,$3 個檔案)",
-       "search-redirect": "(重新導向 $1)",
+       "search-redirect": "(重新導向 $1)",
        "search-section": "(章節 $1)",
        "search-category": "(分類 $1)",
        "search-file-match": "(符合檔案內容)",
        "group-sysop": "管理員",
        "group-bureaucrat": "行政員",
        "group-suppress": "監督員",
-       "group-all": "(全部)",
+       "group-all": "(全部)",
        "group-user-member": "{{GENDER:$1|使用者}}",
        "group-autoconfirmed-member": "自動確認使用者",
        "group-bot-member": "機器人",
        "booksources-text": "下列清單包含其他銷售新書籍或二手書籍的網站連結,可會有你想尋找書籍的進一部資訊:",
        "booksources-invalid-isbn": "您提供的 ISBN 不正確,請檢查複製的來源是否有誤。",
        "specialloguserlabel": "執行者:",
-       "speciallogtitlelabel": "目標(標題或使用者):",
+       "speciallogtitlelabel": "目標 (標題或使用者):",
        "log": "日誌",
        "all-logs-page": "所有公開日誌",
-       "alllogstext": "合併顯示所有 {{SITENAME}} 中所有類型的日誌。\n您可以點選下拉式選單選擇日誌的類型,指定使用者名稱(區分大小寫)或影響的頁面(區分大小寫)。",
+       "alllogstext": "合併顯示所有 {{SITENAME}} 中所有類型的日誌。\n您可以點選下拉式選單選擇日誌的類型,指定使用者名稱 (區分大小寫) 或影響的頁面 (區分大小寫)。",
        "logempty": "無符合條件的日誌。",
        "log-title-wildcard": "搜尋以此欄位文字為字首的標題",
        "showhideselectedlogentries": "顯示/隱藏已選擇的日誌項目",
        "linksearch-pat": "搜尋關鍵字:",
        "linksearch-ns": "命名空間:",
        "linksearch-ok": "搜尋",
-       "linksearch-text": "可使用萬用字元如 *.wikipedia.org。\n萬用字元必須使用在最上層網域,例如 *.org 。<br />\n支援的{{PLURAL:$2|通訊協定}}有:<code>$1</code> (若未指定則預設使用 http:// 通訊協定)。",
+       "linksearch-text": "可使用萬用字元如 *.wikipedia.org。\n萬用字元必須使用在最上層網域,例如 *.org 。<br />\n支援的{{PLURAL:$2|通訊協定}}有:<code>$1</code>  (若未指定則預設使用 http:// 通訊協定) 。",
        "linksearch-line": "$1 由 $2 所連結",
        "linksearch-error": "萬用字元僅可在主機名稱的開頭使用。",
        "listusersfrom": "顯示使用者開始自:",
        "confirmemail_success": "您的電子郵件已經被確認。您現在可以[[Special:UserLogin|登入]]並使用此網站了。",
        "confirmemail_loggedin": "已確認您的電子郵件位址。",
        "confirmemail_subject": "{{SITENAME}} 電子郵件位址確認",
-       "confirmemail_body": "不明人士(可能是您自己,來自 IP 位址 $1 )已在 {{SITENAME}} 註冊了一個帳號 \"$2\" 並使用了此電子郵件位址。\n\n請確認這個帳號是屬於您的,並使用瀏覽器開啟下方連結以啟用在 {{SITENAME}} 上的電子郵件功能:\n\n$3\n\n若您 *未* 註冊此帳號,\n請開啟下方連結取消電子郵件確認:\n\n$5\n\n此確認代碼會於 $4 過期。",
-       "confirmemail_body_changed": "不明人士 (可能是您自己,來自 IP 位址 $1)  已將在 {{SITENAME}} 帳號 \"$2\" 的電子郵件位址更改至此。\n\n請確認這個帳號是屬於您的,並使用瀏覽器開啟下方連結以啟用在 {{SITENAME}} 上的電子郵件功能:\n\n$3\n\n若您 *未* 註冊此帳號,\n請開啟下方連結取消電子郵件確認:\n\n$5\n\n此確認代碼會於 $4 過期。",
-       "confirmemail_body_set": "不明人士(可能是您自己,來自 IP 位址 $1 )已將在 {{SITENAME}} 帳號 \"$2\" 的電子郵件位址設定至此。\n\n請確認這個帳號是屬於您的,並使用瀏覽器開啟下方連結以啟用在 {{SITENAME}} 上的電子郵件功能:\n\n$3\n\n若您 *未* 註冊此帳號,\n請開啟下方連結取消電子郵件確認:\n\n$5\n\n此確認代碼會於 $4 過期。",
+       "confirmemail_body": "不明人士 (可能是您自己,來自 IP 位址 $1) 已在 {{SITENAME}} 註冊了一個帳號 \"$2\" 並使用了此電子郵件位址。\n\n請確認這個帳號是屬於您的,並使用瀏覽器開啟下方連結以開啟在 {{SITENAME}} 上的電子郵件功能:\n\n$3\n\n若您 *未* 註冊此帳號,\n請開啟下方連結取消電子郵件確認:\n\n$5\n\n此確認代碼會於 $4 過期。",
+       "confirmemail_body_changed": "不明人士 (可能是您自己,來自 IP 位址 $1)  已將在 {{SITENAME}} 帳號 \"$2\" 的電子郵件位址更改至此。\n\n請確認這個帳號是屬於您的,並使用瀏覽器開啟下方連結以開啟在 {{SITENAME}} 上的電子郵件功能:\n\n$3\n\n若您 *未* 註冊此帳號,\n請開啟下方連結取消電子郵件確認:\n\n$5\n\n此確認代碼會於 $4 過期。",
+       "confirmemail_body_set": "不明人士 (可能是您自己,來自 IP 位址 $1) 已將在 {{SITENAME}} 帳號 \"$2\" 的電子郵件位址設定至此。\n\n請確認這個帳號是屬於您的,並使用瀏覽器開啟下方連結以開啟在 {{SITENAME}} 上的電子郵件功能:\n\n$3\n\n若您 *未* 註冊此帳號,\n請開啟下方連結取消電子郵件確認:\n\n$5\n\n此確認代碼會於 $4 過期。",
        "confirmemail_invalidated": "已取消電子郵件位址確認",
        "invalidateemail": "取消電子郵件確認",
        "scarytranscludedisabled": "[Interwiki 轉換代碼不可用]",
        "tags-tag": "標籤名稱",
        "tags-display-header": "在更改清單中的出現方式",
        "tags-description-header": "完整含意說明",
-       "tags-active-header": "啟用?",
+       "tags-active-header": "開啟?",
        "tags-hitcount-header": "已加上標籤的更改",
        "tags-active-yes": "是",
        "tags-active-no": "否",
index f5141f6..0589009 100644 (file)
@@ -1,4 +1,4 @@
-# Doxyfile 1.7.6.1
+# Doxyfile 1.8.6
 
 # This file describes the settings to be used by the documentation system
 # doxygen (www.doxygen.org) for MediaWiki.
@@ -47,31 +47,33 @@ MULTILINE_CPP_IS_BRIEF = NO
 INHERIT_DOCS           = YES
 SEPARATE_MEMBER_PAGES  = NO
 TAB_SIZE               = 4
-ALIASES =      "type{1}=<b> \1 </b>:" \
-               "types{2}=<b> \1 </b> or <b> \2 </b>:" \
-               "types{3}=<b> \1 </b>, <b> \2 </b>, or <b> \3 </b>:" \
-               "arrayof{2}=<b> Array </b> of \2" \
-               "null=\type{Null}" \
-               "boolean=\type{Boolean}" \
-               "bool=\type{Boolean}" \
-               "integer=\type{Integer}" \
-               "int=\type{Integer}" \
-               "string=\type{String}" \
-               "str=\type{String}" \
-               "mixed=\type{Mixed}" \
-               "access=\par Access:\n" \
-               "private=\access private" \
-               "protected=\access protected" \
-               "public=\access public" \
-               "copyright=\note" \
-               "license=\note" \
-               "codeCoverageIgnore="
+ALIASES                = "type{1}=<b> \1 </b>:" \
+                         "types{2}=<b> \1 </b> or <b> \2 </b>:" \
+                         "types{3}=<b> \1 </b>, <b> \2 </b>, or <b> \3 </b>:" \
+                         "arrayof{2}=<b> Array </b> of \2" \
+                         "null=\type{Null}" \
+                         "boolean=\type{Boolean}" \
+                         "bool=\type{Boolean}" \
+                         "integer=\type{Integer}" \
+                         "int=\type{Integer}" \
+                         "string=\type{String}" \
+                         "str=\type{String}" \
+                         "mixed=\type{Mixed}" \
+                         "access=\par Access:\n" \
+                         "private=\access private" \
+                         "protected=\access protected" \
+                         "public=\access public" \
+                         "copyright=\note" \
+                         "license=\note" \
+                         "codeCoverageIgnore="
 TCL_SUBST              =
 OPTIMIZE_OUTPUT_FOR_C  = NO
 OPTIMIZE_OUTPUT_JAVA   = NO
 OPTIMIZE_FOR_FORTRAN   = NO
 OPTIMIZE_OUTPUT_VHDL   = NO
 EXTENSION_MAPPING      =
+MARKDOWN_SUPPORT       = YES
+AUTOLINK_SUPPORT       = YES
 BUILTIN_STL_SUPPORT    = NO
 CPP_CLI_SUPPORT        = NO
 SIP_SUPPORT            = NO
@@ -81,13 +83,13 @@ SUBGROUPING            = YES
 INLINE_GROUPED_CLASSES = NO
 INLINE_SIMPLE_STRUCTS  = NO
 TYPEDEF_HIDES_STRUCT   = NO
-SYMBOL_CACHE_SIZE      = 0
 LOOKUP_CACHE_SIZE      = 2
 #---------------------------------------------------------------------------
 # Build related configuration options
 #---------------------------------------------------------------------------
 EXTRACT_ALL            = YES
 EXTRACT_PRIVATE        = YES
+EXTRACT_PACKAGE        = NO
 EXTRACT_STATIC         = YES
 EXTRACT_LOCAL_CLASSES  = YES
 EXTRACT_LOCAL_METHODS  = NO
@@ -100,6 +102,7 @@ INTERNAL_DOCS          = NO
 CASE_SENSE_NAMES       = YES
 HIDE_SCOPE_NAMES       = NO
 SHOW_INCLUDE_FILES     = YES
+SHOW_GROUPED_MEMB_INC  = NO
 FORCE_LOCAL_INCLUDES   = NO
 INLINE_INFO            = YES
 SORT_MEMBER_DOCS       = YES
@@ -115,14 +118,13 @@ GENERATE_DEPRECATEDLIST= YES
 ENABLED_SECTIONS       =
 MAX_INITIALIZER_LINES  = 30
 SHOW_USED_FILES        = YES
-SHOW_DIRECTORIES       = YES
 SHOW_FILES             = YES
 SHOW_NAMESPACES        = NO
 FILE_VERSION_FILTER    =
 LAYOUT_FILE            =
 CITE_BIB_FILES         =
 #---------------------------------------------------------------------------
-# configuration options related to warning and progress messages
+# Configuration options related to warning and progress messages
 #---------------------------------------------------------------------------
 QUIET                  = NO
 WARNINGS               = YES
@@ -132,7 +134,7 @@ WARN_NO_PARAMDOC       = NO
 WARN_FORMAT            = "$file:$line: $text"
 WARN_LOGFILE           =
 #---------------------------------------------------------------------------
-# configuration options related to the input files
+# Configuration options related to the input files
 #---------------------------------------------------------------------------
 INPUT                  = {{INPUT}}
 INPUT_ENCODING         = UTF-8
@@ -182,7 +184,12 @@ FILE_PATTERNS          = *.c \
 RECURSIVE              = YES
 EXCLUDE                = {{EXCLUDE}}
 EXCLUDE_SYMLINKS       = YES
-EXCLUDE_PATTERNS       = LocalSettings.php AdminSettings.php StartProfiler.php .svn */.git/* {{EXCLUDE_PATTERNS}}
+EXCLUDE_PATTERNS       = LocalSettings.php \
+                         AdminSettings.php \
+                         StartProfiler.php \
+                         .svn \
+                         */.git/* \
+                         {{EXCLUDE_PATTERNS}}
 EXCLUDE_SYMBOLS        =
 EXAMPLE_PATH           =
 EXAMPLE_PATTERNS       = *
@@ -192,8 +199,9 @@ INPUT_FILTER           = "{{INPUT_FILTER}}"
 FILTER_PATTERNS        =
 FILTER_SOURCE_FILES    = NO
 FILTER_SOURCE_PATTERNS =
+USE_MDFILE_AS_MAINPAGE =
 #---------------------------------------------------------------------------
-# configuration options related to source browsing
+# Configuration options related to source browsing
 #---------------------------------------------------------------------------
 SOURCE_BROWSER         = YES
 INLINE_SOURCES         = NO
@@ -201,16 +209,17 @@ STRIP_CODE_COMMENTS    = YES
 REFERENCED_BY_RELATION = YES
 REFERENCES_RELATION    = YES
 REFERENCES_LINK_SOURCE = YES
+SOURCE_TOOLTIPS        = YES
 USE_HTAGS              = NO
 VERBATIM_HEADERS       = YES
 #---------------------------------------------------------------------------
-# configuration options related to the alphabetical class index
+# Configuration options related to the alphabetical class index
 #---------------------------------------------------------------------------
 ALPHABETICAL_INDEX     = NO
 COLS_IN_ALPHA_INDEX    = 5
 IGNORE_PREFIX          =
 #---------------------------------------------------------------------------
-# configuration options related to the HTML output
+# Configuration options related to the HTML output
 #---------------------------------------------------------------------------
 GENERATE_HTML          = YES
 HTML_OUTPUT            = html
@@ -218,13 +227,14 @@ HTML_FILE_EXTENSION    = .html
 HTML_HEADER            =
 HTML_FOOTER            =
 HTML_STYLESHEET        =
+HTML_EXTRA_STYLESHEET  =
 HTML_EXTRA_FILES       =
 HTML_COLORSTYLE_HUE    = 220
 HTML_COLORSTYLE_SAT    = 100
 HTML_COLORSTYLE_GAMMA  = 80
 HTML_TIMESTAMP         = YES
-HTML_ALIGN_MEMBERS     = YES
 HTML_DYNAMIC_SECTIONS  = NO
+HTML_INDEX_NUM_ENTRIES = 100
 GENERATE_DOCSET        = NO
 DOCSET_FEEDNAME        = "Doxygen generated docs"
 DOCSET_BUNDLE_ID       = org.doxygen.Project
@@ -248,20 +258,26 @@ QHG_LOCATION           =
 GENERATE_ECLIPSEHELP   = NO
 ECLIPSE_DOC_ID         = org.doxygen.Project
 DISABLE_INDEX          = NO
-ENUM_VALUES_PER_LINE   = 4
 GENERATE_TREEVIEW      = YES
-USE_INLINE_TREES       = YES
+ENUM_VALUES_PER_LINE   = 4
 TREEVIEW_WIDTH         = 250
 EXT_LINKS_IN_WINDOW    = NO
 FORMULA_FONTSIZE       = 10
 FORMULA_TRANSPARENT    = YES
 USE_MATHJAX            = NO
+MATHJAX_FORMAT         = HTML-CSS
 MATHJAX_RELPATH        = http://www.mathjax.org/mathjax
 MATHJAX_EXTENSIONS     =
+MATHJAX_CODEFILE       =
 SEARCHENGINE           = YES
 SERVER_BASED_SEARCH    = YES
+EXTERNAL_SEARCH        = NO
+SEARCHENGINE_URL       =
+SEARCHDATA_FILE        = searchdata.xml
+EXTERNAL_SEARCH_ID     =
+EXTRA_SEARCH_MAPPINGS  =
 #---------------------------------------------------------------------------
-# configuration options related to the LaTeX output
+# Configuration options related to the LaTeX output
 #---------------------------------------------------------------------------
 GENERATE_LATEX         = NO
 LATEX_OUTPUT           = latex
@@ -272,6 +288,7 @@ PAPER_TYPE             = a4wide
 EXTRA_PACKAGES         =
 LATEX_HEADER           =
 LATEX_FOOTER           =
+LATEX_EXTRA_FILES      =
 PDF_HYPERLINKS         = YES
 USE_PDFLATEX           = YES
 LATEX_BATCHMODE        = NO
@@ -279,7 +296,7 @@ LATEX_HIDE_INDICES     = NO
 LATEX_SOURCE_CODE      = NO
 LATEX_BIB_STYLE        = plain
 #---------------------------------------------------------------------------
-# configuration options related to the RTF output
+# Configuration options related to the RTF output
 #---------------------------------------------------------------------------
 GENERATE_RTF           = NO
 RTF_OUTPUT             = rtf
@@ -288,14 +305,14 @@ RTF_HYPERLINKS         = NO
 RTF_STYLESHEET_FILE    =
 RTF_EXTENSIONS_FILE    =
 #---------------------------------------------------------------------------
-# configuration options related to the man page output
+# Configuration options related to the man page output
 #---------------------------------------------------------------------------
 GENERATE_MAN           = {{GENERATE_MAN}}
 MAN_OUTPUT             = man
 MAN_EXTENSION          = .3
 MAN_LINKS              = NO
 #---------------------------------------------------------------------------
-# configuration options related to the XML output
+# Configuration options related to the XML output
 #---------------------------------------------------------------------------
 GENERATE_XML           = NO
 XML_OUTPUT             = xml
@@ -303,11 +320,16 @@ XML_SCHEMA             =
 XML_DTD                =
 XML_PROGRAMLISTING     = YES
 #---------------------------------------------------------------------------
-# configuration options for the AutoGen Definitions output
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+GENERATE_DOCBOOK       = NO
+DOCBOOK_OUTPUT         = docbook
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
 #---------------------------------------------------------------------------
 GENERATE_AUTOGEN_DEF   = NO
 #---------------------------------------------------------------------------
-# configuration options related to the Perl module output
+# Configuration options related to the Perl module output
 #---------------------------------------------------------------------------
 GENERATE_PERLMOD       = NO
 PERLMOD_LATEX          = NO
@@ -326,18 +348,20 @@ PREDEFINED             =
 EXPAND_AS_DEFINED      =
 SKIP_FUNCTION_MACROS   = YES
 #---------------------------------------------------------------------------
-# Configuration::additions related to external references
+# Configuration options related to external references
 #---------------------------------------------------------------------------
 TAGFILES               =
 GENERATE_TAGFILE       = {{OUTPUT_DIRECTORY}}/html/tagfile.xml
 ALLEXTERNALS           = NO
 EXTERNAL_GROUPS        = YES
+EXTERNAL_PAGES         = YES
 PERL_PATH              = /usr/bin/perl
 #---------------------------------------------------------------------------
 # Configuration options related to the dot tool
 #---------------------------------------------------------------------------
 CLASS_DIAGRAMS         = NO
 MSCGEN_PATH            =
+DIA_PATH               =
 HIDE_UNDOC_RELATIONS   = YES
 HAVE_DOT               = {{HAVE_DOT}}
 DOT_NUM_THREADS        = 0
@@ -348,6 +372,7 @@ CLASS_GRAPH            = YES
 COLLABORATION_GRAPH    = YES
 GROUP_GRAPHS           = YES
 UML_LOOK               = NO
+UML_LIMIT_NUM_FIELDS   = 10
 TEMPLATE_RELATIONS     = NO
 INCLUDE_GRAPH          = YES
 INCLUDED_BY_GRAPH      = YES
@@ -360,10 +385,10 @@ INTERACTIVE_SVG        = NO
 DOT_PATH               =
 DOTFILE_DIRS           =
 MSCFILE_DIRS           =
+DIAFILE_DIRS           =
 DOT_GRAPH_MAX_NODES    = 50
 MAX_DOT_GRAPH_DEPTH    = 1000
 DOT_TRANSPARENT        = NO
 DOT_MULTI_TARGETS      = YES
 GENERATE_LEGEND        = YES
 DOT_CLEANUP            = YES
-
index d740f56..1d558d2 100644 (file)
@@ -446,7 +446,7 @@ abstract class Maintenance {
                $this->addOption( 'server', "The protocol and server name to use in URLs, e.g. " .
                        "http://en.wikipedia.org. This is sometimes necessary because " .
                        "server name detection may fail in command line scripts.", false, true );
-               $this->addOption( 'profiler', 'Set to "text" or "trace" to show profiling output', false, true );
+               $this->addOption( 'profiler', 'Profiler output format (usually "text")', false, true );
 
                # Save generic options to display them separately in help
                $this->mGenericParameters = $this->mParams;
@@ -597,6 +597,23 @@ abstract class Maintenance {
                }
        }
 
+       /**
+        * Activate the profiler (assuming $wgProfiler is set)
+        */
+       protected function activateProfiler() {
+               global $wgProfiler;
+
+               $output = $this->getOption( 'profiler' );
+               if ( $output && is_array( $wgProfiler ) ) {
+                       $class = $wgProfiler['class'];
+                       $profiler = new $class(
+                               array( 'sampling' => 1, 'output' => $output ) + $wgProfiler
+                       );
+                       $profiler->setTemplated( true );
+                       Profiler::replaceStubInstance( $profiler );
+               }
+       }
+
        /**
         * Clear all params and arguments.
         */
@@ -920,6 +937,9 @@ abstract class Maintenance {
                        LBFactory::destroyInstance();
                }
 
+               // Per-script profiling; useful for debugging
+               $this->activateProfiler();
+
                $this->afterFinalSetup();
 
                $wgShowSQLErrors = true;
@@ -930,16 +950,6 @@ abstract class Maintenance {
                // @codingStandardsIgnoreStart
 
                $this->adjustMemoryLimit();
-
-               // Per-script profiling; useful for debugging
-               $forcedProfiler = $this->getOption( 'profiler' );
-               if ( $forcedProfiler === 'text' ) {
-                       Profiler::setInstance( new ProfilerSimpleText( array() ) );
-                       Profiler::instance()->setTemplated( true );
-               } elseif ( $forcedProfiler === 'trace' ) {
-                       Profiler::setInstance( new ProfilerSimpleTrace( array() ) );
-                       Profiler::instance()->setTemplated( true );
-               }
        }
 
        /**
index 5f77637..f13cb44 100644 (file)
@@ -354,6 +354,8 @@ class TextPassDumper extends BackupDumper {
                $this->lastName = "";
                $this->thisPage = 0;
                $this->thisRev = 0;
+               $this->thisRevModel = null;
+               $this->thisRevFormat = null;
 
                $parser = xml_parser_create( "UTF-8" );
                xml_parser_set_option( $parser, XML_OPTION_CASE_FOLDING, false );
@@ -421,8 +423,34 @@ class TextPassDumper extends BackupDumper {
                return true;
        }
 
+       /**
+        * Applies applicable export transformations to $text.
+        *
+        * @param string $text
+        * @param string $model
+        * @param string|null $format
+        *
+        * @return string
+        */
+       private function exportTransform( $text, $model, $format = null ) {
+               try {
+                       $handler = ContentHandler::getForModelID( $model );
+                       $text = $handler->exportTransform( $text, $format );
+               }
+               catch ( MWException $ex ) {
+                       $this->progress(
+                               "Unable to apply export transformation for content model '$model': " .
+                               $ex->getMessage()
+                       );
+               }
+
+               return $text;
+       }
+
        /**
         * Tries to get the revision text for a revision id.
+        * Export transformations are applied if the content model can is given or can be
+        * determined from the database.
         *
         * Upon errors, retries (Up to $this->maxFailures tries each call).
         * If still no good revision get could be found even after this retrying, "" is returned.
@@ -431,11 +459,14 @@ class TextPassDumper extends BackupDumper {
         * is thrown.
         *
         * @param string $id The revision id to get the text for
+        * @param string|bool|null $model The content model used to determine applicable export transformations.
+        *      If $model is null, it will be determined from the database.
+        * @param string|null $format The content format used when applying export transformations.
         *
-        * @return string The revision text for $id, or ""
         * @throws MWException
+        * @return string The revision text for $id, or ""
         */
-       function getText( $id ) {
+       function getText( $id, $model = null, $format = null ) {
                global $wgContentHandlerUseDB;
 
                $prefetchNotTried = true; // Whether or not we already tried to get the text via prefetch.
@@ -453,6 +484,24 @@ class TextPassDumper extends BackupDumper {
                $oldConsecutiveFailedTextRetrievals = $consecutiveFailedTextRetrievals;
                $consecutiveFailedTextRetrievals = 0;
 
+               if ( $model === null && $wgContentHandlerUseDB ) {
+                       $row = $this->db->selectRow(
+                               'revision',
+                               array( 'rev_content_model', 'rev_content_format' ),
+                               array( 'rev_id' => $this->thisRev ),
+                               __METHOD__
+                       );
+
+                       if ( $row ) {
+                               $model = $row->rev_content_model;
+                               $format = $row->rev_content_format;
+                       }
+               }
+
+               if ( $model === null || $model === '' ) {
+                       $model = false;
+               }
+
                while ( $failures < $this->maxFailures ) {
 
                        // As soon as we found a good text for the $id, we will return immediately.
@@ -469,9 +518,19 @@ class TextPassDumper extends BackupDumper {
                                        $tryIsPrefetch = true;
                                        $text = $this->prefetch->prefetch( intval( $this->thisPage ),
                                                intval( $this->thisRev ) );
+
                                        if ( $text === null ) {
                                                $text = false;
                                        }
+
+                                       if ( is_string( $text ) && $model !== false ) {
+                                               // Apply export transformation to text coming from an old dump.
+                                               // The purpose of this transformation is to convert up from legacy
+                                               // formats, which may still be used in the older dump that is used
+                                               // for pre-fetching. Applying the transformation again should not
+                                               // interfere with content that is already in the correct form.
+                                               $text = $this->exportTransform( $text, $model, $format );
+                                       }
                                }
 
                                if ( $text === false ) {
@@ -483,6 +542,12 @@ class TextPassDumper extends BackupDumper {
                                                $text = $this->getTextDb( $id );
                                        }
 
+                                       if ( $text !== false && $model !== false ) {
+                                               // Apply export transformation to text coming from the database.
+                                               // Prefetched text should already have transformations applied.
+                                               $text = $this->exportTransform( $text, $model, $format );
+                                       }
+
                                        // No more checks for texts from DB for now.
                                        // If we received something that is not false,
                                        // We treat it as good text, regardless of whether it actually is or is not
@@ -504,21 +569,8 @@ class TextPassDumper extends BackupDumper {
                                        throw new MWException( "No database available" );
                                }
 
-                               $revLength = strlen( $text );
-                               if ( $wgContentHandlerUseDB ) {
-                                       $row = $this->db->selectRow(
-                                               'revision',
-                                               array( 'rev_len', 'rev_content_model' ),
-                                               array( 'rev_id' => $revID ),
-                                               __METHOD__
-                                       );
-                                       if ( $row ) {
-                                               // only check the length for the wikitext content handler,
-                                               // it's a wasted (and failed) check otherwise
-                                               if ( $row->rev_content_model == CONTENT_MODEL_WIKITEXT ) {
-                                                       $revLength = $row->rev_len;
-                                               }
-                                       }
+                               if ( $model !== CONTENT_MODEL_WIKITEXT ) {
+                                       $revLength = strlen( $text );
                                } else {
                                        $revLength = $this->db->selectField( 'revision', 'rev_len', array( 'rev_id' => $revID ) );
                                }
@@ -757,7 +809,14 @@ class TextPassDumper extends BackupDumper {
                }
 
                if ( $name == "text" && isset( $attribs['id'] ) ) {
-                       $text = $this->getText( $attribs['id'] );
+                       $id = $attribs['id'];
+                       $model = trim( $this->thisRevModel );
+                       $format = trim( $this->thisRevFormat );
+
+                       $model = $model === '' ? null : $model;
+                       $format = $format === '' ? null : $format;
+
+                       $text = $this->getText( $id, $model, $format );
                        $this->openElement = array( $name, array( 'xml:space' => 'preserve' ) );
                        if ( strlen( $text ) > 0 ) {
                                $this->characterData( $parser, $text );
@@ -780,6 +839,8 @@ class TextPassDumper extends BackupDumper {
                        $this->egress->writeRevision( null, $this->buffer );
                        $this->buffer = "";
                        $this->thisRev = "";
+                       $this->thisRevModel = null;
+                       $this->thisRevFormat = null;
                } elseif ( $name == 'page' ) {
                        if ( !$this->firstPageWritten ) {
                                $this->firstPageWritten = trim( $this->thisPage );
@@ -834,6 +895,13 @@ class TextPassDumper extends BackupDumper {
                                $this->thisPage .= $data;
                        }
                }
+               elseif ( $this->lastName == "model"  ) {
+                       $this->thisRevModel .= $data;
+               }
+               elseif ( $this->lastName == "format"  ) {
+                       $this->thisRevFormat .= $data;
+               }
+
                // have to skip the newline left over from closepagetag line of
                // end of checkpoint files. nasty hack!!
                if ( $this->checkpointJustWritten ) {
index 86c686b..2e252ad 100644 (file)
@@ -21,6 +21,8 @@
  * @todo document
  * @ingroup Maintenance
  */
+use \Cdb\Exception as CdbException;
+use \Cdb\Reader as CdbReader;
 
 /** */
 require_once __DIR__ . '/commandLine.inc';
index fc676b8..3e2c6c9 100644 (file)
@@ -32,7 +32,8 @@ require_once __DIR__ . '/Maintenance.php';
 class FetchText extends Maintenance {
        public function __construct() {
                parent::__construct();
-               $this->mDescription = "Fetch the revision text from an old_id";
+               $this->mDescription = "Fetch the raw revision blob from an old_id.";
+               $this->mDescription .= "\nNOTE: Export transformations are NOT applied. This is left to backupTextPass.php";
        }
 
        /**
index 5e4cac6..b8caa4d 100644 (file)
@@ -21,5 +21,5 @@ foreach ( glob( $base . '/*.php' ) as $file ) {
 $generator->forceClassPath( 'MyLocalSettingsGenerator', "$base/mw-config/overrides.php" );
 
 // Write out the autoload
-$generator->generateAutoload();
+$generator->generateAutoload( 'maintenance/generateLocalAutoload.php' );
 
index 145749a..145905f 100644 (file)
@@ -70,8 +70,7 @@
                                        "mw.log",
                                        "mw.inspect",
                                        "mw.inspect.reports",
-                                       "mw.Debug",
-                                       "mw.Debug.profile"
+                                       "mw.Debug"
                                ]
                        }
                ]
index 651f211..a20b83b 100755 (executable)
@@ -1,95 +1,56 @@
 #!/usr/bin/env bash
 
-# This script generates a commit that updates our distribution copy of OOjs UI
+# This script generates a commit that updates our copy of OOjs UI
 
-if [ -z "$1" ]
+if [ -n "$2" ]
 then
-       # Missing required parameter
-       echo >&2 "Usage: $0 path/to/repo/for/oojs-ui"
+       # Too many parameters
+       echo >&2 "Usage: $0 [<version>]"
        exit 1
 fi
 
-TARGET_REPO=$(cd "$(dirname $0)/../.."; pwd)
-TARGET_DIR=resources/lib/oojs-ui
-UI_REPO=$1
-
-function oojsuihash() {
-       grep "OOjs UI v" "$TARGET_REPO/$TARGET_DIR/oojs-ui.js" \
-               | head -n 1 \
-               | grep -Eo '\([a-z0-9]+\)' \
-               | sed 's/^(//' \
-               | sed 's/)$//'
-}
-
-function oojsuitag() {
-       grep "OOjs UI v" "$TARGET_REPO/$TARGET_DIR/oojs-ui.js" \
-               | head -n 1 \
-               | grep -Eo '\bv[0-9a-z.-]+\b'
-}
-
-function oojsuiversion() {
-       grep "OOjs UI v" "$TARGET_REPO/$TARGET_DIR/oojs-ui.js" \
-               | head -n 1 \
-               | grep -Eo '\bv[0-9a-z.-]+\b.*$'
-}
+REPO_DIR=$(cd "$(dirname $0)/../.."; pwd) # Root dir of the git repo working tree
+TARGET_DIR="resources/lib/oojs-ui" # Destination relative to the root of the repo
+NPM_DIR=$(mktemp -d 2>/dev/null || mktemp -d -t 'update-oojs-ui') # e.g. /tmp/update-oojs-ui.rI0I5Vir
 
 # Prepare working tree
-cd "$TARGET_REPO" &&
+cd "$REPO_DIR" &&
 git reset $TARGET_DIR && git checkout $TARGET_DIR && git fetch origin &&
-git checkout -B upstream-oojsui origin/master || exit 1
-
-cd $UI_REPO || exit 1
+git checkout -B upstream-oojs-ui origin/master || exit 1
 
-# Read the old version and check for changes
-OLDHASH=$(oojsuihash)
-if [ -z "$OLDHASH" ]
+# Fetch upstream version
+cd $NPM_DIR
+if [ -n "$1" ]
 then
-       OLDTAG=$(oojsuitag)
+       npm install "oojs-ui@$1" || exit 1
+else
+       npm install oojs-ui || exit 1
 fi
-if [ "$OLDHASH" == "" ]
-then
-       OLDHASH=$(git rev-parse "$OLDTAG")
-       if [ $? != 0 ]
-       then
-               echo "Could not find OOjs UI version"
-               cd -
-               exit 1
-       fi
-fi
-if [ "$(git rev-parse $OLDHASH)" == "$(git rev-parse HEAD)" ]
+
+OOJSUI_VERSION=$(node -e 'console.log(require("./node_modules/oojs-ui/package.json").version);')
+if [ "$OOJSUI_VERSION" == "" ]
 then
-       echo "No changes (already at $OLDHASH)"
-       cd -
-       exit 0
+       echo 'Could not find OOjs UI version'
+       exit 1
 fi
 
-# Build the distribution
-npm install && grunt git-build || exit 1
-
-# Get the list of changes
-NEWCHANGES=$(git log $OLDHASH.. --oneline --no-merges --reverse --color=never)
-NEWCHANGESDISPLAY=$(git log $OLDHASH.. --oneline --no-merges --reverse --color=always)
-
 # Copy files
 # - Exclude the minimised distribution files and RTL sheets for non-CSSJanus environments
-rsync --recursive --delete --force --exclude 'oojs-ui*.min.*' --exclude 'oojs-ui*.rtl.css' ./dist/ "$TARGET_REPO/$TARGET_DIR" || exit 1
+rsync --force --recursive --delete --exclude 'oojs-ui*.min.*' --exclude 'oojs-ui*.rtl.css' ./node_modules/oojs-ui/dist/ "$REPO_DIR/$TARGET_DIR" || exit 1
 
-# Read the new version
-NEWVERSION=$(oojsuiversion)
+# Clean up temporary area
+rm -rf "$NPM_DIR"
 
 # Generate commit
-cd "$TARGET_REPO"
+cd $REPO_DIR || exit 1
+
 COMMITMSG=$(cat <<END
-Update OOjs UI to $NEWVERSION
+Update OOjs UI to v$OOJSUI_VERSION
 
-New changes:
-$NEWCHANGES
+Release notes:
+ https://git.wikimedia.org/blob/oojs%2Fui.git/v$OOJSUI_VERSION/History.md
 END
 )
-git add -u $TARGET_DIR && git add $TARGET_DIR && git commit -m "$COMMITMSG"
-cat >&2 <<END
-
 
-Created commit with changes:
-$NEWCHANGESDISPLAY
-END
+# Stage deletion, modification and creation of files. Then commit.
+git add --update $TARGET_DIR && git add $TARGET_DIR && git commit -m "$COMMITMSG" || exit 1
index d9e6fb9..1d5c2b1 100755 (executable)
@@ -1,5 +1,7 @@
 #!/usr/bin/env bash
 
+# This script generates a commit that updates our copy of OOjs
+
 if [ -n "$2" ]
 then
        # Too many parameters
@@ -25,7 +27,7 @@ else
        npm install oojs || exit 1
 fi
 
-OOJS_VERSION=$(node -e 'console.log(JSON.parse(require("fs").readFileSync("./node_modules/oojs/package.json")).version);')
+OOJS_VERSION=$(node -e 'console.log(require("./node_modules/oojs/package.json").version);')
 if [ "$OOJS_VERSION" == "" ]
 then
        echo 'Could not find OOjs version'
index 762af69..b930f42 100644 (file)
@@ -384,7 +384,7 @@ if ( isset( $_REQUEST['filter'] ) ) {
        $last = false;
        foreach ( $res as $o ) {
                $next = new profile_point( $o->pf_name, $o->pf_count, $o->pf_time, $o->pf_memory );
-               if ( $next->name() == '-total' ) {
+               if ( $next->name() == '-total' || $next->name() == 'main()' ) {
                        profile_point::$totaltime = $next->time();
                        profile_point::$totalcount = $next->count();
                        profile_point::$totalmemory = $next->memory();
index 6a7b35a..c39ba3b 100644 (file)
@@ -835,11 +835,9 @@ return array(
        'mediawiki.debug' => array(
                'scripts' => array(
                        'resources/src/mediawiki/mediawiki.debug.js',
-                       'resources/src/mediawiki/mediawiki.debug.profile.js'
                ),
                'styles' => array(
                        'resources/src/mediawiki/mediawiki.debug.less',
-                       'resources/src/mediawiki/mediawiki.debug.profile.css'
                ),
                'dependencies' => array(
                        'jquery.footHovzer',
@@ -1055,7 +1053,10 @@ return array(
                'group' => 'mediawiki.action.history',
        ),
        'mediawiki.action.history.diff' => array(
-               'styles' => 'resources/src/mediawiki.action/mediawiki.action.history.diff.css',
+               'styles' => array(
+                       'resources/src/mediawiki.action/mediawiki.action.history.diff.css',
+                       'resources/src/mediawiki.action/mediawiki.action.history.diff.print.css' => array( 'media' => 'print' ),
+               ),
                'group' => 'mediawiki.action.history',
                'targets' => array( 'desktop', 'mobile' ),
        ),
index 25fb5f5..1247241 100644 (file)
@@ -10,5 +10,6 @@
        "ooui-outline-control-remove": "ДӀадаха меттиг",
        "ooui-toolbar-more": "Кхин",
        "ooui-dialog-message-accept": "ХӀаъ",
-       "ooui-dialog-message-reject": "Цаоьшу"
+       "ooui-dialog-message-reject": "Цаоьшу",
+       "ooui-dialog-process-continue": "Кхин дӀа"
 }
diff --git a/resources/lib/oojs-ui/i18n/crh-cyrl.json b/resources/lib/oojs-ui/i18n/crh-cyrl.json
new file mode 100644 (file)
index 0000000..ccc0026
--- /dev/null
@@ -0,0 +1,8 @@
+{
+       "@metadata": {
+               "authors": [
+                       "Don Alessandro"
+               ]
+       },
+       "ooui-toolbar-more": "Даа зияде"
+}
diff --git a/resources/lib/oojs-ui/i18n/crh-latn.json b/resources/lib/oojs-ui/i18n/crh-latn.json
new file mode 100644 (file)
index 0000000..7ad7b0b
--- /dev/null
@@ -0,0 +1,8 @@
+{
+       "@metadata": {
+               "authors": [
+                       "Don Alessandro"
+               ]
+       },
+       "ooui-toolbar-more": "Daa ziyade"
+}
index 2854f0b..1db9aed 100644 (file)
@@ -10,7 +10,8 @@
                        "Mormegil",
                        "Polda18",
                        "Tchoř",
-                       "ශ්වෙත"
+                       "ශ්වෙත",
+                       "Vojtěch Dostál"
                ]
        },
        "ooui-outline-control-move-down": "Přesunout položku dolů",
@@ -23,5 +24,6 @@
        "ooui-dialog-message-reject": "Storno",
        "ooui-dialog-process-error": "Něco se pokazilo",
        "ooui-dialog-process-dismiss": "Zavřít",
-       "ooui-dialog-process-retry": "Zkusit znovu"
+       "ooui-dialog-process-retry": "Zkusit znovu",
+       "ooui-dialog-process-continue": "Pokračovat"
 }
index 82699a3..915791e 100644 (file)
@@ -28,5 +28,6 @@
        "ooui-dialog-message-reject": "Cancelar",
        "ooui-dialog-process-error": "Algo salió mal",
        "ooui-dialog-process-dismiss": "Descartar",
-       "ooui-dialog-process-retry": "Intentar de nuevo"
+       "ooui-dialog-process-retry": "Intentar de nuevo",
+       "ooui-dialog-process-continue": "Continuar"
 }
index 6262293..3fb4110 100644 (file)
@@ -26,5 +26,6 @@
        "ooui-dialog-message-reject": "Peruuta",
        "ooui-dialog-process-error": "Jokin meni pieleen",
        "ooui-dialog-process-dismiss": "Hylkää",
-       "ooui-dialog-process-retry": "Yritä uudelleen"
+       "ooui-dialog-process-retry": "Yritä uudelleen",
+       "ooui-dialog-process-continue": "Jatka"
 }
index def0346..9144cb0 100644 (file)
@@ -26,7 +26,8 @@
                        "Urhixidur",
                        "Verdy p",
                        "Wyz",
-                       "SnowedEarth"
+                       "SnowedEarth",
+                       "Jdforrester"
                ]
        },
        "ooui-outline-control-move-down": "Faire descendre l’élément",
        "ooui-outline-control-remove": "Supprimer l’élément",
        "ooui-toolbar-more": "Plus",
        "ooui-toolgroup-expand": "Plus",
+       "ooui-toolgroup-collapse": "Moins",
        "ooui-dialog-message-accept": "OK",
        "ooui-dialog-message-reject": "Annuler",
        "ooui-dialog-process-error": "Quelque chose a mal tourné",
        "ooui-dialog-process-dismiss": "Rejeter",
-       "ooui-dialog-process-retry": "Réessayez"
+       "ooui-dialog-process-retry": "Réessayez",
+       "ooui-dialog-process-continue": "Continuer"
 }
index 9117a05..d50e62d 100644 (file)
        "ooui-outline-control-move-up": "Elem mozgatása felfelé",
        "ooui-outline-control-remove": "Elem eltávolítása",
        "ooui-toolbar-more": "Tovább...",
+       "ooui-toolgroup-expand": "Több",
+       "ooui-toolgroup-collapse": "Kevesebb",
        "ooui-dialog-message-accept": "OK",
        "ooui-dialog-message-reject": "Mégse",
        "ooui-dialog-process-dismiss": "Elrejt",
-       "ooui-dialog-process-retry": "Próbáld újra"
+       "ooui-dialog-process-retry": "Próbáld újra",
+       "ooui-dialog-process-continue": "Folytatás"
 }
index ebb2860..2aaf4e4 100644 (file)
        "ooui-outline-control-move-up": "Բարձրացնել կետը",
        "ooui-outline-control-remove": "Հեռացնել տարրը",
        "ooui-toolbar-more": "Ավելին",
+       "ooui-toolgroup-expand": "Ավելին",
+       "ooui-toolgroup-collapse": "Պակաս",
        "ooui-dialog-message-accept": "Լավ",
        "ooui-dialog-message-reject": "Չեղարկել",
        "ooui-dialog-process-error": "Ինչ-որ սխալ է տեղի ունեցել",
        "ooui-dialog-process-dismiss": "Փակել",
-       "ooui-dialog-process-retry": "Կրկին փորձել"
+       "ooui-dialog-process-retry": "Կրկին փորձել",
+       "ooui-dialog-process-continue": "Շարունակել"
 }
index 7565f4f..bd65e71 100644 (file)
        },
        "ooui-outline-control-move-down": "Pindahkan butir ke bawah",
        "ooui-outline-control-move-up": "Pindahkan butir ke atas",
-       "ooui-toolbar-more": "Lainnya"
+       "ooui-outline-control-remove": "Hapus butir",
+       "ooui-toolbar-more": "Lainnya",
+       "ooui-toolgroup-expand": "Selengkapnya",
+       "ooui-toolgroup-collapse": "Secukupnya",
+       "ooui-dialog-message-accept": "Oke",
+       "ooui-dialog-message-reject": "Batal",
+       "ooui-dialog-process-error": "Ada yang tidak beres",
+       "ooui-dialog-process-dismiss": "Tutup",
+       "ooui-dialog-process-retry": "Coba lagi",
+       "ooui-dialog-process-continue": "Lanjutkan"
 }
index 3fe75e3..c5ecbac 100644 (file)
        "ooui-outline-control-move-up": "Flytt opp",
        "ooui-outline-control-remove": "Fjern element",
        "ooui-toolbar-more": "Mer",
+       "ooui-toolgroup-expand": "Mer",
+       "ooui-toolgroup-collapse": "Mindre",
        "ooui-dialog-message-accept": "OK",
        "ooui-dialog-message-reject": "Avbryt",
        "ooui-dialog-process-error": "Noe gikk galt",
        "ooui-dialog-process-dismiss": "Lukk",
-       "ooui-dialog-process-retry": "Prøv igjen"
+       "ooui-dialog-process-retry": "Prøv igjen",
+       "ooui-dialog-process-continue": "Fortsett"
 }
index c62782e..ecf9597 100644 (file)
@@ -9,9 +9,12 @@
        "ooui-outline-control-move-up": "Ol baasi",
        "ooui-outline-control-remove": "Balleessi",
        "ooui-toolbar-more": "Dabalata",
+       "ooui-toolgroup-expand": "Dabalata",
+       "ooui-toolgroup-collapse": "Xiqqaa",
        "ooui-dialog-message-accept": "Tole",
        "ooui-dialog-message-reject": "Dhiisi",
        "ooui-dialog-process-error": "Dogoggorri wayii ummameera",
        "ooui-dialog-process-dismiss": "Didi",
-       "ooui-dialog-process-retry": "Itti deebi'ii yaali"
+       "ooui-dialog-process-retry": "Itti deebi'ii yaali",
+       "ooui-dialog-process-continue": "Itti fufi"
 }
index d653356..c827554 100644 (file)
        "ooui-outline-control-move-up": "Премести ставку на горе",
        "ooui-outline-control-remove": "Уклони ставку",
        "ooui-toolbar-more": "Више",
+       "ooui-toolgroup-expand": "Више",
+       "ooui-toolgroup-collapse": "Мање",
        "ooui-dialog-message-accept": "У реду",
        "ooui-dialog-message-reject": "Откажи",
        "ooui-dialog-process-error": "Нешто је пошло наопако",
        "ooui-dialog-process-dismiss": "Одбаци",
-       "ooui-dialog-process-retry": "Покушај поново"
+       "ooui-dialog-process-retry": "Покушај поново",
+       "ooui-dialog-process-continue": "Настави"
 }
index d61d951..d499427 100644 (file)
@@ -24,5 +24,6 @@
        "ooui-dialog-message-reject": "Avbryt",
        "ooui-dialog-process-error": "Något gick fel",
        "ooui-dialog-process-dismiss": "Stäng",
-       "ooui-dialog-process-retry": "Försök igen"
+       "ooui-dialog-process-retry": "Försök igen",
+       "ooui-dialog-process-continue": "Fortsätt"
 }
index f633de2..f8916e8 100644 (file)
@@ -1,13 +1,59 @@
 /*!
- * OOjs UI v0.1.0-pre (fe4076af75)
+ * OOjs UI v0.2.2
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2014 OOjs Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2014-11-13T22:25:27Z
+ * Date: 2014-11-25T01:13:20Z
  */
+.oo-ui-progressBarWidget-slide-frames from {
+       margin-left: -40%;
+}
+.oo-ui-progressBarWidget-slide-frames to {
+       margin-left: 100%;
+}
+@-webkit-keyframes oo-ui-progressBarWidget-slide {
+       from {
+               margin-left: -40%;
+       }
+       to {
+               margin-left: 100%;
+       }
+}
+@-moz-keyframes oo-ui-progressBarWidget-slide {
+       from {
+               margin-left: -40%;
+       }
+       to {
+               margin-left: 100%;
+       }
+}
+@-ms-keyframes oo-ui-progressBarWidget-slide {
+       from {
+               margin-left: -40%;
+       }
+       to {
+               margin-left: 100%;
+       }
+}
+@-o-keyframes oo-ui-progressBarWidget-slide {
+       from {
+               margin-left: -40%;
+       }
+       to {
+               margin-left: 100%;
+       }
+}
+@keyframes oo-ui-progressBarWidget-slide {
+       from {
+               margin-left: -40%;
+       }
+       to {
+               margin-left: 100%;
+       }
+}
 /* @noflip */
 .oo-ui-rtl {
        direction: rtl;
@@ -47,7 +93,7 @@
        display: inline-block;
        position: relative;
 }
-.oo-ui-buttonElement-frameless > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
+.oo-ui-buttonElement-frameless.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
        display: inline-block;
        vertical-align: middle;
 }
        vertical-align: top;
        text-align: center;
 }
-.oo-ui-buttonElement-framed > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
+.oo-ui-buttonElement-framed.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
        display: inline-block;
        vertical-align: middle;
 }
 }
 .oo-ui-buttonElement-frameless > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
        color: #333333;
+}
+.oo-ui-buttonElement-frameless.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
        margin-left: 0.25em;
 }
 .oo-ui-buttonElement-frameless.oo-ui-flaggedElement-primary > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
 .oo-ui-bookletLayout-stackLayout .oo-ui-panelLayout-padded {
        padding: 2em;
 }
-.oo-ui-bookletLayout-outlinePanel-editable .oo-ui-outlineWidget {
+.oo-ui-bookletLayout-outlinePanel-editable .oo-ui-outlineSelectWidget {
        position: absolute;
        top: 0;
        left: 0;
 }
 .oo-ui-fieldLayout .oo-ui-fieldLayout-help-content {
        padding: 0.5em 0.75em;
+       line-height: 1.5em;
 }
 .oo-ui-fieldLayout:last-child {
        margin-bottom: 0;
 .oo-ui-popupToolGroup.oo-ui-indicatorElement.oo-ui-iconElement {
        min-width: 3.5em;
 }
+.oo-ui-popupToolGroup.oo-ui-labelElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
+       line-height: 2.6em;
+       font-size: 0.8em;
+       margin: 0 1em;
+}
+.oo-ui-popupToolGroup.oo-ui-labelElement.oo-ui-iconElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
+       margin-left: 3em;
+}
+.oo-ui-popupToolGroup.oo-ui-labelElement.oo-ui-indicatorElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
+       margin-right: 2.25em;
+}
 .oo-ui-popupToolGroup-handle .oo-ui-indicatorElement-indicator,
 .oo-ui-popupToolGroup-handle .oo-ui-iconElement-icon {
        top: 0;
 .oo-ui-popupToolGroup-handle .oo-ui-iconElement-icon {
        left: 0.25em;
 }
-.oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
-       line-height: 2.6em;
-       font-size: 0.8em;
-       margin: 0 1em;
-}
 .oo-ui-popupToolGroup-header {
        line-height: 2.6em;
        font-size: 0.8em;
        background-image:      -o-linear-gradient(top, #f1f7fb 0%, #ffffff 100%);
        background-image:         linear-gradient(top, #f1f7fb 0%, #ffffff 100%);
 }
-.oo-ui-popupToolGroup.oo-ui-iconElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
-       margin-left: 3em;
-}
-.oo-ui-popupToolGroup.oo-ui-indicatorElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
-       margin-right: 2.25em;
-}
 .oo-ui-popupToolGroup .oo-ui-toolGroup-tools {
        top: 2em;
        margin: 0 -1px;
        position: relative;
        display: block;
        cursor: pointer;
-       padding: 0.5em 2em 0.5em 3em;
+       padding: 0.25em 0.5em;
        border: none;
 }
 .oo-ui-optionWidget.oo-ui-widget-disabled {
        cursor: default;
 }
-.oo-ui-optionWidget .oo-ui-labelElement-label {
+.oo-ui-optionWidget.oo-ui-labelElement .oo-ui-labelElement-label {
        display: block;
        white-space: nowrap;
        text-overflow: ellipsis;
 .oo-ui-optionWidget.oo-ui-widget-disabled {
        color: #cccccc;
 }
+.oo-ui-decoratedOptionWidget {
+       padding: 0.5em 2em 0.5em 3em;
+}
 .oo-ui-decoratedOptionWidget .oo-ui-iconElement-icon,
 .oo-ui-decoratedOptionWidget .oo-ui-indicatorElement-indicator {
        position: absolute;
        border-bottom-right-radius: 0.3em;
        border-top-right-radius: 0.3em;
 }
+.oo-ui-radioSelectWidget {
+       padding: 0.75em 0 0.5em 0;
+}
 .oo-ui-buttonOptionWidget {
        display: inline-block;
        padding: 0;
 .oo-ui-buttonOptionWidget.oo-ui-optionWidget-highlighted {
        background-color: transparent;
 }
+.oo-ui-radioOptionWidget {
+       cursor: default;
+       padding: 0;
+       background-color: transparent;
+}
+.oo-ui-radioOptionWidget .oo-ui-radioInputWidget,
+.oo-ui-radioOptionWidget .oo-ui-labelElement-label {
+       display: inline-block;
+       vertical-align: middle;
+}
+.oo-ui-radioOptionWidget.oo-ui-optionWidget-selected,
+.oo-ui-radioOptionWidget.oo-ui-optionWidget-pressed,
+.oo-ui-radioOptionWidget.oo-ui-optionWidget-highlighted {
+       background-color: transparent;
+}
+.oo-ui-radioOptionWidget > .oo-ui-labelElement-label {
+       padding: 0 0.5em;
+}
 .oo-ui-labelWidget {
        display: inline-block;
        padding: 0.5em 0;
        margin-left: 0;
 }
 .oo-ui-progressBarWidget {
-       width: 100%;
        max-width: 50em;
        border: solid 1px #a6cee1;
        border-radius: 0.25em;
+       overflow: hidden;
 }
 .oo-ui-progressBarWidget-bar {
        height: 1em;
        border-right: solid 1px #a6cee1;
-       -webkit-transition: width 200ms;
-          -moz-transition: width 200ms;
-           -ms-transition: width 200ms;
-            -o-transition: width 200ms;
-               transition: width 200ms;
+       -webkit-transition: width 200ms, margin-left 200ms;
+          -moz-transition: width 200ms, margin-left 200ms;
+           -ms-transition: width 200ms, margin-left 200ms;
+            -o-transition: width 200ms, margin-left 200ms;
+               transition: width 200ms, margin-left 200ms;
        background: #cde7f4;
        filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#eaf4fa', endColorstr='#b0d9ee');
        background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0%, #eaf4fa), color-stop(100%, #b0d9ee));
        background-image:      -o-linear-gradient(top, #eaf4fa 0%, #b0d9ee 100%);
        background-image:         linear-gradient(top, #eaf4fa 0%, #b0d9ee 100%);
 }
+.oo-ui-progressBarWidget-indeterminate .oo-ui-progressBarWidget-bar {
+       -webkit-animation: oo-ui-progressBarWidget-slide 2s infinite linear;
+          -moz-animation: oo-ui-progressBarWidget-slide 2s infinite linear;
+           -ms-animation: oo-ui-progressBarWidget-slide 2s infinite linear;
+            -o-animation: oo-ui-progressBarWidget-slide 2s infinite linear;
+               animation: oo-ui-progressBarWidget-slide 2s infinite linear;
+       width: 40%;
+       margin-left: -10%;
+       border-left: solid 1px #a6cee1;
+}
 .oo-ui-progressBarWidget.oo-ui-widget-disabled {
        opacity: 0.6;
 }
 .oo-ui-popupButtonWidget.oo-ui-buttonElement-frameless > .oo-ui-popupWidget {
        left: 1em;
 }
-.oo-ui-popupButtonWidget.oo-ui-buttonElement-frameless > .oo-ui-popupWidget > .oo-ui-popupWidget-popup {
-       left: -1em;
-}
 .oo-ui-popupButtonWidget.oo-ui-buttonElement-framed > .oo-ui-popupWidget {
        left: 1.25em;
 }
-.oo-ui-popupButtonWidget.oo-ui-buttonElement-framed > .oo-ui-popupWidget > .oo-ui-popupWidget-popup {
-       left: -1.25em;
-}
 .oo-ui-textInputWidget {
        position: relative;
        -webkit-box-sizing: border-box;
        width: 1.5em;
        background-position: left center;
 }
-.oo-ui-menuWidget {
+.oo-ui-menuSelectWidget {
        position: absolute;
        background: #ffffff;
        margin-top: -1px;
        border-radius: 0 0 0.25em 0.25em;
        box-shadow: 0 0.15em 1em 0 rgba(0, 0, 0, 0.2);
 }
-.oo-ui-menuWidget input {
+.oo-ui-menuSelectWidget input {
        position: absolute;
        width: 0;
        height: 0;
        overflow: hidden;
        opacity: 0;
 }
-.oo-ui-menuItemWidget {
+.oo-ui-menuOptionWidget {
        position: relative;
 }
-.oo-ui-menuItemWidget .oo-ui-iconElement-icon {
+.oo-ui-menuOptionWidget .oo-ui-iconElement-icon {
        display: none;
 }
-.oo-ui-menuItemWidget.oo-ui-optionWidget-selected {
+.oo-ui-menuOptionWidget.oo-ui-optionWidget-selected {
        background-color: transparent;
 }
-.oo-ui-menuItemWidget.oo-ui-optionWidget-selected .oo-ui-iconElement-icon {
+.oo-ui-menuOptionWidget.oo-ui-optionWidget-selected .oo-ui-iconElement-icon {
        display: block;
 }
-.oo-ui-menuItemWidget.oo-ui-optionWidget-selected {
+.oo-ui-menuOptionWidget.oo-ui-optionWidget-selected {
        background-color: transparent;
 }
-.oo-ui-menuItemWidget.oo-ui-optionWidget-highlighted,
-.oo-ui-menuItemWidget.oo-ui-optionWidget-highlighted.oo-ui-optionWidget-selected {
+.oo-ui-menuOptionWidget.oo-ui-optionWidget-highlighted,
+.oo-ui-menuOptionWidget.oo-ui-optionWidget-highlighted.oo-ui-optionWidget-selected {
        background-color: #e1f3ff;
 }
-.oo-ui-menuSectionItemWidget {
+.oo-ui-menuSectionOptionWidget {
        cursor: default;
        padding: 0.33em 0.75em;
        color: #888888;
        background-position: center center;
        background-repeat: no-repeat;
 }
-.oo-ui-dropdownWidget .oo-ui-menuWidget {
+.oo-ui-dropdownWidget .oo-ui-menuSelectWidget {
        z-index: 1;
        width: 100%;
 }
 .oo-ui-dropdownWidget.oo-ui-indicatorElement .oo-ui-dropdownWidget-handle .oo-ui-labelElement-label {
        margin-right: 2em;
 }
-.oo-ui-outlineItemWidget {
+.oo-ui-outlineOptionWidget {
        position: relative;
        cursor: pointer;
        -webkit-touch-callout: none;
        font-size: 1.1em;
        padding: 0.75em;
 }
-.oo-ui-outlineItemWidget.oo-ui-indicatorElement .oo-ui-labelElement-label {
+.oo-ui-outlineOptionWidget.oo-ui-indicatorElement .oo-ui-labelElement-label {
        padding-right: 1.5em;
 }
-.oo-ui-outlineItemWidget.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator {
+.oo-ui-outlineOptionWidget.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator {
        opacity: 0.5;
 }
-.oo-ui-outlineItemWidget-level-0 {
+.oo-ui-outlineOptionWidget-level-0 {
        padding-left: 3.5em;
 }
-.oo-ui-outlineItemWidget-level-0 .oo-ui-iconElement-icon {
+.oo-ui-outlineOptionWidget-level-0 .oo-ui-iconElement-icon {
        left: 1em;
 }
-.oo-ui-outlineItemWidget-level-1 {
+.oo-ui-outlineOptionWidget-level-1 {
        padding-left: 5em;
 }
-.oo-ui-outlineItemWidget-level-1 .oo-ui-iconElement-icon {
+.oo-ui-outlineOptionWidget-level-1 .oo-ui-iconElement-icon {
        left: 2.5em;
 }
-.oo-ui-outlineItemWidget-level-2 {
+.oo-ui-outlineOptionWidget-level-2 {
        padding-left: 6.5em;
 }
-.oo-ui-outlineItemWidget-level-2 .oo-ui-iconElement-icon {
+.oo-ui-outlineOptionWidget-level-2 .oo-ui-iconElement-icon {
        left: 4em;
 }
-.oo-ui-selectWidget-depressed .oo-ui-outlineItemWidget.oo-ui-optionWidget-selected {
+.oo-ui-selectWidget-depressed .oo-ui-outlineOptionWidget.oo-ui-optionWidget-selected {
        background-color: #a7dcff;
        text-shadow: 0 1px 1px rgba(255, 255, 255, 0.5);
 }
-.oo-ui-outlineItemWidget.oo-ui-flaggedElement-important {
+.oo-ui-outlineOptionWidget.oo-ui-flaggedElement-important {
        font-weight: bold;
 }
-.oo-ui-outlineItemWidget.oo-ui-flaggedElement-placeholder {
+.oo-ui-outlineOptionWidget.oo-ui-flaggedElement-placeholder {
        font-style: italic;
 }
-.oo-ui-outlineItemWidget.oo-ui-flaggedElement-empty .oo-ui-iconElement-icon {
+.oo-ui-outlineOptionWidget.oo-ui-flaggedElement-empty .oo-ui-iconElement-icon {
        opacity: 0.5;
 }
-.oo-ui-outlineItemWidget.oo-ui-flaggedElement-empty .oo-ui-labelElement-label {
+.oo-ui-outlineOptionWidget.oo-ui-flaggedElement-empty .oo-ui-labelElement-label {
        color: #777777;
 }
 .oo-ui-outlineControlsWidget {
 .oo-ui-messageDialog-actions-vertical .oo-ui-actionWidget:last-child {
        border-bottom-width: 0;
 }
-.oo-ui-messageDialog-actions .oo-ui-actionWidget .oo-ui-labelElement-label {
+.oo-ui-messageDialog-actions .oo-ui-actionWidget {
+       height: 3.4em;
+}
+.oo-ui-messageDialog-actions .oo-ui-actionWidget.oo-ui-labelElement .oo-ui-labelElement-label {
        text-align: center;
        line-height: 3.4em;
        padding: 0 2em;
 .oo-ui-processDialog-actions-safe .oo-ui-actionWidget .oo-ui-buttonElement-button,
 .oo-ui-processDialog-actions-primary .oo-ui-actionWidget .oo-ui-buttonElement-button,
 .oo-ui-processDialog-actions-other .oo-ui-actionWidget .oo-ui-buttonElement-button {
-       padding-top: 0.75em;
-       padding-bottom: 0.75em;
        min-width: 1.9em;
        min-height: 1.9em;
 }
 .oo-ui-processDialog-actions-primary .oo-ui-actionWidget .oo-ui-labelElement-label,
 .oo-ui-processDialog-actions-other .oo-ui-actionWidget .oo-ui-labelElement-label {
        line-height: 1.9em;
-       padding: 0 1em;
 }
-.oo-ui-processDialog-actions-safe .oo-ui-actionWidget .oo-ui-iconElement-icon,
-.oo-ui-processDialog-actions-primary .oo-ui-actionWidget .oo-ui-iconElement-icon,
-.oo-ui-processDialog-actions-other .oo-ui-actionWidget .oo-ui-iconElement-icon {
-       position: absolute;
+.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-iconElement .oo-ui-iconElement-icon,
+.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-iconElement .oo-ui-iconElement-icon,
+.oo-ui-processDialog-actions-other .oo-ui-actionWidget.oo-ui-iconElement .oo-ui-iconElement-icon {
        margin-top: -0.125em;
 }
 .oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-buttonElement-framed,
 .oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-buttonElement-framed .oo-ui-buttonElement-button,
 .oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-buttonElement-framed .oo-ui-buttonElement-button,
 .oo-ui-processDialog-actions-other .oo-ui-actionWidget.oo-ui-buttonElement-framed .oo-ui-buttonElement-button {
-       padding: 0;
+       padding: 0 1em;
        vertical-align: middle;
 }
 .oo-ui-processDialog-actions-safe .oo-ui-actionWidget:hover,
 .oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-flaggedElement-destructive:active {
        background-color: rgba(212, 83, 83, 0.1);
 }
-.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-iconElement .oo-ui-iconElement-icon {
-       left: 0.5em;
-}
-.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-iconElement .oo-ui-labelElement-label {
-       padding-left: 2.25em;
-}
-.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-iconElement .oo-ui-iconElement-icon {
-       right: 0.5em;
-}
-.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-iconElement .oo-ui-labelElement-label {
-       padding-right: 2.25em;
-}
 .oo-ui-processDialog > .oo-ui-window-frame {
        min-height: 5em;
 }
        overflow: hidden;
        max-width: 100%;
        max-height: 100%;
+       visibility: visible;
 }
 .oo-ui-windowManager-modal > .oo-ui-dialog.oo-ui-window-setup > .oo-ui-window-frame > iframe {
        width: 100%;
        height: 100%;
 }
-.oo-ui-windowManager-modal > .oo-ui-dialog .oo-ui-window-frame {
+.oo-ui-windowManager-modal > .oo-ui-dialog .oo-ui-window-frame {
        visibility: hidden;
 }
-.oo-ui-windowManager-modal > .oo-ui-dialog.oo-ui-window-ready > .oo-ui-window-frame {
-       visibility: visible;
-}
 .oo-ui-windowManager-fullscreen > .oo-ui-dialog > .oo-ui-window-frame {
        width: 100%;
        height: 100%;
 .oo-ui-windowManager-modal > .oo-ui-dialog.oo-ui-window-load {
        opacity: 1;
 }
-.oo-ui-windowManager-modal > .oo-ui-dialog.oo-ui-window-ready > .oo-ui-window-frame {
+.oo-ui-windowManager-modal > .oo-ui-dialog.oo-ui-window-setup > .oo-ui-window-frame {
        -webkit-transform: scale(1);
           -moz-transform: scale(1);
            -ms-transform: scale(1);
index c61b3b9..2a62964 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.1.0-pre (fe4076af75)
+ * OOjs UI v0.2.2
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2014 OOjs Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2014-11-13T22:25:17Z
+ * Date: 2014-11-25T01:13:13Z
  */
 /* Instantiation */
 
index 23313e5..26a4c0b 100644 (file)
@@ -1,13 +1,59 @@
 /*!
- * OOjs UI v0.1.0-pre (fe4076af75)
+ * OOjs UI v0.2.2
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2014 OOjs Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2014-11-13T22:25:27Z
+ * Date: 2014-11-25T01:13:20Z
  */
+.oo-ui-progressBarWidget-slide-frames from {
+       margin-left: -40%;
+}
+.oo-ui-progressBarWidget-slide-frames to {
+       margin-left: 100%;
+}
+@-webkit-keyframes oo-ui-progressBarWidget-slide {
+       from {
+               margin-left: -40%;
+       }
+       to {
+               margin-left: 100%;
+       }
+}
+@-moz-keyframes oo-ui-progressBarWidget-slide {
+       from {
+               margin-left: -40%;
+       }
+       to {
+               margin-left: 100%;
+       }
+}
+@-ms-keyframes oo-ui-progressBarWidget-slide {
+       from {
+               margin-left: -40%;
+       }
+       to {
+               margin-left: 100%;
+       }
+}
+@-o-keyframes oo-ui-progressBarWidget-slide {
+       from {
+               margin-left: -40%;
+       }
+       to {
+               margin-left: 100%;
+       }
+}
+@keyframes oo-ui-progressBarWidget-slide {
+       from {
+               margin-left: -40%;
+       }
+       to {
+               margin-left: 100%;
+       }
+}
 /* @noflip */
 .oo-ui-rtl {
        direction: rtl;
@@ -47,7 +93,7 @@
        display: inline-block;
        position: relative;
 }
-.oo-ui-buttonElement-frameless > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
+.oo-ui-buttonElement-frameless.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
        display: inline-block;
        vertical-align: middle;
 }
        vertical-align: top;
        text-align: center;
 }
-.oo-ui-buttonElement-framed > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
+.oo-ui-buttonElement-framed.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
        display: inline-block;
        vertical-align: middle;
 }
 }
 .oo-ui-buttonElement-frameless > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
        color: #333333;
+}
+.oo-ui-buttonElement-frameless.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
        margin-left: 0.25em;
 }
 .oo-ui-buttonElement-frameless.oo-ui-flaggedElement-primary > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
 .oo-ui-bookletLayout-stackLayout .oo-ui-panelLayout-padded {
        padding: 2em;
 }
-.oo-ui-bookletLayout-outlinePanel-editable .oo-ui-outlineWidget {
+.oo-ui-bookletLayout-outlinePanel-editable .oo-ui-outlineSelectWidget {
        position: absolute;
        top: 0;
        left: 0;
 }
 .oo-ui-fieldLayout .oo-ui-fieldLayout-help-content {
        padding: 0.5em 0.75em;
+       line-height: 1.5em;
 }
 .oo-ui-fieldLayout:last-child {
        margin-bottom: 0;
 .oo-ui-popupToolGroup.oo-ui-indicatorElement.oo-ui-iconElement {
        min-width: 3.5em;
 }
+.oo-ui-popupToolGroup.oo-ui-labelElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
+       line-height: 2.6em;
+       font-size: 0.8em;
+       margin: 0 1em;
+}
+.oo-ui-popupToolGroup.oo-ui-labelElement.oo-ui-iconElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
+       margin-left: 3em;
+}
+.oo-ui-popupToolGroup.oo-ui-labelElement.oo-ui-indicatorElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
+       margin-right: 2.25em;
+}
 .oo-ui-popupToolGroup-handle .oo-ui-indicatorElement-indicator,
 .oo-ui-popupToolGroup-handle .oo-ui-iconElement-icon {
        top: 0;
 .oo-ui-popupToolGroup-handle .oo-ui-iconElement-icon {
        left: 0.25em;
 }
-.oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
-       line-height: 2.6em;
-       font-size: 0.8em;
-       margin: 0 1em;
-}
 .oo-ui-popupToolGroup-header {
        line-height: 2.6em;
        font-size: 0.8em;
        background-image:      -o-linear-gradient(top, #f1f7fb 0%, #ffffff 100%);
        background-image:         linear-gradient(top, #f1f7fb 0%, #ffffff 100%);
 }
-.oo-ui-popupToolGroup.oo-ui-iconElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
-       margin-left: 3em;
-}
-.oo-ui-popupToolGroup.oo-ui-indicatorElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
-       margin-right: 2.25em;
-}
 .oo-ui-popupToolGroup .oo-ui-toolGroup-tools {
        top: 2em;
        margin: 0 -1px;
        position: relative;
        display: block;
        cursor: pointer;
-       padding: 0.5em 2em 0.5em 3em;
+       padding: 0.25em 0.5em;
        border: none;
 }
 .oo-ui-optionWidget.oo-ui-widget-disabled {
        cursor: default;
 }
-.oo-ui-optionWidget .oo-ui-labelElement-label {
+.oo-ui-optionWidget.oo-ui-labelElement .oo-ui-labelElement-label {
        display: block;
        white-space: nowrap;
        text-overflow: ellipsis;
 .oo-ui-optionWidget.oo-ui-widget-disabled {
        color: #cccccc;
 }
+.oo-ui-decoratedOptionWidget {
+       padding: 0.5em 2em 0.5em 3em;
+}
 .oo-ui-decoratedOptionWidget .oo-ui-iconElement-icon,
 .oo-ui-decoratedOptionWidget .oo-ui-indicatorElement-indicator {
        position: absolute;
        border-bottom-right-radius: 0.3em;
        border-top-right-radius: 0.3em;
 }
+.oo-ui-radioSelectWidget {
+       padding: 0.75em 0 0.5em 0;
+}
 .oo-ui-buttonOptionWidget {
        display: inline-block;
        padding: 0;
 .oo-ui-buttonOptionWidget.oo-ui-optionWidget-highlighted {
        background-color: transparent;
 }
+.oo-ui-radioOptionWidget {
+       cursor: default;
+       padding: 0;
+       background-color: transparent;
+}
+.oo-ui-radioOptionWidget .oo-ui-radioInputWidget,
+.oo-ui-radioOptionWidget .oo-ui-labelElement-label {
+       display: inline-block;
+       vertical-align: middle;
+}
+.oo-ui-radioOptionWidget.oo-ui-optionWidget-selected,
+.oo-ui-radioOptionWidget.oo-ui-optionWidget-pressed,
+.oo-ui-radioOptionWidget.oo-ui-optionWidget-highlighted {
+       background-color: transparent;
+}
+.oo-ui-radioOptionWidget > .oo-ui-labelElement-label {
+       padding: 0 0.5em;
+}
 .oo-ui-labelWidget {
        display: inline-block;
        padding: 0.5em 0;
        margin-left: 0;
 }
 .oo-ui-progressBarWidget {
-       width: 100%;
        max-width: 50em;
        border: solid 1px #a6cee1;
        border-radius: 0.25em;
+       overflow: hidden;
 }
 .oo-ui-progressBarWidget-bar {
        height: 1em;
        border-right: solid 1px #a6cee1;
-       -webkit-transition: width 200ms;
-          -moz-transition: width 200ms;
-           -ms-transition: width 200ms;
-            -o-transition: width 200ms;
-               transition: width 200ms;
+       -webkit-transition: width 200ms, margin-left 200ms;
+          -moz-transition: width 200ms, margin-left 200ms;
+           -ms-transition: width 200ms, margin-left 200ms;
+            -o-transition: width 200ms, margin-left 200ms;
+               transition: width 200ms, margin-left 200ms;
        background: #cde7f4;
        filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#eaf4fa', endColorstr='#b0d9ee');
        background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0%, #eaf4fa), color-stop(100%, #b0d9ee));
        background-image:      -o-linear-gradient(top, #eaf4fa 0%, #b0d9ee 100%);
        background-image:         linear-gradient(top, #eaf4fa 0%, #b0d9ee 100%);
 }
+.oo-ui-progressBarWidget-indeterminate .oo-ui-progressBarWidget-bar {
+       -webkit-animation: oo-ui-progressBarWidget-slide 2s infinite linear;
+          -moz-animation: oo-ui-progressBarWidget-slide 2s infinite linear;
+           -ms-animation: oo-ui-progressBarWidget-slide 2s infinite linear;
+            -o-animation: oo-ui-progressBarWidget-slide 2s infinite linear;
+               animation: oo-ui-progressBarWidget-slide 2s infinite linear;
+       width: 40%;
+       margin-left: -10%;
+       border-left: solid 1px #a6cee1;
+}
 .oo-ui-progressBarWidget.oo-ui-widget-disabled {
        opacity: 0.6;
 }
 .oo-ui-popupButtonWidget.oo-ui-buttonElement-frameless > .oo-ui-popupWidget {
        left: 1em;
 }
-.oo-ui-popupButtonWidget.oo-ui-buttonElement-frameless > .oo-ui-popupWidget > .oo-ui-popupWidget-popup {
-       left: -1em;
-}
 .oo-ui-popupButtonWidget.oo-ui-buttonElement-framed > .oo-ui-popupWidget {
        left: 1.25em;
 }
-.oo-ui-popupButtonWidget.oo-ui-buttonElement-framed > .oo-ui-popupWidget > .oo-ui-popupWidget-popup {
-       left: -1.25em;
-}
 .oo-ui-textInputWidget {
        position: relative;
        -webkit-box-sizing: border-box;
        width: 1.5em;
        background-position: left center;
 }
-.oo-ui-menuWidget {
+.oo-ui-menuSelectWidget {
        position: absolute;
        background: #ffffff;
        margin-top: -1px;
        border-radius: 0 0 0.25em 0.25em;
        box-shadow: 0 0.15em 1em 0 rgba(0, 0, 0, 0.2);
 }
-.oo-ui-menuWidget input {
+.oo-ui-menuSelectWidget input {
        position: absolute;
        width: 0;
        height: 0;
        overflow: hidden;
        opacity: 0;
 }
-.oo-ui-menuItemWidget {
+.oo-ui-menuOptionWidget {
        position: relative;
 }
-.oo-ui-menuItemWidget .oo-ui-iconElement-icon {
+.oo-ui-menuOptionWidget .oo-ui-iconElement-icon {
        display: none;
 }
-.oo-ui-menuItemWidget.oo-ui-optionWidget-selected {
+.oo-ui-menuOptionWidget.oo-ui-optionWidget-selected {
        background-color: transparent;
 }
-.oo-ui-menuItemWidget.oo-ui-optionWidget-selected .oo-ui-iconElement-icon {
+.oo-ui-menuOptionWidget.oo-ui-optionWidget-selected .oo-ui-iconElement-icon {
        display: block;
 }
-.oo-ui-menuItemWidget.oo-ui-optionWidget-selected {
+.oo-ui-menuOptionWidget.oo-ui-optionWidget-selected {
        background-color: transparent;
 }
-.oo-ui-menuItemWidget.oo-ui-optionWidget-highlighted,
-.oo-ui-menuItemWidget.oo-ui-optionWidget-highlighted.oo-ui-optionWidget-selected {
+.oo-ui-menuOptionWidget.oo-ui-optionWidget-highlighted,
+.oo-ui-menuOptionWidget.oo-ui-optionWidget-highlighted.oo-ui-optionWidget-selected {
        background-color: #e1f3ff;
 }
-.oo-ui-menuSectionItemWidget {
+.oo-ui-menuSectionOptionWidget {
        cursor: default;
        padding: 0.33em 0.75em;
        color: #888888;
        background-position: center center;
        background-repeat: no-repeat;
 }
-.oo-ui-dropdownWidget .oo-ui-menuWidget {
+.oo-ui-dropdownWidget .oo-ui-menuSelectWidget {
        z-index: 1;
        width: 100%;
 }
 .oo-ui-dropdownWidget.oo-ui-indicatorElement .oo-ui-dropdownWidget-handle .oo-ui-labelElement-label {
        margin-right: 2em;
 }
-.oo-ui-outlineItemWidget {
+.oo-ui-outlineOptionWidget {
        position: relative;
        cursor: pointer;
        -webkit-touch-callout: none;
        font-size: 1.1em;
        padding: 0.75em;
 }
-.oo-ui-outlineItemWidget.oo-ui-indicatorElement .oo-ui-labelElement-label {
+.oo-ui-outlineOptionWidget.oo-ui-indicatorElement .oo-ui-labelElement-label {
        padding-right: 1.5em;
 }
-.oo-ui-outlineItemWidget.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator {
+.oo-ui-outlineOptionWidget.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator {
        opacity: 0.5;
 }
-.oo-ui-outlineItemWidget-level-0 {
+.oo-ui-outlineOptionWidget-level-0 {
        padding-left: 3.5em;
 }
-.oo-ui-outlineItemWidget-level-0 .oo-ui-iconElement-icon {
+.oo-ui-outlineOptionWidget-level-0 .oo-ui-iconElement-icon {
        left: 1em;
 }
-.oo-ui-outlineItemWidget-level-1 {
+.oo-ui-outlineOptionWidget-level-1 {
        padding-left: 5em;
 }
-.oo-ui-outlineItemWidget-level-1 .oo-ui-iconElement-icon {
+.oo-ui-outlineOptionWidget-level-1 .oo-ui-iconElement-icon {
        left: 2.5em;
 }
-.oo-ui-outlineItemWidget-level-2 {
+.oo-ui-outlineOptionWidget-level-2 {
        padding-left: 6.5em;
 }
-.oo-ui-outlineItemWidget-level-2 .oo-ui-iconElement-icon {
+.oo-ui-outlineOptionWidget-level-2 .oo-ui-iconElement-icon {
        left: 4em;
 }
-.oo-ui-selectWidget-depressed .oo-ui-outlineItemWidget.oo-ui-optionWidget-selected {
+.oo-ui-selectWidget-depressed .oo-ui-outlineOptionWidget.oo-ui-optionWidget-selected {
        background-color: #a7dcff;
        text-shadow: 0 1px 1px rgba(255, 255, 255, 0.5);
 }
-.oo-ui-outlineItemWidget.oo-ui-flaggedElement-important {
+.oo-ui-outlineOptionWidget.oo-ui-flaggedElement-important {
        font-weight: bold;
 }
-.oo-ui-outlineItemWidget.oo-ui-flaggedElement-placeholder {
+.oo-ui-outlineOptionWidget.oo-ui-flaggedElement-placeholder {
        font-style: italic;
 }
-.oo-ui-outlineItemWidget.oo-ui-flaggedElement-empty .oo-ui-iconElement-icon {
+.oo-ui-outlineOptionWidget.oo-ui-flaggedElement-empty .oo-ui-iconElement-icon {
        opacity: 0.5;
 }
-.oo-ui-outlineItemWidget.oo-ui-flaggedElement-empty .oo-ui-labelElement-label {
+.oo-ui-outlineOptionWidget.oo-ui-flaggedElement-empty .oo-ui-labelElement-label {
        color: #777777;
 }
 .oo-ui-outlineControlsWidget {
 .oo-ui-messageDialog-actions-vertical .oo-ui-actionWidget:last-child {
        border-bottom-width: 0;
 }
-.oo-ui-messageDialog-actions .oo-ui-actionWidget .oo-ui-labelElement-label {
+.oo-ui-messageDialog-actions .oo-ui-actionWidget {
+       height: 3.4em;
+}
+.oo-ui-messageDialog-actions .oo-ui-actionWidget.oo-ui-labelElement .oo-ui-labelElement-label {
        text-align: center;
        line-height: 3.4em;
        padding: 0 2em;
 .oo-ui-processDialog-actions-safe .oo-ui-actionWidget .oo-ui-buttonElement-button,
 .oo-ui-processDialog-actions-primary .oo-ui-actionWidget .oo-ui-buttonElement-button,
 .oo-ui-processDialog-actions-other .oo-ui-actionWidget .oo-ui-buttonElement-button {
-       padding-top: 0.75em;
-       padding-bottom: 0.75em;
        min-width: 1.9em;
        min-height: 1.9em;
 }
 .oo-ui-processDialog-actions-primary .oo-ui-actionWidget .oo-ui-labelElement-label,
 .oo-ui-processDialog-actions-other .oo-ui-actionWidget .oo-ui-labelElement-label {
        line-height: 1.9em;
-       padding: 0 1em;
 }
-.oo-ui-processDialog-actions-safe .oo-ui-actionWidget .oo-ui-iconElement-icon,
-.oo-ui-processDialog-actions-primary .oo-ui-actionWidget .oo-ui-iconElement-icon,
-.oo-ui-processDialog-actions-other .oo-ui-actionWidget .oo-ui-iconElement-icon {
-       position: absolute;
+.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-iconElement .oo-ui-iconElement-icon,
+.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-iconElement .oo-ui-iconElement-icon,
+.oo-ui-processDialog-actions-other .oo-ui-actionWidget.oo-ui-iconElement .oo-ui-iconElement-icon {
        margin-top: -0.125em;
 }
 .oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-buttonElement-framed,
 .oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-buttonElement-framed .oo-ui-buttonElement-button,
 .oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-buttonElement-framed .oo-ui-buttonElement-button,
 .oo-ui-processDialog-actions-other .oo-ui-actionWidget.oo-ui-buttonElement-framed .oo-ui-buttonElement-button {
-       padding: 0;
+       padding: 0 1em;
        vertical-align: middle;
 }
 .oo-ui-processDialog-actions-safe .oo-ui-actionWidget:hover,
 .oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-flaggedElement-destructive:active {
        background-color: rgba(212, 83, 83, 0.1);
 }
-.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-iconElement .oo-ui-iconElement-icon {
-       left: 0.5em;
-}
-.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-iconElement .oo-ui-labelElement-label {
-       padding-left: 2.25em;
-}
-.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-iconElement .oo-ui-iconElement-icon {
-       right: 0.5em;
-}
-.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-iconElement .oo-ui-labelElement-label {
-       padding-right: 2.25em;
-}
 .oo-ui-processDialog > .oo-ui-window-frame {
        min-height: 5em;
 }
        overflow: hidden;
        max-width: 100%;
        max-height: 100%;
+       visibility: visible;
 }
 .oo-ui-windowManager-modal > .oo-ui-dialog.oo-ui-window-setup > .oo-ui-window-frame > iframe {
        width: 100%;
        height: 100%;
 }
-.oo-ui-windowManager-modal > .oo-ui-dialog .oo-ui-window-frame {
+.oo-ui-windowManager-modal > .oo-ui-dialog .oo-ui-window-frame {
        visibility: hidden;
 }
-.oo-ui-windowManager-modal > .oo-ui-dialog.oo-ui-window-ready > .oo-ui-window-frame {
-       visibility: visible;
-}
 .oo-ui-windowManager-fullscreen > .oo-ui-dialog > .oo-ui-window-frame {
        width: 100%;
        height: 100%;
 .oo-ui-windowManager-modal > .oo-ui-dialog.oo-ui-window-load {
        opacity: 1;
 }
-.oo-ui-windowManager-modal > .oo-ui-dialog.oo-ui-window-ready > .oo-ui-window-frame {
+.oo-ui-windowManager-modal > .oo-ui-dialog.oo-ui-window-setup > .oo-ui-window-frame {
        -webkit-transform: scale(1);
           -moz-transform: scale(1);
            -ms-transform: scale(1);
index f13404b..a70e18a 100644 (file)
@@ -1,13 +1,59 @@
 /*!
- * OOjs UI v0.1.0-pre (fe4076af75)
+ * OOjs UI v0.2.2
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2014 OOjs Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2014-11-13T22:25:27Z
+ * Date: 2014-11-25T01:13:20Z
  */
+.oo-ui-progressBarWidget-slide-frames from {
+       margin-left: -40%;
+}
+.oo-ui-progressBarWidget-slide-frames to {
+       margin-left: 100%;
+}
+@-webkit-keyframes oo-ui-progressBarWidget-slide {
+       from {
+               margin-left: -40%;
+       }
+       to {
+               margin-left: 100%;
+       }
+}
+@-moz-keyframes oo-ui-progressBarWidget-slide {
+       from {
+               margin-left: -40%;
+       }
+       to {
+               margin-left: 100%;
+       }
+}
+@-ms-keyframes oo-ui-progressBarWidget-slide {
+       from {
+               margin-left: -40%;
+       }
+       to {
+               margin-left: 100%;
+       }
+}
+@-o-keyframes oo-ui-progressBarWidget-slide {
+       from {
+               margin-left: -40%;
+       }
+       to {
+               margin-left: 100%;
+       }
+}
+@keyframes oo-ui-progressBarWidget-slide {
+       from {
+               margin-left: -40%;
+       }
+       to {
+               margin-left: 100%;
+       }
+}
 /* @noflip */
 .oo-ui-rtl {
        direction: rtl;
@@ -47,7 +93,7 @@
        display: inline-block;
        position: relative;
 }
-.oo-ui-buttonElement-frameless > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
+.oo-ui-buttonElement-frameless.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
        display: inline-block;
        vertical-align: middle;
 }
        vertical-align: top;
        text-align: center;
 }
-.oo-ui-buttonElement-framed > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
+.oo-ui-buttonElement-framed.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
        display: inline-block;
        vertical-align: middle;
 }
 .oo-ui-buttonElement-frameless > .oo-ui-buttonElement-button:focus {
        outline: none;
 }
-.oo-ui-buttonElement-frameless > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
+.oo-ui-buttonElement-frameless.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
        margin-left: 0.25em;
 }
 .oo-ui-buttonElement-frameless.oo-ui-widget-enabled > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
 .oo-ui-bookletLayout-stackLayout .oo-ui-panelLayout-padded {
        padding: 2em;
 }
-.oo-ui-bookletLayout-outlinePanel-editable .oo-ui-outlineWidget {
+.oo-ui-bookletLayout-outlinePanel-editable .oo-ui-outlineSelectWidget {
        position: absolute;
        top: 0;
        left: 0;
 }
 .oo-ui-fieldLayout .oo-ui-fieldLayout-help-content {
        padding: 0.5em 0.75em;
+       line-height: 1.5em;
 }
 .oo-ui-fieldLayout:last-child {
        margin-bottom: 0;
 .oo-ui-popupToolGroup.oo-ui-indicatorElement.oo-ui-iconElement {
        min-width: 3.5em;
 }
+.oo-ui-popupToolGroup.oo-ui-labelElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
+       line-height: 2.6em;
+       font-size: 0.8em;
+       margin: 0 1em;
+}
+.oo-ui-popupToolGroup.oo-ui-labelElement.oo-ui-iconElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
+       margin-left: 3em;
+}
+.oo-ui-popupToolGroup.oo-ui-labelElement.oo-ui-indicatorElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
+       margin-right: 2.25em;
+}
 .oo-ui-popupToolGroup-handle .oo-ui-indicatorElement-indicator,
 .oo-ui-popupToolGroup-handle .oo-ui-iconElement-icon {
        top: 0;
 .oo-ui-popupToolGroup-handle .oo-ui-iconElement-icon {
        left: 0.25em;
 }
-.oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
-       line-height: 2.6em;
-       font-size: 0.8em;
-       margin: 0 1em;
-}
 .oo-ui-popupToolGroup-header {
        line-height: 2.6em;
        font-size: 0.8em;
        margin: 0 0.6em;
        font-weight: bold;
 }
-.oo-ui-popupToolGroup.oo-ui-iconElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
-       margin-left: 3em;
-}
-.oo-ui-popupToolGroup.oo-ui-indicatorElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
-       margin-right: 2.25em;
-}
 .oo-ui-popupToolGroup .oo-ui-toolGroup-tools {
        top: 2em;
        background-color: white;
        position: relative;
        display: block;
        cursor: pointer;
-       padding: 0.5em 2em 0.5em 3em;
+       padding: 0.25em 0.5em;
        border: none;
 }
 .oo-ui-optionWidget.oo-ui-widget-disabled {
        cursor: default;
 }
-.oo-ui-optionWidget .oo-ui-labelElement-label {
+.oo-ui-optionWidget.oo-ui-labelElement .oo-ui-labelElement-label {
        display: block;
        white-space: nowrap;
        text-overflow: ellipsis;
 .oo-ui-optionWidget.oo-ui-widget-disabled {
        color: #cccccc;
 }
+.oo-ui-decoratedOptionWidget {
+       padding: 0.5em 2em 0.5em 3em;
+}
 .oo-ui-decoratedOptionWidget .oo-ui-iconElement-icon,
 .oo-ui-decoratedOptionWidget .oo-ui-indicatorElement-indicator {
        position: absolute;
        border-bottom-right-radius: 0.3em;
        border-top-right-radius: 0.3em;
 }
+.oo-ui-radioSelectWidget {
+       padding: 0.75em 0 0.5em 0;
+}
 .oo-ui-buttonOptionWidget {
        display: inline-block;
        padding: 0;
 .oo-ui-buttonOptionWidget.oo-ui-optionWidget-highlighted {
        background-color: transparent;
 }
+.oo-ui-radioOptionWidget {
+       cursor: default;
+       padding: 0;
+       background-color: transparent;
+}
+.oo-ui-radioOptionWidget .oo-ui-radioInputWidget,
+.oo-ui-radioOptionWidget .oo-ui-labelElement-label {
+       display: inline-block;
+       vertical-align: middle;
+}
+.oo-ui-radioOptionWidget.oo-ui-optionWidget-selected,
+.oo-ui-radioOptionWidget.oo-ui-optionWidget-pressed,
+.oo-ui-radioOptionWidget.oo-ui-optionWidget-highlighted {
+       background-color: transparent;
+}
+.oo-ui-radioOptionWidget > .oo-ui-labelElement-label {
+       padding: 0 0.5em;
+}
 .oo-ui-labelWidget {
        display: inline-block;
 }
        background-color: #ffffff;
 }
 .oo-ui-progressBarWidget {
-       width: 100%;
        max-width: 50em;
        border: solid 1px #0274ff;
        border-radius: 0.1em;
+       overflow: hidden;
 }
 .oo-ui-progressBarWidget-bar {
        height: 1em;
-       border-right: solid 1px #0274ff;
        background: #0274ff;
-       -webkit-transition: width 200ms;
-          -moz-transition: width 200ms;
-           -ms-transition: width 200ms;
-            -o-transition: width 200ms;
-               transition: width 200ms;
+       -webkit-transition: width 200ms, margin-left 200ms;
+          -moz-transition: width 200ms, margin-left 200ms;
+           -ms-transition: width 200ms, margin-left 200ms;
+            -o-transition: width 200ms, margin-left 200ms;
+               transition: width 200ms, margin-left 200ms;
+}
+.oo-ui-progressBarWidget-indeterminate .oo-ui-progressBarWidget-bar {
+       -webkit-animation: oo-ui-progressBarWidget-slide 2s infinite linear;
+          -moz-animation: oo-ui-progressBarWidget-slide 2s infinite linear;
+           -ms-animation: oo-ui-progressBarWidget-slide 2s infinite linear;
+            -o-animation: oo-ui-progressBarWidget-slide 2s infinite linear;
+               animation: oo-ui-progressBarWidget-slide 2s infinite linear;
+       width: 40%;
+       margin-left: -10%;
+       border-left-width: 1px;
 }
 .oo-ui-progressBarWidget.oo-ui-widget-disabled {
        opacity: 0.2;
 .oo-ui-popupButtonWidget.oo-ui-buttonElement-frameless > .oo-ui-popupWidget {
        left: 1em;
 }
-.oo-ui-popupButtonWidget.oo-ui-buttonElement-frameless > .oo-ui-popupWidget > .oo-ui-popupWidget-popup {
-       left: -1em;
-}
 .oo-ui-popupButtonWidget.oo-ui-buttonElement-framed > .oo-ui-popupWidget {
        left: 1.75em;
 }
-.oo-ui-popupButtonWidget.oo-ui-buttonElement-framed > .oo-ui-popupWidget > .oo-ui-popupWidget-popup {
-       left: -1.75em;
-}
 .oo-ui-checkboxInputWidget {
        position: relative;
        line-height: 1.6em;
        height: 100%;
        background-position: left center;
 }
-.oo-ui-menuWidget {
+.oo-ui-menuSelectWidget {
        position: absolute;
        background: #ffffff;
        margin-top: -1px;
        padding-bottom: 0.25em;
        box-shadow: inset 0 -0.2em 0 0 rgba(0, 0, 0, 0.2), 0 0.1em 0 0 rgba(0, 0, 0, 0.2);
 }
-.oo-ui-menuWidget input {
+.oo-ui-menuSelectWidget input {
        position: absolute;
        width: 0;
        height: 0;
        overflow: hidden;
        opacity: 0;
 }
-.oo-ui-menuItemWidget {
+.oo-ui-menuOptionWidget {
        position: relative;
 }
-.oo-ui-menuItemWidget .oo-ui-iconElement-icon {
+.oo-ui-menuOptionWidget .oo-ui-iconElement-icon {
        display: none;
 }
-.oo-ui-menuItemWidget.oo-ui-optionWidget-selected {
+.oo-ui-menuOptionWidget.oo-ui-optionWidget-selected {
        background-color: transparent;
 }
-.oo-ui-menuItemWidget.oo-ui-optionWidget-selected .oo-ui-iconElement-icon {
+.oo-ui-menuOptionWidget.oo-ui-optionWidget-selected .oo-ui-iconElement-icon {
        display: block;
 }
-.oo-ui-menuItemWidget.oo-ui-optionWidget-selected {
+.oo-ui-menuOptionWidget.oo-ui-optionWidget-selected {
        background-color: transparent;
 }
-.oo-ui-menuItemWidget.oo-ui-optionWidget-highlighted {
+.oo-ui-menuOptionWidget.oo-ui-optionWidget-highlighted {
        background-color: #eeeeee;
 }
-.oo-ui-menuSectionItemWidget {
+.oo-ui-menuSectionOptionWidget {
        cursor: default;
        padding: 0.33em 0.75em;
        color: #888888;
        background-position: center center;
        background-repeat: no-repeat;
 }
-.oo-ui-dropdownWidget .oo-ui-menuWidget {
+.oo-ui-dropdownWidget .oo-ui-menuSelectWidget {
        z-index: 1;
        width: 100%;
 }
 .oo-ui-dropdownWidget .oo-ui-selectWidget {
        border-top-color: #ffffff;
 }
-.oo-ui-outlineItemWidget {
+.oo-ui-outlineOptionWidget {
        position: relative;
        cursor: pointer;
        -webkit-touch-callout: none;
 .oo-ui-messageDialog-actions-vertical .oo-ui-actionWidget:last-child {
        border-bottom-width: 0;
 }
-.oo-ui-messageDialog-actions .oo-ui-actionWidget .oo-ui-labelElement-label {
+.oo-ui-messageDialog-actions .oo-ui-actionWidget {
+       height: 3.4em;
+}
+.oo-ui-messageDialog-actions .oo-ui-actionWidget.oo-ui-labelElement .oo-ui-labelElement-label {
        text-align: center;
        line-height: 3.4em;
        padding: 0 2em;
 .oo-ui-processDialog-actions-safe .oo-ui-actionWidget .oo-ui-buttonElement-button,
 .oo-ui-processDialog-actions-primary .oo-ui-actionWidget .oo-ui-buttonElement-button,
 .oo-ui-processDialog-actions-other .oo-ui-actionWidget .oo-ui-buttonElement-button {
-       padding-top: 0.75em;
-       padding-bottom: 0.75em;
        min-width: 1.9em;
        min-height: 1.9em;
 }
 .oo-ui-processDialog-actions-primary .oo-ui-actionWidget .oo-ui-labelElement-label,
 .oo-ui-processDialog-actions-other .oo-ui-actionWidget .oo-ui-labelElement-label {
        line-height: 1.9em;
-       padding: 0 1em;
 }
-.oo-ui-processDialog-actions-safe .oo-ui-actionWidget .oo-ui-iconElement-icon,
-.oo-ui-processDialog-actions-primary .oo-ui-actionWidget .oo-ui-iconElement-icon,
-.oo-ui-processDialog-actions-other .oo-ui-actionWidget .oo-ui-iconElement-icon {
-       position: absolute;
+.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-iconElement .oo-ui-iconElement-icon,
+.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-iconElement .oo-ui-iconElement-icon,
+.oo-ui-processDialog-actions-other .oo-ui-actionWidget.oo-ui-iconElement .oo-ui-iconElement-icon {
        margin-top: -0.125em;
 }
 .oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-buttonElement-framed,
 .oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-buttonElement-framed .oo-ui-buttonElement-button,
 .oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-buttonElement-framed .oo-ui-buttonElement-button,
 .oo-ui-processDialog-actions-other .oo-ui-actionWidget.oo-ui-buttonElement-framed .oo-ui-buttonElement-button {
-       padding: 0;
+       padding: 0 1em;
        vertical-align: middle;
 }
 .oo-ui-processDialog-actions-safe .oo-ui-actionWidget:hover,
 .oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-flaggedElement-destructive:active {
        background-color: rgba(212, 83, 83, 0.1);
 }
-.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-iconElement .oo-ui-iconElement-icon {
-       left: 0.5em;
-}
-.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-iconElement .oo-ui-labelElement-label {
-       padding-left: 2.25em;
-}
-.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-iconElement .oo-ui-iconElement-icon {
-       right: 0.5em;
-}
-.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-iconElement .oo-ui-labelElement-label {
-       padding-right: 2.25em;
-}
 .oo-ui-processDialog > .oo-ui-window-frame {
        min-height: 5em;
 }
        overflow: hidden;
        max-width: 100%;
        max-height: 100%;
+       visibility: visible;
 }
 .oo-ui-windowManager-modal > .oo-ui-dialog.oo-ui-window-setup > .oo-ui-window-frame > iframe {
        width: 100%;
        height: 100%;
 }
-.oo-ui-windowManager-modal > .oo-ui-dialog .oo-ui-window-frame {
+.oo-ui-windowManager-modal > .oo-ui-dialog .oo-ui-window-frame {
        visibility: hidden;
 }
-.oo-ui-windowManager-modal > .oo-ui-dialog.oo-ui-window-ready > .oo-ui-window-frame {
-       visibility: visible;
-}
 .oo-ui-windowManager-fullscreen > .oo-ui-dialog > .oo-ui-window-frame {
        width: 100%;
        height: 100%;
 .oo-ui-windowManager-modal > .oo-ui-dialog.oo-ui-window-load {
        opacity: 1;
 }
-.oo-ui-windowManager-modal > .oo-ui-dialog.oo-ui-window-ready > .oo-ui-window-frame {
+.oo-ui-windowManager-modal > .oo-ui-dialog.oo-ui-window-setup > .oo-ui-window-frame {
        -webkit-transform: scale(1);
           -moz-transform: scale(1);
            -ms-transform: scale(1);
index 856be2a..9a57c66 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.1.0-pre (fe4076af75)
+ * OOjs UI v0.2.2
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2014 OOjs Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2014-11-13T22:25:17Z
+ * Date: 2014-11-25T01:13:13Z
  */
 /**
  * @class
index b901a4c..f38a637 100644 (file)
@@ -1,13 +1,59 @@
 /*!
- * OOjs UI v0.1.0-pre (fe4076af75)
+ * OOjs UI v0.2.2
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2014 OOjs Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2014-11-13T22:25:27Z
+ * Date: 2014-11-25T01:13:20Z
  */
+.oo-ui-progressBarWidget-slide-frames from {
+       margin-left: -40%;
+}
+.oo-ui-progressBarWidget-slide-frames to {
+       margin-left: 100%;
+}
+@-webkit-keyframes oo-ui-progressBarWidget-slide {
+       from {
+               margin-left: -40%;
+       }
+       to {
+               margin-left: 100%;
+       }
+}
+@-moz-keyframes oo-ui-progressBarWidget-slide {
+       from {
+               margin-left: -40%;
+       }
+       to {
+               margin-left: 100%;
+       }
+}
+@-ms-keyframes oo-ui-progressBarWidget-slide {
+       from {
+               margin-left: -40%;
+       }
+       to {
+               margin-left: 100%;
+       }
+}
+@-o-keyframes oo-ui-progressBarWidget-slide {
+       from {
+               margin-left: -40%;
+       }
+       to {
+               margin-left: 100%;
+       }
+}
+@keyframes oo-ui-progressBarWidget-slide {
+       from {
+               margin-left: -40%;
+       }
+       to {
+               margin-left: 100%;
+       }
+}
 /* @noflip */
 .oo-ui-rtl {
        direction: rtl;
@@ -47,7 +93,7 @@
        display: inline-block;
        position: relative;
 }
-.oo-ui-buttonElement-frameless > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
+.oo-ui-buttonElement-frameless.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
        display: inline-block;
        vertical-align: middle;
 }
        vertical-align: top;
        text-align: center;
 }
-.oo-ui-buttonElement-framed > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
+.oo-ui-buttonElement-framed.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
        display: inline-block;
        vertical-align: middle;
 }
 .oo-ui-buttonElement-frameless > .oo-ui-buttonElement-button:focus {
        outline: none;
 }
-.oo-ui-buttonElement-frameless > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
+.oo-ui-buttonElement-frameless.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
        margin-left: 0.25em;
 }
 .oo-ui-buttonElement-frameless.oo-ui-widget-enabled > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
 .oo-ui-bookletLayout-stackLayout .oo-ui-panelLayout-padded {
        padding: 2em;
 }
-.oo-ui-bookletLayout-outlinePanel-editable .oo-ui-outlineWidget {
+.oo-ui-bookletLayout-outlinePanel-editable .oo-ui-outlineSelectWidget {
        position: absolute;
        top: 0;
        left: 0;
 }
 .oo-ui-fieldLayout .oo-ui-fieldLayout-help-content {
        padding: 0.5em 0.75em;
+       line-height: 1.5em;
 }
 .oo-ui-fieldLayout:last-child {
        margin-bottom: 0;
 .oo-ui-popupToolGroup.oo-ui-indicatorElement.oo-ui-iconElement {
        min-width: 3.5em;
 }
+.oo-ui-popupToolGroup.oo-ui-labelElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
+       line-height: 2.6em;
+       font-size: 0.8em;
+       margin: 0 1em;
+}
+.oo-ui-popupToolGroup.oo-ui-labelElement.oo-ui-iconElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
+       margin-left: 3em;
+}
+.oo-ui-popupToolGroup.oo-ui-labelElement.oo-ui-indicatorElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
+       margin-right: 2.25em;
+}
 .oo-ui-popupToolGroup-handle .oo-ui-indicatorElement-indicator,
 .oo-ui-popupToolGroup-handle .oo-ui-iconElement-icon {
        top: 0;
 .oo-ui-popupToolGroup-handle .oo-ui-iconElement-icon {
        left: 0.25em;
 }
-.oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
-       line-height: 2.6em;
-       font-size: 0.8em;
-       margin: 0 1em;
-}
 .oo-ui-popupToolGroup-header {
        line-height: 2.6em;
        font-size: 0.8em;
        margin: 0 0.6em;
        font-weight: bold;
 }
-.oo-ui-popupToolGroup.oo-ui-iconElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
-       margin-left: 3em;
-}
-.oo-ui-popupToolGroup.oo-ui-indicatorElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
-       margin-right: 2.25em;
-}
 .oo-ui-popupToolGroup .oo-ui-toolGroup-tools {
        top: 2em;
        background-color: white;
        position: relative;
        display: block;
        cursor: pointer;
-       padding: 0.5em 2em 0.5em 3em;
+       padding: 0.25em 0.5em;
        border: none;
 }
 .oo-ui-optionWidget.oo-ui-widget-disabled {
        cursor: default;
 }
-.oo-ui-optionWidget .oo-ui-labelElement-label {
+.oo-ui-optionWidget.oo-ui-labelElement .oo-ui-labelElement-label {
        display: block;
        white-space: nowrap;
        text-overflow: ellipsis;
 .oo-ui-optionWidget.oo-ui-widget-disabled {
        color: #cccccc;
 }
+.oo-ui-decoratedOptionWidget {
+       padding: 0.5em 2em 0.5em 3em;
+}
 .oo-ui-decoratedOptionWidget .oo-ui-iconElement-icon,
 .oo-ui-decoratedOptionWidget .oo-ui-indicatorElement-indicator {
        position: absolute;
        border-bottom-right-radius: 0.3em;
        border-top-right-radius: 0.3em;
 }
+.oo-ui-radioSelectWidget {
+       padding: 0.75em 0 0.5em 0;
+}
 .oo-ui-buttonOptionWidget {
        display: inline-block;
        padding: 0;
 .oo-ui-buttonOptionWidget.oo-ui-optionWidget-highlighted {
        background-color: transparent;
 }
+.oo-ui-radioOptionWidget {
+       cursor: default;
+       padding: 0;
+       background-color: transparent;
+}
+.oo-ui-radioOptionWidget .oo-ui-radioInputWidget,
+.oo-ui-radioOptionWidget .oo-ui-labelElement-label {
+       display: inline-block;
+       vertical-align: middle;
+}
+.oo-ui-radioOptionWidget.oo-ui-optionWidget-selected,
+.oo-ui-radioOptionWidget.oo-ui-optionWidget-pressed,
+.oo-ui-radioOptionWidget.oo-ui-optionWidget-highlighted {
+       background-color: transparent;
+}
+.oo-ui-radioOptionWidget > .oo-ui-labelElement-label {
+       padding: 0 0.5em;
+}
 .oo-ui-labelWidget {
        display: inline-block;
 }
        background-color: #ffffff;
 }
 .oo-ui-progressBarWidget {
-       width: 100%;
        max-width: 50em;
        border: solid 1px #0274ff;
        border-radius: 0.1em;
+       overflow: hidden;
 }
 .oo-ui-progressBarWidget-bar {
        height: 1em;
-       border-right: solid 1px #0274ff;
        background: #0274ff;
-       -webkit-transition: width 200ms;
-          -moz-transition: width 200ms;
-           -ms-transition: width 200ms;
-            -o-transition: width 200ms;
-               transition: width 200ms;
+       -webkit-transition: width 200ms, margin-left 200ms;
+          -moz-transition: width 200ms, margin-left 200ms;
+           -ms-transition: width 200ms, margin-left 200ms;
+            -o-transition: width 200ms, margin-left 200ms;
+               transition: width 200ms, margin-left 200ms;
+}
+.oo-ui-progressBarWidget-indeterminate .oo-ui-progressBarWidget-bar {
+       -webkit-animation: oo-ui-progressBarWidget-slide 2s infinite linear;
+          -moz-animation: oo-ui-progressBarWidget-slide 2s infinite linear;
+           -ms-animation: oo-ui-progressBarWidget-slide 2s infinite linear;
+            -o-animation: oo-ui-progressBarWidget-slide 2s infinite linear;
+               animation: oo-ui-progressBarWidget-slide 2s infinite linear;
+       width: 40%;
+       margin-left: -10%;
+       border-left-width: 1px;
 }
 .oo-ui-progressBarWidget.oo-ui-widget-disabled {
        opacity: 0.2;
 .oo-ui-popupButtonWidget.oo-ui-buttonElement-frameless > .oo-ui-popupWidget {
        left: 1em;
 }
-.oo-ui-popupButtonWidget.oo-ui-buttonElement-frameless > .oo-ui-popupWidget > .oo-ui-popupWidget-popup {
-       left: -1em;
-}
 .oo-ui-popupButtonWidget.oo-ui-buttonElement-framed > .oo-ui-popupWidget {
        left: 1.75em;
 }
-.oo-ui-popupButtonWidget.oo-ui-buttonElement-framed > .oo-ui-popupWidget > .oo-ui-popupWidget-popup {
-       left: -1.75em;
-}
 .oo-ui-checkboxInputWidget {
        position: relative;
        line-height: 1.6em;
        height: 100%;
        background-position: left center;
 }
-.oo-ui-menuWidget {
+.oo-ui-menuSelectWidget {
        position: absolute;
        background: #ffffff;
        margin-top: -1px;
        padding-bottom: 0.25em;
        box-shadow: inset 0 -0.2em 0 0 rgba(0, 0, 0, 0.2), 0 0.1em 0 0 rgba(0, 0, 0, 0.2);
 }
-.oo-ui-menuWidget input {
+.oo-ui-menuSelectWidget input {
        position: absolute;
        width: 0;
        height: 0;
        overflow: hidden;
        opacity: 0;
 }
-.oo-ui-menuItemWidget {
+.oo-ui-menuOptionWidget {
        position: relative;
 }
-.oo-ui-menuItemWidget .oo-ui-iconElement-icon {
+.oo-ui-menuOptionWidget .oo-ui-iconElement-icon {
        display: none;
 }
-.oo-ui-menuItemWidget.oo-ui-optionWidget-selected {
+.oo-ui-menuOptionWidget.oo-ui-optionWidget-selected {
        background-color: transparent;
 }
-.oo-ui-menuItemWidget.oo-ui-optionWidget-selected .oo-ui-iconElement-icon {
+.oo-ui-menuOptionWidget.oo-ui-optionWidget-selected .oo-ui-iconElement-icon {
        display: block;
 }
-.oo-ui-menuItemWidget.oo-ui-optionWidget-selected {
+.oo-ui-menuOptionWidget.oo-ui-optionWidget-selected {
        background-color: transparent;
 }
-.oo-ui-menuItemWidget.oo-ui-optionWidget-highlighted {
+.oo-ui-menuOptionWidget.oo-ui-optionWidget-highlighted {
        background-color: #eeeeee;
 }
-.oo-ui-menuSectionItemWidget {
+.oo-ui-menuSectionOptionWidget {
        cursor: default;
        padding: 0.33em 0.75em;
        color: #888888;
        background-position: center center;
        background-repeat: no-repeat;
 }
-.oo-ui-dropdownWidget .oo-ui-menuWidget {
+.oo-ui-dropdownWidget .oo-ui-menuSelectWidget {
        z-index: 1;
        width: 100%;
 }
 .oo-ui-dropdownWidget .oo-ui-selectWidget {
        border-top-color: #ffffff;
 }
-.oo-ui-outlineItemWidget {
+.oo-ui-outlineOptionWidget {
        position: relative;
        cursor: pointer;
        -webkit-touch-callout: none;
 .oo-ui-messageDialog-actions-vertical .oo-ui-actionWidget:last-child {
        border-bottom-width: 0;
 }
-.oo-ui-messageDialog-actions .oo-ui-actionWidget .oo-ui-labelElement-label {
+.oo-ui-messageDialog-actions .oo-ui-actionWidget {
+       height: 3.4em;
+}
+.oo-ui-messageDialog-actions .oo-ui-actionWidget.oo-ui-labelElement .oo-ui-labelElement-label {
        text-align: center;
        line-height: 3.4em;
        padding: 0 2em;
 .oo-ui-processDialog-actions-safe .oo-ui-actionWidget .oo-ui-buttonElement-button,
 .oo-ui-processDialog-actions-primary .oo-ui-actionWidget .oo-ui-buttonElement-button,
 .oo-ui-processDialog-actions-other .oo-ui-actionWidget .oo-ui-buttonElement-button {
-       padding-top: 0.75em;
-       padding-bottom: 0.75em;
        min-width: 1.9em;
        min-height: 1.9em;
 }
 .oo-ui-processDialog-actions-primary .oo-ui-actionWidget .oo-ui-labelElement-label,
 .oo-ui-processDialog-actions-other .oo-ui-actionWidget .oo-ui-labelElement-label {
        line-height: 1.9em;
-       padding: 0 1em;
 }
-.oo-ui-processDialog-actions-safe .oo-ui-actionWidget .oo-ui-iconElement-icon,
-.oo-ui-processDialog-actions-primary .oo-ui-actionWidget .oo-ui-iconElement-icon,
-.oo-ui-processDialog-actions-other .oo-ui-actionWidget .oo-ui-iconElement-icon {
-       position: absolute;
+.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-iconElement .oo-ui-iconElement-icon,
+.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-iconElement .oo-ui-iconElement-icon,
+.oo-ui-processDialog-actions-other .oo-ui-actionWidget.oo-ui-iconElement .oo-ui-iconElement-icon {
        margin-top: -0.125em;
 }
 .oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-buttonElement-framed,
 .oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-buttonElement-framed .oo-ui-buttonElement-button,
 .oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-buttonElement-framed .oo-ui-buttonElement-button,
 .oo-ui-processDialog-actions-other .oo-ui-actionWidget.oo-ui-buttonElement-framed .oo-ui-buttonElement-button {
-       padding: 0;
+       padding: 0 1em;
        vertical-align: middle;
 }
 .oo-ui-processDialog-actions-safe .oo-ui-actionWidget:hover,
 .oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-flaggedElement-destructive:active {
        background-color: rgba(212, 83, 83, 0.1);
 }
-.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-iconElement .oo-ui-iconElement-icon {
-       left: 0.5em;
-}
-.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-iconElement .oo-ui-labelElement-label {
-       padding-left: 2.25em;
-}
-.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-iconElement .oo-ui-iconElement-icon {
-       right: 0.5em;
-}
-.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-iconElement .oo-ui-labelElement-label {
-       padding-right: 2.25em;
-}
 .oo-ui-processDialog > .oo-ui-window-frame {
        min-height: 5em;
 }
        overflow: hidden;
        max-width: 100%;
        max-height: 100%;
+       visibility: visible;
 }
 .oo-ui-windowManager-modal > .oo-ui-dialog.oo-ui-window-setup > .oo-ui-window-frame > iframe {
        width: 100%;
        height: 100%;
 }
-.oo-ui-windowManager-modal > .oo-ui-dialog .oo-ui-window-frame {
+.oo-ui-windowManager-modal > .oo-ui-dialog .oo-ui-window-frame {
        visibility: hidden;
 }
-.oo-ui-windowManager-modal > .oo-ui-dialog.oo-ui-window-ready > .oo-ui-window-frame {
-       visibility: visible;
-}
 .oo-ui-windowManager-fullscreen > .oo-ui-dialog > .oo-ui-window-frame {
        width: 100%;
        height: 100%;
 .oo-ui-windowManager-modal > .oo-ui-dialog.oo-ui-window-load {
        opacity: 1;
 }
-.oo-ui-windowManager-modal > .oo-ui-dialog.oo-ui-window-ready > .oo-ui-window-frame {
+.oo-ui-windowManager-modal > .oo-ui-dialog.oo-ui-window-setup > .oo-ui-window-frame {
        -webkit-transform: scale(1);
           -moz-transform: scale(1);
            -ms-transform: scale(1);
index d781397..2e2a011 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.1.0-pre (fe4076af75)
+ * OOjs UI v0.2.2
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2014 OOjs Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2014-11-13T22:25:17Z
+ * Date: 2014-11-25T01:13:13Z
  */
 ( function ( OO ) {
 
@@ -101,8 +101,8 @@ OO.ui.getLocalValue = function ( obj, lang, fallback ) {
  *
  * @param {HTMLElement|HTMLElement[]} containers Container node(s) to search in
  * @param {HTMLElement} contained Node to find
- * @param {boolean} [matchContainers] Include the container(s) in the list of nodes to match, otherwise only match descendents
- * @returns {boolean} The node is in the list of target nodes
+ * @param {boolean} [matchContainers] Include the container(s) in the list of nodes to match, otherwise only match descendants
+ * @return {boolean} The node is in the list of target nodes
  */
 OO.ui.contains = function ( containers, contained, matchContainers ) {
        var i;
@@ -314,7 +314,7 @@ OO.ui.PendingElement.prototype.popPending = function () {
  * @param {Object} [config] Configuration options
  */
 OO.ui.ActionSet = function OoUiActionSet( config ) {
-       // Configuration intialization
+       // Configuration initialization
        config = config || {};
 
        // Mixin constructors
@@ -656,7 +656,7 @@ OO.ui.ActionSet.prototype.clear = function () {
 /**
  * Organize actions.
  *
- * This is called whenver organized information is requested. It will only reorganize the actions
+ * This is called whenever organized information is requested. It will only reorganize the actions
  * if something has changed since the last time it ran.
  *
  * @private
@@ -673,7 +673,7 @@ OO.ui.ActionSet.prototype.organize = function () {
                for ( i = 0, iLen = this.list.length; i < iLen; i++ ) {
                        action = this.list[i];
                        if ( action.isVisible() ) {
-                               // Populate catgeories
+                               // Populate categories
                                for ( category in this.categories ) {
                                        if ( !this.categorized[category] ) {
                                                this.categorized[category] = {};
@@ -723,6 +723,7 @@ OO.ui.ActionSet.prototype.organize = function () {
  * @cfg {string[]} [classes] CSS class names to add
  * @cfg {string} [text] Text to insert
  * @cfg {jQuery} [$content] Content elements to append (after text)
+ * @cfg {Mixed} [data] Element data
  */
 OO.ui.Element = function OoUiElement( config ) {
        // Configuration initialization
@@ -730,6 +731,7 @@ OO.ui.Element = function OoUiElement( config ) {
 
        // Properties
        this.$ = config.$ || OO.ui.Element.getJQuery( document );
+       this.data = config.data;
        this.$element = this.$( this.$.context.createElement( this.getTagName() ) );
        this.elementGroup = null;
        this.debouncedUpdateThemeClassesHandler = this.debouncedUpdateThemeClasses.bind( this );
@@ -1135,6 +1137,26 @@ OO.ui.Element.offDOMEvent = function ( el, event, callback ) {
 
 /* Methods */
 
+/**
+ * Get element data.
+ *
+ * @return {Mixed} Element data
+ */
+OO.ui.Element.prototype.getData = function () {
+       return this.data;
+};
+
+/**
+ * Set element data.
+ *
+ * @param {Mixed} Element data
+ * @chainable
+ */
+OO.ui.Element.prototype.setData = function ( data ) {
+       this.data = data;
+       return this;
+};
+
 /**
  * Check if element supports one or more methods.
  *
@@ -1158,7 +1180,7 @@ OO.ui.Element.prototype.supports = function ( methods ) {
 /**
  * Update the theme-provided classes.
  *
- * @localdoc This is called in element mixins and widget classes anytime state changes.
+ * @localdoc This is called in element mixins and widget classes any time state changes.
  *   Updating is debounced, minimizing overhead of changing multiple attributes and
  *   guaranteeing that theme updates do not occur within an element's constructor
  */
@@ -1451,8 +1473,8 @@ OO.ui.Widget.prototype.updateDisabled = function () {
  *
  * Each process (setup, ready, hold and teardown) can be extended in subclasses by overriding
  * {@link #getSetupProcess}, {@link #getReadyProcess}, {@link #getHoldProcess} and
- * {@link #getTeardownProcess} respectively. Each process is executed in series, so asynchonous
- * processing can complete. Always assume window processes are executed asychronously. See
+ * {@link #getTeardownProcess} respectively. Each process is executed in series, so asynchronous
+ * processing can complete. Always assume window processes are executed asynchronously. See
  * OO.ui.Process for more details about how to work with processes. Some events, as well as the
  * #open and #close methods, provide promises which are resolved when the window enters a new state.
  *
@@ -1494,7 +1516,7 @@ OO.ui.Window = function OoUiWindow( config ) {
        this.$frame.addClass( 'oo-ui-window-frame' );
        this.$overlay.addClass( 'oo-ui-window-overlay' );
 
-       // NOTE: Additional intitialization will occur when #setManager is called
+       // NOTE: Additional initialization will occur when #setManager is called
 };
 
 /* Setup */
@@ -1840,7 +1862,7 @@ OO.ui.Window.prototype.getTeardownProcess = function () {
 /**
  * Toggle visibility of window.
  *
- * If the window is isolated and hasn't fully loaded yet, the visiblity property will be used
+ * If the window is isolated and hasn't fully loaded yet, the visibility property will be used
  * instead of display.
  *
  * @param {boolean} [show] Make window visible, omit to toggle visibility
@@ -2013,7 +2035,7 @@ OO.ui.Window.prototype.close = function ( data ) {
 /**
  * Setup window.
  *
- * This is called by OO.ui.WindowManager durring window opening, and should not be called directly
+ * This is called by OO.ui.WindowManager during window opening, and should not be called directly
  * by other systems.
  *
  * @param {Object} [data] Window opening data
@@ -2038,7 +2060,7 @@ OO.ui.Window.prototype.setup = function ( data ) {
 /**
  * Ready window.
  *
- * This is called by OO.ui.WindowManager durring window opening, and should not be called directly
+ * This is called by OO.ui.WindowManager during window opening, and should not be called directly
  * by other systems.
  *
  * @param {Object} [data] Window opening data
@@ -2062,7 +2084,7 @@ OO.ui.Window.prototype.ready = function ( data ) {
 /**
  * Hold window.
  *
- * This is called by OO.ui.WindowManager durring window closing, and should not be called directly
+ * This is called by OO.ui.WindowManager during window closing, and should not be called directly
  * by other systems.
  *
  * @param {Object} [data] Window closing data
@@ -2093,7 +2115,7 @@ OO.ui.Window.prototype.hold = function ( data ) {
 /**
  * Teardown window.
  *
- * This is called by OO.ui.WindowManager durring window closing, and should not be called directly
+ * This is called by OO.ui.WindowManager during window closing, and should not be called directly
  * by other systems.
  *
  * @param {Object} [data] Window closing data
@@ -3162,7 +3184,7 @@ OO.ui.WindowManager.prototype.toggleAriaIsolation = function ( isolate ) {
                                .attr( 'aria-hidden', '' );
                }
        } else if ( this.$ariaHidden ) {
-               // Restore screen reader visiblity
+               // Restore screen reader visibility
                this.$ariaHidden.removeAttr( 'aria-hidden' );
                this.$ariaHidden = null;
        }
@@ -3592,7 +3614,7 @@ OO.ui.Theme.prototype.getElementClasses = function ( /* element */ ) {
 /**
  * Update CSS classes provided by the theme.
  *
- * For elements with theme logic hooks, this should be called anytime there's a state change.
+ * For elements with theme logic hooks, this should be called any time there's a state change.
  *
  * @param {OO.ui.Element} element Element for which to update classes
  * @return {Object.<string,string[]>} Categorized class names with `on` and `off` lists
@@ -3711,7 +3733,7 @@ OO.ui.ButtonElement.prototype.onMouseUp = function ( e ) {
        if ( this.isDisabled() || e.which !== 1 ) {
                return false;
        }
-       // Restore the tab-index after the button is up to restore the button's accesssibility
+       // Restore the tab-index after the button is up to restore the button's accessibility
        this.$button.attr( 'tabindex', this.tabIndex );
        this.$element.removeClass( 'oo-ui-buttonElement-pressed' );
        // Stop listening for mouseup, since we only needed this once
@@ -3814,7 +3836,7 @@ OO.ui.ButtonElement.prototype.setActive = function ( value ) {
  * @cfg {jQuery} [$group] Container node, assigned to #$group, omit to use a generated `<div>`
  */
 OO.ui.GroupElement = function OoUiGroupElement( config ) {
-       // Configuration intialization
+       // Configuration initialization
        config = config || {};
 
        // Properties
@@ -3862,6 +3884,51 @@ OO.ui.GroupElement.prototype.getItems = function () {
        return this.items.slice( 0 );
 };
 
+/**
+ * Get an item by its data.
+ *
+ * Data is compared by a hash of its value. Only the first item with matching data will be returned.
+ *
+ * @param {Object} data Item data to search for
+ * @return {OO.ui.Element|null} Item with equivalent data, `null` if none exists
+ */
+OO.ui.GroupElement.prototype.getItemFromData = function ( data ) {
+       var i, len, item,
+               hash = OO.getHash( data );
+
+       for ( i = 0, len = this.items.length; i < len; i++ ) {
+               item = this.items[i];
+               if ( hash === OO.getHash( item.getData() ) ) {
+                       return item;
+               }
+       }
+
+       return null;
+};
+
+/**
+ * Get items by their data.
+ *
+ * Data is compared by a hash of its value. All items with matching data will be returned.
+ *
+ * @param {Object} data Item data to search for
+ * @return {OO.ui.Element[]} Items with equivalent data
+ */
+OO.ui.GroupElement.prototype.getItemsFromData = function ( data ) {
+       var i, len, item,
+               hash = OO.getHash( data ),
+               items = [];
+
+       for ( i = 0, len = this.items.length; i < len; i++ ) {
+               item = this.items[i];
+               if ( hash === OO.getHash( item.getData() ) ) {
+                       items.push( item );
+               }
+       }
+
+       return items;
+};
+
 /**
  * Add an aggregate item event.
  *
@@ -4052,7 +4119,7 @@ OO.ui.GroupElement.prototype.clearItems = function () {
  * @cfg {string} [iconTitle] Icon title text or a function that returns text
  */
 OO.ui.IconElement = function OoUiIconElement( config ) {
-       // Configuration intialization
+       // Configuration initialization
        config = config || {};
 
        // Properties
@@ -4474,7 +4541,6 @@ OO.ui.LabelElement.prototype.setLabelContent = function ( label ) {
        } else {
                this.$label.empty();
        }
-       this.$label.css( 'display', !label ? 'none' : '' );
 };
 
 /**
@@ -5017,7 +5083,14 @@ OO.ui.Tool = function OoUiTool( toolGroup, config ) {
 
        // Initialization
        this.$titleText.addClass( 'oo-ui-tool-title-text' );
-       this.$accel.addClass( 'oo-ui-tool-accel' );
+       this.$accel
+               .addClass( 'oo-ui-tool-accel' )
+               .prop( {
+                       // This may need to be changed if the key names are ever localized,
+                       // but for now they are essentially written in English
+                       dir: 'ltr',
+                       lang: 'en'
+               } );
        this.$title
                .addClass( 'oo-ui-tool-title' )
                .append( this.$titleText, this.$accel );
@@ -5277,12 +5350,12 @@ OO.ui.Toolbar = function OoUiToolbar( toolFactory, toolGroupFactory, config ) {
 
        // Initialization
        this.$group.addClass( 'oo-ui-toolbar-tools' );
-       this.$bar.addClass( 'oo-ui-toolbar-bar' ).append( this.$group );
        if ( config.actions ) {
-               this.$actions.addClass( 'oo-ui-toolbar-actions' );
-               this.$bar.append( this.$actions );
+               this.$bar.append( this.$actions.addClass( 'oo-ui-toolbar-actions' ) );
        }
-       this.$bar.append( '<div style="clear:both"></div>' );
+       this.$bar
+               .addClass( 'oo-ui-toolbar-bar' )
+               .append( this.$group, '<div style="clear:both"></div>' );
        if ( config.shadow ) {
                this.$bar.append( '<div class="oo-ui-toolbar-shadow"></div>' );
        }
@@ -5805,7 +5878,7 @@ OO.ui.MessageDialog.static.verbose = false;
  * Dialog title.
  *
  * A confirmation dialog's title should describe what the progressive action will do. An alert
- * dialog's title should describe what event occured.
+ * dialog's title should describe what event occurred.
  *
  * @static
  * inheritable
@@ -5815,7 +5888,7 @@ OO.ui.MessageDialog.static.title = null;
 
 /**
  * A confirmation dialog's message should describe the consequences of the progressive action. An
- * alert dialog's message should describe why the event occured.
+ * alert dialog's message should describe why the event occurred.
  *
  * @static
  * inheritable
@@ -5835,7 +5908,7 @@ OO.ui.MessageDialog.static.actions = [
  */
 OO.ui.MessageDialog.prototype.onActionResize = function ( action ) {
        this.fitActions();
-       return OO.ui.ProcessDialog.super.prototype.onActionResize.call( this, action );
+       return OO.ui.MessageDialog.super.prototype.onActionResize.call( this, action );
 };
 
 /**
@@ -5961,10 +6034,9 @@ OO.ui.MessageDialog.prototype.attachActions = function () {
                special.primary.toggleFramed( false );
        }
 
+       this.manager.updateWindowSize( this );
        this.fitActions();
-       if ( !this.isOpening() ) {
-               this.manager.updateWindowSize( this );
-       }
+
        this.$body.css( 'bottom', this.$foot.outerHeight( true ) );
 };
 
@@ -6161,7 +6233,7 @@ OO.ui.ProcessDialog.prototype.fitLabel = function () {
 };
 
 /**
- * Handle errors that occured durring accept or reject processes.
+ * Handle errors that occurred during accept or reject processes.
  *
  * @param {OO.ui.Error[]} errors Errors to be handled
  */
@@ -6239,7 +6311,7 @@ OO.ui.BookletLayout = function OoUiBookletLayout( config ) {
        if ( this.outlined ) {
                this.editable = !!config.editable;
                this.outlineControlsWidget = null;
-               this.outlineWidget = new OO.ui.OutlineWidget( { $: this.$ } );
+               this.outlineSelectWidget = new OO.ui.OutlineSelectWidget( { $: this.$ } );
                this.outlinePanel = new OO.ui.PanelLayout( { $: this.$, scrollable: true } );
                this.gridLayout = new OO.ui.GridLayout(
                        [ this.outlinePanel, this.stackLayout ],
@@ -6248,7 +6320,7 @@ OO.ui.BookletLayout = function OoUiBookletLayout( config ) {
                this.outlineVisible = true;
                if ( this.editable ) {
                        this.outlineControlsWidget = new OO.ui.OutlineControlsWidget(
-                               this.outlineWidget, { $: this.$ }
+                               this.outlineSelectWidget, { $: this.$ }
                        );
                }
        }
@@ -6256,7 +6328,7 @@ OO.ui.BookletLayout = function OoUiBookletLayout( config ) {
        // Events
        this.stackLayout.connect( this, { set: 'onStackLayoutSet' } );
        if ( this.outlined ) {
-               this.outlineWidget.connect( this, { select: 'onOutlineWidgetSelect' } );
+               this.outlineSelectWidget.connect( this, { select: 'onOutlineSelectWidgetSelect' } );
        }
        if ( this.autoFocus ) {
                // Event 'focus' does not bubble, but 'focusin' does
@@ -6269,7 +6341,7 @@ OO.ui.BookletLayout = function OoUiBookletLayout( config ) {
        if ( this.outlined ) {
                this.outlinePanel.$element
                        .addClass( 'oo-ui-bookletLayout-outlinePanel' )
-                       .append( this.outlineWidget.$element );
+                       .append( this.outlineSelectWidget.$element );
                if ( this.editable ) {
                        this.outlinePanel.$element
                                .addClass( 'oo-ui-bookletLayout-outlinePanel-editable' )
@@ -6330,28 +6402,43 @@ OO.ui.BookletLayout.prototype.onStackLayoutFocus = function ( e ) {
  * @param {OO.ui.PanelLayout|null} page The page panel that is now the current panel
  */
 OO.ui.BookletLayout.prototype.onStackLayoutSet = function ( page ) {
-       var $input, layout = this;
+       var layout = this;
        if ( page ) {
                page.scrollElementIntoView( { complete: function () {
                        if ( layout.autoFocus ) {
-                               // Set focus to the first input if nothing on the page is focused yet
-                               if ( !page.$element.find( ':focus' ).length ) {
-                                       $input = page.$element.find( ':input:first' );
-                                       if ( $input.length ) {
-                                               $input[0].focus();
-                                       }
-                               }
+                               layout.focus();
                        }
                } } );
        }
 };
 
+/**
+ * Focus the first input in the current page.
+ *
+ * If no page is selected, the first selectable page will be selected.
+ * If the focus is already in an element on the current page, nothing will happen.
+ */
+OO.ui.BookletLayout.prototype.focus = function () {
+       var $input, page = this.stackLayout.getCurrentItem();
+       if ( !page ) {
+               this.selectFirstSelectablePage();
+               page = this.stackLayout.getCurrentItem();
+       }
+       // Only change the focus if is not already in the current page
+       if ( !page.$element.find( ':focus' ).length ) {
+               $input = page.$element.find( ':input:first' );
+               if ( $input.length ) {
+                       $input[0].focus();
+               }
+       }
+};
+
 /**
  * Handle outline widget select events.
  *
  * @param {OO.ui.OptionWidget|null} item Selected item
  */
-OO.ui.BookletLayout.prototype.onOutlineWidgetSelect = function ( item ) {
+OO.ui.BookletLayout.prototype.onOutlineSelectWidgetSelect = function ( item ) {
        if ( item ) {
                this.setPage( item.getData() );
        }
@@ -6416,16 +6503,16 @@ OO.ui.BookletLayout.prototype.getClosestPage = function ( page ) {
                prev = pages[index - 1];
                // Prefer adjacent pages at the same level
                if ( this.outlined ) {
-                       level = this.outlineWidget.getItemFromData( page.getName() ).getLevel();
+                       level = this.outlineSelectWidget.getItemFromData( page.getName() ).getLevel();
                        if (
                                prev &&
-                               level === this.outlineWidget.getItemFromData( prev.getName() ).getLevel()
+                               level === this.outlineSelectWidget.getItemFromData( prev.getName() ).getLevel()
                        ) {
                                return prev;
                        }
                        if (
                                next &&
-                               level === this.outlineWidget.getItemFromData( next.getName() ).getLevel()
+                               level === this.outlineSelectWidget.getItemFromData( next.getName() ).getLevel()
                        ) {
                                return next;
                        }
@@ -6437,10 +6524,10 @@ OO.ui.BookletLayout.prototype.getClosestPage = function ( page ) {
 /**
  * Get the outline widget.
  *
- * @return {OO.ui.OutlineWidget|null} Outline widget, or null if boolet has no outline
+ * @return {OO.ui.OutlineSelectWidget|null} Outline widget, or null if booklet has no outline
  */
 OO.ui.BookletLayout.prototype.getOutline = function () {
-       return this.outlineWidget;
+       return this.outlineSelectWidget;
 };
 
 /**
@@ -6512,15 +6599,15 @@ OO.ui.BookletLayout.prototype.addPages = function ( pages, index ) {
                name = page.getName();
                this.pages[page.getName()] = page;
                if ( this.outlined ) {
-                       item = new OO.ui.OutlineItemWidget( name, page, { $: this.$ } );
+                       item = new OO.ui.OutlineOptionWidget( { $: this.$, data: name } );
                        page.setOutlineItem( item );
                        items.push( item );
                }
        }
 
        if ( this.outlined && items.length ) {
-               this.outlineWidget.addItems( items, index );
-               this.updateOutlineWidget();
+               this.outlineSelectWidget.addItems( items, index );
+               this.selectFirstSelectablePage();
        }
        this.stackLayout.addItems( pages, index );
        this.emit( 'add', pages, index );
@@ -6543,13 +6630,13 @@ OO.ui.BookletLayout.prototype.removePages = function ( pages ) {
                name = page.getName();
                delete this.pages[name];
                if ( this.outlined ) {
-                       items.push( this.outlineWidget.getItemFromData( name ) );
+                       items.push( this.outlineSelectWidget.getItemFromData( name ) );
                        page.setOutlineItem( null );
                }
        }
        if ( this.outlined && items.length ) {
-               this.outlineWidget.removeItems( items );
-               this.updateOutlineWidget();
+               this.outlineSelectWidget.removeItems( items );
+               this.selectFirstSelectablePage();
        }
        this.stackLayout.removeItems( pages );
        this.emit( 'remove', pages );
@@ -6570,7 +6657,7 @@ OO.ui.BookletLayout.prototype.clearPages = function () {
        this.pages = {};
        this.currentPageName = null;
        if ( this.outlined ) {
-               this.outlineWidget.clearItems();
+               this.outlineSelectWidget.clearItems();
                for ( i = 0, len = pages.length; i < len; i++ ) {
                        pages[i].setOutlineItem( null );
                }
@@ -6595,9 +6682,9 @@ OO.ui.BookletLayout.prototype.setPage = function ( name ) {
 
        if ( name !== this.currentPageName ) {
                if ( this.outlined ) {
-                       selectedItem = this.outlineWidget.getSelectedItem();
+                       selectedItem = this.outlineSelectWidget.getSelectedItem();
                        if ( selectedItem && selectedItem.getData() !== name ) {
-                               this.outlineWidget.selectItem( this.outlineWidget.getItemFromData( name ) );
+                               this.outlineSelectWidget.selectItem( this.outlineSelectWidget.getItemFromData( name ) );
                        }
                }
                if ( page ) {
@@ -6622,14 +6709,13 @@ OO.ui.BookletLayout.prototype.setPage = function ( name ) {
 };
 
 /**
- * Call this after adding or removing items from the OutlineWidget.
+ * Select the first selectable page.
  *
  * @chainable
  */
-OO.ui.BookletLayout.prototype.updateOutlineWidget = function () {
-       // Auto-select first item when nothing is selected anymore
-       if ( !this.outlineWidget.getSelectedItem() ) {
-               this.outlineWidget.selectItem( this.outlineWidget.getFirstSelectableItem() );
+OO.ui.BookletLayout.prototype.selectFirstSelectablePage = function () {
+       if ( !this.outlineSelectWidget.getSelectedItem() ) {
+               this.outlineSelectWidget.selectItem( this.outlineSelectWidget.getFirstSelectableItem() );
        }
 
        return this;
@@ -6662,6 +6748,9 @@ OO.ui.FieldLayout = function OoUiFieldLayout( fieldWidget, config ) {
        // Configuration initialization
        config = $.extend( { align: 'left' }, config );
 
+       // Properties (must be set before parent constructor, which calls #getTagName)
+       this.fieldWidget = fieldWidget;
+
        // Parent constructor
        OO.ui.FieldLayout.super.call( this, config );
 
@@ -6670,7 +6759,6 @@ OO.ui.FieldLayout = function OoUiFieldLayout( fieldWidget, config ) {
 
        // Properties
        this.$field = this.$( '<div>' );
-       this.fieldWidget = fieldWidget;
        this.align = null;
        if ( config.help ) {
                this.popupButtonWidget = new OO.ui.PopupButtonWidget( {
@@ -6710,12 +6798,19 @@ OO.ui.FieldLayout = function OoUiFieldLayout( fieldWidget, config ) {
 OO.inheritClass( OO.ui.FieldLayout, OO.ui.Layout );
 OO.mixinClass( OO.ui.FieldLayout, OO.ui.LabelElement );
 
-/* Static Properties */
-
-OO.ui.FieldLayout.static.tagName = 'label';
-
 /* Methods */
 
+/**
+ * @inheritdoc
+ */
+OO.ui.FieldLayout.prototype.getTagName = function () {
+       if ( this.fieldWidget instanceof OO.ui.InputWidget ) {
+               return 'label';
+       } else {
+               return 'div';
+       }
+};
+
 /**
  * Handle field disable events.
  *
@@ -7031,7 +7126,7 @@ OO.ui.GridLayout.prototype.update = function () {
  *
  * @param {number} x Horizontal position
  * @param {number} y Vertical position
- * @return {OO.ui.PanelLayout} The panel at the given postion
+ * @return {OO.ui.PanelLayout} The panel at the given position
  */
 OO.ui.GridLayout.prototype.getPanel = function ( x, y ) {
        return this.panels[ ( x * this.widths.length ) + y ];
@@ -7138,7 +7233,7 @@ OO.ui.PageLayout.prototype.isActive = function () {
 /**
  * Get outline item.
  *
- * @return {OO.ui.OutlineItemWidget|null} Outline item widget
+ * @return {OO.ui.OutlineOptionWidget|null} Outline item widget
  */
 OO.ui.PageLayout.prototype.getOutlineItem = function () {
        return this.outlineItem;
@@ -7150,9 +7245,9 @@ OO.ui.PageLayout.prototype.getOutlineItem = function () {
  * @localdoc Subclasses should override #setupOutlineItem instead of this method to adjust the
  *   outline item as desired; this method is called for setting (with an object) and unsetting
  *   (with null) and overriding methods would have to check the value of `outlineItem` to avoid
- *   operating on null instead of an OO.ui.OutlineItemWidget object.
+ *   operating on null instead of an OO.ui.OutlineOptionWidget object.
  *
- * @param {OO.ui.OutlineItemWidget|null} outlineItem Outline item widget, null to clear
+ * @param {OO.ui.OutlineOptionWidget|null} outlineItem Outline item widget, null to clear
  * @chainable
  */
 OO.ui.PageLayout.prototype.setOutlineItem = function ( outlineItem ) {
@@ -7168,7 +7263,7 @@ OO.ui.PageLayout.prototype.setOutlineItem = function ( outlineItem ) {
  *
  * @localdoc Subclasses should override this method to adjust the outline item as desired.
  *
- * @param {OO.ui.OutlineItemWidget} outlineItem Outline item widget to setup
+ * @param {OO.ui.OutlineOptionWidget} outlineItem Outline item widget to setup
  * @chainable
  */
 OO.ui.PageLayout.prototype.setupOutlineItem = function () {
@@ -7581,7 +7676,7 @@ OO.ui.PopupToolGroup.prototype.setActive = function ( value ) {
  * @cfg {boolean} [expanded=false] Whether the collapsible tools are expanded by default
  */
 OO.ui.ListToolGroup = function OoUiListToolGroup( toolbar, config ) {
-       // Configuration intialization
+       // Configuration initialization
        config = config || {};
 
        // Properties (must be set before parent constructor, which calls #populate)
@@ -7858,7 +7953,7 @@ OO.ui.GroupWidget.prototype.setDisabled = function ( disabled ) {
  * Mixin for widgets used as items in widgets that inherit OO.ui.GroupWidget.
  *
  * Item widgets have a reference to a OO.ui.GroupWidget while they are attached to the group. This
- * allows bidrectional communication.
+ * allows bidirectional communication.
  *
  * Use together with OO.ui.GroupWidget to make disabled state inheritable.
  *
@@ -7927,7 +8022,7 @@ OO.ui.LookupInputWidget = function OoUiLookupInputWidget( input, config ) {
        // Properties
        this.lookupInput = input;
        this.$overlay = config.$overlay || this.$element;
-       this.lookupMenu = new OO.ui.TextInputMenuWidget( this, {
+       this.lookupMenu = new OO.ui.TextInputMenuSelectWidget( this, {
                $: OO.ui.Element.getJQuery( this.$overlay ),
                input: this.lookupInput,
                $container: config.$container
@@ -8009,7 +8104,7 @@ OO.ui.LookupInputWidget.prototype.onLookupMenuToggle = function ( visible ) {
        if ( !visible ) {
                // When the menu is hidden, abort any active request and clear the menu.
                // This has to be done here in addition to closeLookupMenu(), because
-               // MenuWidget will close itself when the user presses Esc.
+               // MenuSelectWidget will close itself when the user presses Esc.
                this.abortLookupRequest();
                this.lookupMenu.clearItems();
        }
@@ -8018,7 +8113,7 @@ OO.ui.LookupInputWidget.prototype.onLookupMenuToggle = function ( visible ) {
 /**
  * Get lookup menu.
  *
- * @return {OO.ui.TextInputMenuWidget}
+ * @return {OO.ui.TextInputMenuSelectWidget}
  */
 OO.ui.LookupInputWidget.prototype.getLookupMenu = function () {
        return this.lookupMenu;
@@ -8195,7 +8290,7 @@ OO.ui.LookupInputWidget.prototype.getLookupRequest = function () {
  *
  * @abstract
  * @param {Mixed} data Cached result data, usually an array
- * @return {OO.ui.MenuItemWidget[]} Menu items
+ * @return {OO.ui.MenuOptionWidget[]} Menu items
  */
 OO.ui.LookupInputWidget.prototype.getLookupMenuItemsFromData = function () {
        // Stub, implemented in subclass
@@ -8215,7 +8310,7 @@ OO.ui.LookupInputWidget.prototype.getLookupCacheItemFromData = function () {
 };
 
 /**
- * Set of controls for an OO.ui.OutlineWidget.
+ * Set of controls for an OO.ui.OutlineSelectWidget.
  *
  * Controls include moving items up and down, removing items, and adding different kinds of items.
  *
@@ -8225,7 +8320,7 @@ OO.ui.LookupInputWidget.prototype.getLookupCacheItemFromData = function () {
  * @mixins OO.ui.IconElement
  *
  * @constructor
- * @param {OO.ui.OutlineWidget} outline Outline to control
+ * @param {OO.ui.OutlineSelectWidget} outline Outline to control
  * @param {Object} [config] Configuration options
  */
 OO.ui.OutlineControlsWidget = function OoUiOutlineControlsWidget( outline, config ) {
@@ -8854,7 +8949,7 @@ OO.ui.ToggleButtonWidget.prototype.setValue = function ( value ) {
  * Dropdown menus provide a control for accessing a menu and compose a menu within the widget, which
  * can be accessed using the #getMenu method.
  *
- * Use with OO.ui.MenuItemWidget.
+ * Use with OO.ui.MenuOptionWidget.
  *
  * @class
  * @extends OO.ui.Widget
@@ -8881,7 +8976,7 @@ OO.ui.DropdownWidget = function OoUiDropdownWidget( config ) {
        OO.ui.TitledElement.call( this, $.extend( {}, config, { $titled: this.$label } ) );
 
        // Properties
-       this.menu = new OO.ui.MenuWidget( $.extend( { $: this.$, widget: this }, config.menu ) );
+       this.menu = new OO.ui.MenuSelectWidget( $.extend( { $: this.$, widget: this }, config.menu ) );
        this.$handle = this.$( '<span>' );
 
        // Events
@@ -8910,7 +9005,7 @@ OO.mixinClass( OO.ui.DropdownWidget, OO.ui.TitledElement );
 /**
  * Get the menu.
  *
- * @return {OO.ui.MenuWidget} Menu of widget
+ * @return {OO.ui.MenuSelectWidget} Menu of widget
  */
 OO.ui.DropdownWidget.prototype.getMenu = function () {
        return this.menu;
@@ -8919,7 +9014,7 @@ OO.ui.DropdownWidget.prototype.getMenu = function () {
 /**
  * Handles menu select events.
  *
- * @param {OO.ui.MenuItemWidget} item Selected menu item
+ * @param {OO.ui.MenuOptionWidget} item Selected menu item
  */
 OO.ui.DropdownWidget.prototype.onMenuSelect = function ( item ) {
        var selectedLabel;
@@ -9147,12 +9242,12 @@ OO.ui.InputWidget.prototype.setRTL = function ( isRTL ) {
  * @chainable
  */
 OO.ui.InputWidget.prototype.setValue = function ( value ) {
-       value = this.sanitizeValue( value );
+       value = this.cleanUpValue( value );
        if ( this.value !== value ) {
                this.value = value;
                this.emit( 'change', this.value );
        }
-       // Update the DOM if it has changed. Note that with sanitizeValue, it
+       // Update the DOM if it has changed. Note that with cleanUpValue, it
        // is possible for the DOM value to change without this.value changing.
        if ( this.$input.val() !== this.value ) {
                this.$input.val( this.value );
@@ -9161,15 +9256,15 @@ OO.ui.InputWidget.prototype.setValue = function ( value ) {
 };
 
 /**
- * Sanitize incoming value.
+ * Clean up incoming value.
  *
  * Ensures value is a string, and converts undefined and null to empty string.
  *
  * @private
  * @param {string} value Original value
- * @return {string} Sanitized value
+ * @return {string} Cleaned up value
  */
-OO.ui.InputWidget.prototype.sanitizeValue = function ( value ) {
+OO.ui.InputWidget.prototype.cleanUpValue = function ( value ) {
        if ( value === undefined || value === null ) {
                return '';
        } else if ( this.inputFilter ) {
@@ -9300,7 +9395,7 @@ OO.mixinClass( OO.ui.ButtonInputWidget, OO.ui.FlaggedElement );
  * @return {jQuery} Input element
  */
 OO.ui.ButtonInputWidget.prototype.getInputElement = function ( config ) {
-       // Configuration intialization
+       // Configuration initialization
        config = config || {};
 
        var html = '<' + ( config.useInputTag ? 'input' : 'button' ) + ' type="' + config.type + '">';
@@ -9446,6 +9541,74 @@ OO.ui.CheckboxInputWidget.prototype.onEdit = function () {
        }
 };
 
+/**
+ * Radio input widget.
+ *
+ * Radio buttons only make sense as a set, and you probably want to use the OO.ui.RadioSelectWidget
+ * class instead of using this class directly.
+ *
+ * This class doesn't make it possible to learn whether the radio button is selected ("pressed").
+ *
+ * @class
+ * @extends OO.ui.InputWidget
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @param {boolean} [config.selected=false] Whether the radio button is initially selected
+ */
+OO.ui.RadioInputWidget = function OoUiRadioInputWidget( config ) {
+       // Parent constructor
+       OO.ui.RadioInputWidget.super.call( this, config );
+
+       // Initialization
+       this.$element.addClass( 'oo-ui-radioInputWidget' );
+       this.setSelected( config.selected !== undefined ? config.selected : false );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.RadioInputWidget, OO.ui.InputWidget );
+
+/* Methods */
+
+/**
+ * Get input element.
+ *
+ * @private
+ * @return {jQuery} Input element
+ */
+OO.ui.RadioInputWidget.prototype.getInputElement = function () {
+       return this.$( '<input type="radio" />' );
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.RadioInputWidget.prototype.onEdit = function () {
+       // RadioInputWidget doesn't track its state.
+};
+
+/**
+ * Set selection state of this radio button.
+ *
+ * @param {boolean} state Whether the button is selected
+ * @chainable
+ */
+OO.ui.RadioInputWidget.prototype.setSelected = function ( state ) {
+       // RadioInputWidget doesn't track its state.
+       this.$input.prop( 'checked', state );
+       return this;
+};
+
+/**
+ * Check if this radio button is selected.
+ *
+ * @return {boolean} Radio is selected
+ */
+OO.ui.RadioInputWidget.prototype.isSelected = function () {
+       return this.$input.prop( 'checked' );
+};
+
 /**
  * Input widget with a text field.
  *
@@ -9628,7 +9791,7 @@ OO.ui.TextInputWidget.prototype.isReadOnly = function () {
 /**
  * Set the read-only state of the widget.
  *
- * This should probably change the widgets's appearance and prevent it from being used.
+ * This should probably change the widget's appearance and prevent it from being used.
  *
  * @param {boolean} state Make input read-only
  * @chainable
@@ -9781,7 +9944,7 @@ OO.ui.ComboBoxWidget = function OoUiComboBoxWidget( config ) {
                { $: this.$, indicator: 'down', disabled: this.isDisabled() },
                config.input
        ) );
-       this.menu = new OO.ui.TextInputMenuWidget( this.input, $.extend(
+       this.menu = new OO.ui.TextInputMenuSelectWidget( this.input, $.extend(
                {
                        $: OO.ui.Element.getJQuery( this.$overlay ),
                        widget: this,
@@ -9815,6 +9978,14 @@ OO.inheritClass( OO.ui.ComboBoxWidget, OO.ui.Widget );
 
 /* Methods */
 
+/**
+ * Get the combobox's menu.
+ * @return {OO.ui.TextInputMenuSelectWidget} Menu widget
+ */
+OO.ui.ComboBoxWidget.prototype.getMenu = function () {
+       return this.menu;
+};
+
 /**
  * Handle input change events.
  *
@@ -9894,6 +10065,7 @@ OO.ui.ComboBoxWidget.prototype.setDisabled = function ( disabled ) {
  *
  * @constructor
  * @param {Object} [config] Configuration options
+ * @cfg {OO.ui.InputWidget} [input] Input widget this label is for
  */
 OO.ui.LabelWidget = function OoUiLabelWidget( config ) {
        // Configuration initialization
@@ -9949,10 +10121,9 @@ OO.ui.LabelWidget.prototype.onClick = function () {
  * @mixins OO.ui.FlaggedElement
  *
  * @constructor
- * @param {Mixed} data Option data
  * @param {Object} [config] Configuration options
  */
-OO.ui.OptionWidget = function OoUiOptionWidget( data, config ) {
+OO.ui.OptionWidget = function OoUiOptionWidget( config ) {
        // Configuration initialization
        config = config || {};
 
@@ -9965,7 +10136,6 @@ OO.ui.OptionWidget = function OoUiOptionWidget( data, config ) {
        OO.ui.FlaggedElement.call( this, config );
 
        // Properties
-       this.data = data;
        this.selected = false;
        this.highlighted = false;
        this.pressed = false;
@@ -10129,15 +10299,6 @@ OO.ui.OptionWidget.prototype.flash = function () {
        return deferred.promise();
 };
 
-/**
- * Get option data.
- *
- * @return {Mixed} Option data
- */
-OO.ui.OptionWidget.prototype.getData = function () {
-       return this.data;
-};
-
 /**
  * Option widget with an option icon and indicator.
  *
@@ -10149,12 +10310,11 @@ OO.ui.OptionWidget.prototype.getData = function () {
  * @mixins OO.ui.IndicatorElement
  *
  * @constructor
- * @param {Mixed} data Option data
  * @param {Object} [config] Configuration options
  */
-OO.ui.DecoratedOptionWidget = function OoUiDecoratedOptionWidget( data, config ) {
+OO.ui.DecoratedOptionWidget = function OoUiDecoratedOptionWidget( config ) {
        // Parent constructor
-       OO.ui.DecoratedOptionWidget.super.call( this, data, config );
+       OO.ui.DecoratedOptionWidget.super.call( this, config );
 
        // Mixin constructors
        OO.ui.IconElement.call( this, config );
@@ -10183,12 +10343,11 @@ OO.mixinClass( OO.ui.OptionWidget, OO.ui.IndicatorElement );
  * @mixins OO.ui.ButtonElement
  *
  * @constructor
- * @param {Mixed} data Option data
  * @param {Object} [config] Configuration options
  */
-OO.ui.ButtonOptionWidget = function OoUiButtonOptionWidget( data, config ) {
+OO.ui.ButtonOptionWidget = function OoUiButtonOptionWidget( config ) {
        // Parent constructor
-       OO.ui.ButtonOptionWidget.super.call( this, data, config );
+       OO.ui.ButtonOptionWidget.super.call( this, config );
 
        // Mixin constructors
        OO.ui.ButtonElement.call( this, config );
@@ -10225,78 +10384,123 @@ OO.ui.ButtonOptionWidget.prototype.setSelected = function ( state ) {
 };
 
 /**
- * Item of an OO.ui.MenuWidget.
+ * Option widget that looks like a radio button.
+ *
+ * Use together with OO.ui.RadioSelectWidget.
+ *
+ * @class
+ * @extends OO.ui.OptionWidget
+ * @mixins OO.ui.ButtonElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.RadioOptionWidget = function OoUiRadioOptionWidget( config ) {
+       // Parent constructor
+       OO.ui.RadioOptionWidget.super.call( this, config );
+
+       // Properties
+       this.radio = new OO.ui.RadioInputWidget( { value: config.data } );
+
+       // Initialization
+       this.$element
+               .addClass( 'oo-ui-radioOptionWidget' )
+               .prepend( this.radio.$element );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.RadioOptionWidget, OO.ui.OptionWidget );
+
+/* Static Properties */
+
+OO.ui.RadioOptionWidget.static.highlightable = false;
+
+OO.ui.RadioOptionWidget.static.pressable = false;
+
+/* Methods */
+
+/**
+ * @inheritdoc
+ */
+OO.ui.RadioOptionWidget.prototype.setSelected = function ( state ) {
+       OO.ui.RadioOptionWidget.super.prototype.setSelected.call( this, state );
+
+       this.radio.setSelected( state );
+
+       return this;
+};
+
+/**
+ * Item of an OO.ui.MenuSelectWidget.
  *
  * @class
  * @extends OO.ui.DecoratedOptionWidget
  *
  * @constructor
- * @param {Mixed} data Item data
  * @param {Object} [config] Configuration options
  */
-OO.ui.MenuItemWidget = function OoUiMenuItemWidget( data, config ) {
+OO.ui.MenuOptionWidget = function OoUiMenuOptionWidget( config ) {
        // Configuration initialization
        config = $.extend( { icon: 'check' }, config );
 
        // Parent constructor
-       OO.ui.MenuItemWidget.super.call( this, data, config );
+       OO.ui.MenuOptionWidget.super.call( this, config );
 
        // Initialization
        this.$element
                .attr( 'role', 'menuitem' )
-               .addClass( 'oo-ui-menuItemWidget' );
+               .addClass( 'oo-ui-menuOptionWidget' );
 };
 
 /* Setup */
 
-OO.inheritClass( OO.ui.MenuItemWidget, OO.ui.DecoratedOptionWidget );
+OO.inheritClass( OO.ui.MenuOptionWidget, OO.ui.DecoratedOptionWidget );
 
 /**
- * Section to group one or more items in a OO.ui.MenuWidget.
+ * Section to group one or more items in a OO.ui.MenuSelectWidget.
  *
  * @class
  * @extends OO.ui.DecoratedOptionWidget
  *
  * @constructor
- * @param {Mixed} data Item data
  * @param {Object} [config] Configuration options
  */
-OO.ui.MenuSectionItemWidget = function OoUiMenuSectionItemWidget( data, config ) {
+OO.ui.MenuSectionOptionWidget = function OoUiMenuSectionOptionWidget( config ) {
        // Parent constructor
-       OO.ui.MenuSectionItemWidget.super.call( this, data, config );
+       OO.ui.MenuSectionOptionWidget.super.call( this, config );
 
        // Initialization
-       this.$element.addClass( 'oo-ui-menuSectionItemWidget' );
+       this.$element.addClass( 'oo-ui-menuSectionOptionWidget' );
 };
 
 /* Setup */
 
-OO.inheritClass( OO.ui.MenuSectionItemWidget, OO.ui.DecoratedOptionWidget );
+OO.inheritClass( OO.ui.MenuSectionOptionWidget, OO.ui.DecoratedOptionWidget );
 
 /* Static Properties */
 
-OO.ui.MenuSectionItemWidget.static.selectable = false;
+OO.ui.MenuSectionOptionWidget.static.selectable = false;
 
-OO.ui.MenuSectionItemWidget.static.highlightable = false;
+OO.ui.MenuSectionOptionWidget.static.highlightable = false;
 
 /**
- * Items for an OO.ui.OutlineWidget.
+ * Items for an OO.ui.OutlineSelectWidget.
  *
  * @class
  * @extends OO.ui.DecoratedOptionWidget
  *
  * @constructor
- * @param {Mixed} data Item data
  * @param {Object} [config] Configuration options
  * @cfg {number} [level] Indentation level
  * @cfg {boolean} [movable] Allow modification from outline controls
  */
-OO.ui.OutlineItemWidget = function OoUiOutlineItemWidget( data, config ) {
+OO.ui.OutlineOptionWidget = function OoUiOutlineOptionWidget( config ) {
        // Configuration initialization
        config = config || {};
 
        // Parent constructor
-       OO.ui.OutlineItemWidget.super.call( this, data, config );
+       OO.ui.OutlineOptionWidget.super.call( this, config );
 
        // Properties
        this.level = 0;
@@ -10304,45 +10508,45 @@ OO.ui.OutlineItemWidget = function OoUiOutlineItemWidget( data, config ) {
        this.removable = !!config.removable;
 
        // Initialization
-       this.$element.addClass( 'oo-ui-outlineItemWidget' );
+       this.$element.addClass( 'oo-ui-outlineOptionWidget' );
        this.setLevel( config.level );
 };
 
 /* Setup */
 
-OO.inheritClass( OO.ui.OutlineItemWidget, OO.ui.DecoratedOptionWidget );
+OO.inheritClass( OO.ui.OutlineOptionWidget, OO.ui.DecoratedOptionWidget );
 
 /* Static Properties */
 
-OO.ui.OutlineItemWidget.static.highlightable = false;
+OO.ui.OutlineOptionWidget.static.highlightable = false;
 
-OO.ui.OutlineItemWidget.static.scrollIntoViewOnSelect = true;
+OO.ui.OutlineOptionWidget.static.scrollIntoViewOnSelect = true;
 
-OO.ui.OutlineItemWidget.static.levelClass = 'oo-ui-outlineItemWidget-level-';
+OO.ui.OutlineOptionWidget.static.levelClass = 'oo-ui-outlineOptionWidget-level-';
 
-OO.ui.OutlineItemWidget.static.levels = 3;
+OO.ui.OutlineOptionWidget.static.levels = 3;
 
 /* Methods */
 
 /**
  * Check if item is movable.
  *
- * Movablilty is used by outline controls.
+ * Movability is used by outline controls.
  *
  * @return {boolean} Item is movable
  */
-OO.ui.OutlineItemWidget.prototype.isMovable = function () {
+OO.ui.OutlineOptionWidget.prototype.isMovable = function () {
        return this.movable;
 };
 
 /**
  * Check if item is removable.
  *
- * Removablilty is used by outline controls.
+ * Removability is used by outline controls.
  *
  * @return {boolean} Item is removable
  */
-OO.ui.OutlineItemWidget.prototype.isRemovable = function () {
+OO.ui.OutlineOptionWidget.prototype.isRemovable = function () {
        return this.removable;
 };
 
@@ -10351,19 +10555,19 @@ OO.ui.OutlineItemWidget.prototype.isRemovable = function () {
  *
  * @return {number} Indentation level
  */
-OO.ui.OutlineItemWidget.prototype.getLevel = function () {
+OO.ui.OutlineOptionWidget.prototype.getLevel = function () {
        return this.level;
 };
 
 /**
  * Set movability.
  *
- * Movablilty is used by outline controls.
+ * Movability is used by outline controls.
  *
  * @param {boolean} movable Item is movable
  * @chainable
  */
-OO.ui.OutlineItemWidget.prototype.setMovable = function ( movable ) {
+OO.ui.OutlineOptionWidget.prototype.setMovable = function ( movable ) {
        this.movable = !!movable;
        this.updateThemeClasses();
        return this;
@@ -10372,12 +10576,12 @@ OO.ui.OutlineItemWidget.prototype.setMovable = function ( movable ) {
 /**
  * Set removability.
  *
- * Removablilty is used by outline controls.
+ * Removability is used by outline controls.
  *
  * @param {boolean} movable Item is removable
  * @chainable
  */
-OO.ui.OutlineItemWidget.prototype.setRemovable = function ( removable ) {
+OO.ui.OutlineOptionWidget.prototype.setRemovable = function ( removable ) {
        this.removable = !!removable;
        this.updateThemeClasses();
        return this;
@@ -10389,7 +10593,7 @@ OO.ui.OutlineItemWidget.prototype.setRemovable = function ( removable ) {
  * @param {number} [level=0] Indentation level, in the range of [0,#maxLevel]
  * @chainable
  */
-OO.ui.OutlineItemWidget.prototype.setLevel = function ( level ) {
+OO.ui.OutlineOptionWidget.prototype.setLevel = function ( level ) {
        var levels = this.constructor.static.levels,
                levelClass = this.constructor.static.levelClass,
                i = levels;
@@ -10699,7 +10903,7 @@ OO.ui.PopupWidget.prototype.updateDimensions = function ( transition ) {
  *
  * @constructor
  * @param {Object} [config] Configuration options
- * @cfg {number} [progress=0] Initial progress
+ * @cfg {number|boolean} [progress=false] Initial progress percent or false for indeterminate
  */
 OO.ui.ProgressBarWidget = function OoUiProgressBarWidget( config ) {
        // Configuration initialization
@@ -10713,7 +10917,7 @@ OO.ui.ProgressBarWidget = function OoUiProgressBarWidget( config ) {
        this.progress = null;
 
        // Initialization
-       this.setProgress( config.progress || 0 );
+       this.setProgress( config.progress !== undefined ? config.progress : false );
        this.$bar.addClass( 'oo-ui-progressBarWidget-bar');
        this.$element
                .attr( {
@@ -10747,13 +10951,19 @@ OO.ui.ProgressBarWidget.prototype.getProgress = function () {
 /**
  * Set progress percent
  *
- * @param {number} progress Progress percent
+ * @param {number|boolean} progress Progress percent or false for indeterminate
  */
 OO.ui.ProgressBarWidget.prototype.setProgress = function ( progress ) {
        this.progress = progress;
 
-       this.$bar.css( 'width', this.progress + '%' );
-       this.$element.attr( 'aria-valuenow', this.progress );
+       if ( progress !== false ) {
+               this.$bar.css( 'width', this.progress + '%' );
+               this.$element.attr( 'aria-valuenow', this.progress );
+       } else {
+               this.$bar.css( 'width', '' );
+               this.$element.removeAttr( 'aria-valuenow' );
+       }
+       this.$element.toggleClass( 'oo-ui-progressBarWidget-indeterminate', !progress );
 };
 
 /**
@@ -10771,7 +10981,7 @@ OO.ui.ProgressBarWidget.prototype.setProgress = function ( progress ) {
  * @cfg {string} [value] Initial query value
  */
 OO.ui.SearchWidget = function OoUiSearchWidget( config ) {
-       // Configuration intialization
+       // Configuration initialization
        config = config || {};
 
        // Parent constructor
@@ -10914,8 +11124,8 @@ OO.ui.SearchWidget.prototype.getResults = function () {
 /**
  * Generic selection of options.
  *
- * Items can contain any rendering, and are uniquely identified by a hash of their data. Any widget
- * that provides options, from which the user must choose one, should be built on this class.
+ * Items can contain any rendering. Any widget that provides options, from which the user must
+ * choose one, should be built on this class.
  *
  * Use together with OO.ui.OptionWidget.
  *
@@ -10940,7 +11150,6 @@ OO.ui.SelectWidget = function OoUiSelectWidget( config ) {
        // Properties
        this.pressed = false;
        this.selecting = null;
-       this.hashes = {};
        this.onMouseUpHandler = this.onMouseUp.bind( this );
        this.onMouseMoveHandler = this.onMouseMove.bind( this );
 
@@ -11162,22 +11371,6 @@ OO.ui.SelectWidget.prototype.getHighlightedItem = function () {
        return null;
 };
 
-/**
- * Get an existing item with equivilant data.
- *
- * @param {Object} data Item data to search for
- * @return {OO.ui.OptionWidget|null} Item with equivilent value, `null` if none exists
- */
-OO.ui.SelectWidget.prototype.getItemFromData = function ( data ) {
-       var hash = OO.getHash( data );
-
-       if ( Object.prototype.hasOwnProperty.call( this.hashes, hash ) ) {
-               return this.hashes[hash];
-       }
-
-       return null;
-};
-
 /**
  * Toggle pressed state.
  *
@@ -11293,7 +11486,7 @@ OO.ui.SelectWidget.prototype.chooseItem = function ( item ) {
  * Get an item relative to another one.
  *
  * @param {OO.ui.OptionWidget} item Item to start at
- * @param {number} direction Direction to move in
+ * @param {number} direction Direction to move in, -1 to look backward, 1 to move forward
  * @return {OO.ui.OptionWidget|null} Item at position, `null` if there are no items in the menu
  */
 OO.ui.SelectWidget.prototype.getRelativeSelectableItem = function ( item, direction ) {
@@ -11325,7 +11518,7 @@ OO.ui.SelectWidget.prototype.getRelativeSelectableItem = function ( item, direct
 /**
  * Get the next selectable item.
  *
- * @return {OO.ui.OptionWidget|null} Item, `null` if ther aren't any selectable items
+ * @return {OO.ui.OptionWidget|null} Item, `null` if there aren't any selectable items
  */
 OO.ui.SelectWidget.prototype.getFirstSelectableItem = function () {
        var i, len, item;
@@ -11343,31 +11536,12 @@ OO.ui.SelectWidget.prototype.getFirstSelectableItem = function () {
 /**
  * Add items.
  *
- * When items are added with the same values as existing items, the existing items will be
- * automatically removed before the new items are added.
- *
  * @param {OO.ui.OptionWidget[]} items Items to add
  * @param {number} [index] Index to insert items after
  * @fires add
  * @chainable
  */
 OO.ui.SelectWidget.prototype.addItems = function ( items, index ) {
-       var i, len, item, hash,
-               remove = [];
-
-       for ( i = 0, len = items.length; i < len; i++ ) {
-               item = items[i];
-               hash = OO.getHash( item.getData() );
-               if ( Object.prototype.hasOwnProperty.call( this.hashes, hash ) ) {
-                       // Remove item with same value
-                       remove.push( this.hashes[hash] );
-               }
-               this.hashes[hash] = item;
-       }
-       if ( remove.length ) {
-               this.removeItems( remove );
-       }
-
        // Mixin method
        OO.ui.GroupWidget.prototype.addItems.call( this, items, index );
 
@@ -11387,15 +11561,11 @@ OO.ui.SelectWidget.prototype.addItems = function ( items, index ) {
  * @chainable
  */
 OO.ui.SelectWidget.prototype.removeItems = function ( items ) {
-       var i, len, item, hash;
+       var i, len, item;
 
+       // Deselect items being removed
        for ( i = 0, len = items.length; i < len; i++ ) {
                item = items[i];
-               hash = OO.getHash( item.getData() );
-               if ( Object.prototype.hasOwnProperty.call( this.hashes, hash ) ) {
-                       // Remove existing item
-                       delete this.hashes[hash];
-               }
                if ( item.isSelected() ) {
                        this.selectItem( null );
                }
@@ -11420,10 +11590,10 @@ OO.ui.SelectWidget.prototype.removeItems = function ( items ) {
 OO.ui.SelectWidget.prototype.clearItems = function () {
        var items = this.items.slice();
 
-       // Clear all items
-       this.hashes = {};
        // Mixin method
        OO.ui.GroupWidget.prototype.clearItems.call( this );
+
+       // Clear selection
        this.selectItem( null );
 
        this.emit( 'remove', items );
@@ -11454,13 +11624,36 @@ OO.ui.ButtonSelectWidget = function OoUiButtonSelectWidget( config ) {
 
 OO.inheritClass( OO.ui.ButtonSelectWidget, OO.ui.SelectWidget );
 
+/**
+ * Select widget containing radio button options.
+ *
+ * Use together with OO.ui.RadioOptionWidget.
+ *
+ * @class
+ * @extends OO.ui.SelectWidget
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.RadioSelectWidget = function OoUiRadioSelectWidget( config ) {
+       // Parent constructor
+       OO.ui.RadioSelectWidget.super.call( this, config );
+
+       // Initialization
+       this.$element.addClass( 'oo-ui-radioSelectWidget' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.RadioSelectWidget, OO.ui.SelectWidget );
+
 /**
  * Overlaid menu of options.
  *
  * Menus are clipped to the visible viewport. They do not provide a control for opening or closing
  * the menu.
  *
- * Use together with OO.ui.MenuItemWidget.
+ * Use together with OO.ui.MenuOptionWidget.
  *
  * @class
  * @extends OO.ui.SelectWidget
@@ -11472,12 +11665,12 @@ OO.inheritClass( OO.ui.ButtonSelectWidget, OO.ui.SelectWidget );
  * @cfg {OO.ui.Widget} [widget] Widget to bind mouse handlers to
  * @cfg {boolean} [autoHide=true] Hide the menu when the mouse is pressed outside the menu
  */
-OO.ui.MenuWidget = function OoUiMenuWidget( config ) {
+OO.ui.MenuSelectWidget = function OoUiMenuSelectWidget( config ) {
        // Configuration initialization
        config = config || {};
 
        // Parent constructor
-       OO.ui.MenuWidget.super.call( this, config );
+       OO.ui.MenuSelectWidget.super.call( this, config );
 
        // Mixin constructors
        OO.ui.ClippableElement.call( this, $.extend( {}, config, { $clippable: this.$group } ) );
@@ -11498,13 +11691,13 @@ OO.ui.MenuWidget = function OoUiMenuWidget( config ) {
        this.$element
                .hide()
                .attr( 'role', 'menu' )
-               .addClass( 'oo-ui-menuWidget' );
+               .addClass( 'oo-ui-menuSelectWidget' );
 };
 
 /* Setup */
 
-OO.inheritClass( OO.ui.MenuWidget, OO.ui.SelectWidget );
-OO.mixinClass( OO.ui.MenuWidget, OO.ui.ClippableElement );
+OO.inheritClass( OO.ui.MenuSelectWidget, OO.ui.SelectWidget );
+OO.mixinClass( OO.ui.MenuSelectWidget, OO.ui.ClippableElement );
 
 /* Methods */
 
@@ -11513,7 +11706,7 @@ OO.mixinClass( OO.ui.MenuWidget, OO.ui.ClippableElement );
  *
  * @param {jQuery.Event} e Key down event
  */
-OO.ui.MenuWidget.prototype.onDocumentMouseDown = function ( e ) {
+OO.ui.MenuSelectWidget.prototype.onDocumentMouseDown = function ( e ) {
        if (
                !OO.ui.contains( this.$element[0], e.target, true ) &&
                ( !this.$widget || !OO.ui.contains( this.$widget[0], e.target, true ) )
@@ -11527,7 +11720,7 @@ OO.ui.MenuWidget.prototype.onDocumentMouseDown = function ( e ) {
  *
  * @param {jQuery.Event} e Key down event
  */
-OO.ui.MenuWidget.prototype.onKeyDown = function ( e ) {
+OO.ui.MenuSelectWidget.prototype.onKeyDown = function ( e ) {
        var nextItem,
                handled = false,
                highlightItem = this.getHighlightedItem();
@@ -11574,7 +11767,7 @@ OO.ui.MenuWidget.prototype.onKeyDown = function ( e ) {
 /**
  * Bind key down listener.
  */
-OO.ui.MenuWidget.prototype.bindKeyDownListener = function () {
+OO.ui.MenuSelectWidget.prototype.bindKeyDownListener = function () {
        if ( this.$input ) {
                this.$input.on( 'keydown', this.onKeyDownHandler );
        } else {
@@ -11586,7 +11779,7 @@ OO.ui.MenuWidget.prototype.bindKeyDownListener = function () {
 /**
  * Unbind key down listener.
  */
-OO.ui.MenuWidget.prototype.unbindKeyDownListener = function () {
+OO.ui.MenuSelectWidget.prototype.unbindKeyDownListener = function () {
        if ( this.$input ) {
                this.$input.off( 'keydown' );
        } else {
@@ -11602,11 +11795,11 @@ OO.ui.MenuWidget.prototype.unbindKeyDownListener = function () {
  * @param {OO.ui.OptionWidget} item Item to choose
  * @chainable
  */
-OO.ui.MenuWidget.prototype.chooseItem = function ( item ) {
+OO.ui.MenuSelectWidget.prototype.chooseItem = function ( item ) {
        var widget = this;
 
        // Parent method
-       OO.ui.MenuWidget.super.prototype.chooseItem.call( this, item );
+       OO.ui.MenuSelectWidget.super.prototype.chooseItem.call( this, item );
 
        if ( item && !this.flashing ) {
                this.flashing = true;
@@ -11624,11 +11817,11 @@ OO.ui.MenuWidget.prototype.chooseItem = function ( item ) {
 /**
  * @inheritdoc
  */
-OO.ui.MenuWidget.prototype.addItems = function ( items, index ) {
+OO.ui.MenuSelectWidget.prototype.addItems = function ( items, index ) {
        var i, len, item;
 
        // Parent method
-       OO.ui.MenuWidget.super.prototype.addItems.call( this, items, index );
+       OO.ui.MenuSelectWidget.super.prototype.addItems.call( this, items, index );
 
        // Auto-initialize
        if ( !this.newItems ) {
@@ -11654,9 +11847,9 @@ OO.ui.MenuWidget.prototype.addItems = function ( items, index ) {
 /**
  * @inheritdoc
  */
-OO.ui.MenuWidget.prototype.removeItems = function ( items ) {
+OO.ui.MenuSelectWidget.prototype.removeItems = function ( items ) {
        // Parent method
-       OO.ui.MenuWidget.super.prototype.removeItems.call( this, items );
+       OO.ui.MenuSelectWidget.super.prototype.removeItems.call( this, items );
 
        // Reevaluate clipping
        this.clip();
@@ -11667,9 +11860,9 @@ OO.ui.MenuWidget.prototype.removeItems = function ( items ) {
 /**
  * @inheritdoc
  */
-OO.ui.MenuWidget.prototype.clearItems = function () {
+OO.ui.MenuSelectWidget.prototype.clearItems = function () {
        // Parent method
-       OO.ui.MenuWidget.super.prototype.clearItems.call( this );
+       OO.ui.MenuSelectWidget.super.prototype.clearItems.call( this );
 
        // Reevaluate clipping
        this.clip();
@@ -11680,7 +11873,7 @@ OO.ui.MenuWidget.prototype.clearItems = function () {
 /**
  * @inheritdoc
  */
-OO.ui.MenuWidget.prototype.toggle = function ( visible ) {
+OO.ui.MenuSelectWidget.prototype.toggle = function ( visible ) {
        visible = ( visible === undefined ? !this.visible : !!visible ) && !!this.items.length;
 
        var i, len,
@@ -11689,7 +11882,7 @@ OO.ui.MenuWidget.prototype.toggle = function ( visible ) {
                widgetDoc = this.$widget ? this.$widget[0].ownerDocument : null;
 
        // Parent method
-       OO.ui.MenuWidget.super.prototype.toggle.call( this, visible );
+       OO.ui.MenuSelectWidget.super.prototype.toggle.call( this, visible );
 
        if ( change ) {
                if ( visible ) {
@@ -11750,19 +11943,19 @@ OO.ui.MenuWidget.prototype.toggle = function ( visible ) {
  * menu is toggled or the window is resized.
  *
  * @class
- * @extends OO.ui.MenuWidget
+ * @extends OO.ui.MenuSelectWidget
  *
  * @constructor
  * @param {OO.ui.TextInputWidget} input Text input widget to provide menu for
  * @param {Object} [config] Configuration options
  * @cfg {jQuery} [$container=input.$element] Element to render menu under
  */
-OO.ui.TextInputMenuWidget = function OoUiTextInputMenuWidget( input, config ) {
-       // Configuration intialization
+OO.ui.TextInputMenuSelectWidget = function OoUiTextInputMenuSelectWidget( input, config ) {
+       // Configuration initialization
        config = config || {};
 
        // Parent constructor
-       OO.ui.TextInputMenuWidget.super.call( this, config );
+       OO.ui.TextInputMenuSelectWidget.super.call( this, config );
 
        // Properties
        this.input = input;
@@ -11770,12 +11963,12 @@ OO.ui.TextInputMenuWidget = function OoUiTextInputMenuWidget( input, config ) {
        this.onWindowResizeHandler = this.onWindowResize.bind( this );
 
        // Initialization
-       this.$element.addClass( 'oo-ui-textInputMenuWidget' );
+       this.$element.addClass( 'oo-ui-textInputMenuSelectWidget' );
 };
 
 /* Setup */
 
-OO.inheritClass( OO.ui.TextInputMenuWidget, OO.ui.MenuWidget );
+OO.inheritClass( OO.ui.TextInputMenuSelectWidget, OO.ui.MenuSelectWidget );
 
 /* Methods */
 
@@ -11784,14 +11977,14 @@ OO.inheritClass( OO.ui.TextInputMenuWidget, OO.ui.MenuWidget );
  *
  * @param {jQuery.Event} e Window resize event
  */
-OO.ui.TextInputMenuWidget.prototype.onWindowResize = function () {
+OO.ui.TextInputMenuSelectWidget.prototype.onWindowResize = function () {
        this.position();
 };
 
 /**
  * @inheritdoc
  */
-OO.ui.TextInputMenuWidget.prototype.toggle = function ( visible ) {
+OO.ui.TextInputMenuSelectWidget.prototype.toggle = function ( visible ) {
        visible = visible === undefined ? !this.isVisible() : !!visible;
 
        var change = visible !== this.isVisible();
@@ -11804,7 +11997,7 @@ OO.ui.TextInputMenuWidget.prototype.toggle = function ( visible ) {
        }
 
        // Parent method
-       OO.ui.TextInputMenuWidget.super.prototype.toggle.call( this, visible );
+       OO.ui.TextInputMenuSelectWidget.super.prototype.toggle.call( this, visible );
 
        if ( change ) {
                if ( this.isVisible() ) {
@@ -11823,7 +12016,7 @@ OO.ui.TextInputMenuWidget.prototype.toggle = function ( visible ) {
  *
  * @chainable
  */
-OO.ui.TextInputMenuWidget.prototype.position = function () {
+OO.ui.TextInputMenuSelectWidget.prototype.position = function () {
        var $container = this.$container,
                pos = OO.ui.Element.getRelativePosition( $container, this.$element.offsetParent() );
 
@@ -11842,7 +12035,7 @@ OO.ui.TextInputMenuWidget.prototype.position = function () {
 /**
  * Structured list of items.
  *
- * Use with OO.ui.OutlineItemWidget.
+ * Use with OO.ui.OutlineOptionWidget.
  *
  * @class
  * @extends OO.ui.SelectWidget
@@ -11850,20 +12043,20 @@ OO.ui.TextInputMenuWidget.prototype.position = function () {
  * @constructor
  * @param {Object} [config] Configuration options
  */
-OO.ui.OutlineWidget = function OoUiOutlineWidget( config ) {
+OO.ui.OutlineSelectWidget = function OoUiOutlineSelectWidget( config ) {
        // Configuration initialization
        config = config || {};
 
        // Parent constructor
-       OO.ui.OutlineWidget.super.call( this, config );
+       OO.ui.OutlineSelectWidget.super.call( this, config );
 
        // Initialization
-       this.$element.addClass( 'oo-ui-outlineWidget' );
+       this.$element.addClass( 'oo-ui-outlineSelectWidget' );
 };
 
 /* Setup */
 
-OO.inheritClass( OO.ui.OutlineWidget, OO.ui.SelectWidget );
+OO.inheritClass( OO.ui.OutlineSelectWidget, OO.ui.SelectWidget );
 
 /**
  * Switch that slides on and off.
index f8a990c..398ea8b 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs v1.1.2 optimised for jQuery
+ * OOjs v1.1.3 optimised for jQuery
  * https://www.mediawiki.org/wiki/OOjs
  *
  * Copyright 2011-2014 OOjs Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2014-11-06T17:42:36Z
+ * Date: 2014-11-17T19:17:29Z
  */
 ( function ( global ) {
 
@@ -20,6 +20,7 @@ var
         * @singleton
         */
        oo = {},
+       // Optimisation: Local reference to Object.prototype.hasOwnProperty
        hasOwn = oo.hasOwnProperty,
        toString = oo.toString;
 
@@ -158,6 +159,64 @@ oo.mixinClass = function ( targetFn, originFn ) {
 
 /* Object Methods */
 
+/**
+ * Get a deeply nested property of an object using variadic arguments, protecting against
+ * undefined property errors.
+ *
+ * `quux = oo.getProp( obj, 'foo', 'bar', 'baz' );` is equivalent to `quux = obj.foo.bar.baz;`
+ * except that the former protects against JS errors if one of the intermediate properties
+ * is undefined. Instead of throwing an error, this function will return undefined in
+ * that case.
+ *
+ * @param {Object} obj
+ * @param {Mixed...} [keys]
+ * @returns obj[arguments[1]][arguments[2]].... or undefined
+ */
+oo.getProp = function ( obj ) {
+       var i,
+               retval = obj;
+       for ( i = 1; i < arguments.length; i++ ) {
+               if ( retval === undefined || retval === null ) {
+                       // Trying to access a property of undefined or null causes an error
+                       return undefined;
+               }
+               retval = retval[arguments[i]];
+       }
+       return retval;
+};
+
+/**
+ * Set a deeply nested property of an object using variadic arguments, protecting against
+ * undefined property errors.
+ *
+ * `oo.setProp( obj, 'foo', 'bar', 'baz' );` is equivalent to `obj.foo.bar = baz;` except that
+ * the former protects against JS errors if one of the intermediate properties is
+ * undefined. Instead of throwing an error, undefined intermediate properties will be
+ * initialized to an empty object. If an intermediate property is not an object, or if obj itself
+ * is not an object, this function will silently abort.
+ *
+ * @param {Object} obj
+ * @param {Mixed...} [keys]
+ * @param {Mixed} [value]
+ */
+oo.setProp = function ( obj ) {
+       var i,
+               prop = obj;
+       if ( Object( obj ) !== obj ) {
+               return;
+       }
+       for ( i = 1; i < arguments.length - 2; i++ ) {
+               if ( prop[arguments[i]] === undefined ) {
+                       prop[arguments[i]] = {};
+               }
+               if ( Object( prop[arguments[i]] ) !== prop[arguments[i]] ) {
+                       return;
+               }
+               prop = prop[arguments[i]];
+       }
+       prop[arguments[arguments.length - 2]] = arguments[arguments.length - 1];
+};
+
 /**
  * Create a new object that is an instance of the same
  * constructor as the input, inherits from the same object
@@ -228,7 +287,8 @@ oo.getObjectValues = function ( obj ) {
  *
  * @param {Object|undefined|null} a First object to compare
  * @param {Object|undefined|null} b Second object to compare
- * @param {boolean} [asymmetrical] Whether to check only that b contains values from a
+ * @param {boolean} [asymmetrical] Whether to check only that a's values are equal to b's
+ *  (i.e. a is a subset of b)
  * @return {boolean} If the objects contain the same values as each other
  */
 oo.compare = function ( a, b, asymmetrical ) {
@@ -242,9 +302,11 @@ oo.compare = function ( a, b, asymmetrical ) {
        b = b || {};
 
        for ( k in a ) {
-               if ( !hasOwn.call( a, k ) ) {
-                       // Support es3-shim: Without this filter, comparing [] to {} will be false in ES3
+               if ( !hasOwn.call( a, k ) || a[k] === undefined ) {
+                       // Support es3-shim: Without the hasOwn filter, comparing [] to {} will be false in ES3
                        // because the shimmed "forEach" is enumerable and shows up in Array but not Object.
+                       // Also ignore undefined values, because there is no conceptual difference between
+                       // a key that is absent and a key that is present but whose value is undefined.
                        continue;
                }
 
index ea2c5f9..f3f2655 100644 (file)
  *         to sortable tr elements in the thead on a descending sort. Default
  *         value: "headerSortDown"
  *
- * @option String sortInitialOrder ( optional ) A string of the inital sorting
- *         order can be asc or desc. Default value: "asc"
- *
  * @option String sortMultisortKey ( optional ) A string of the multi-column sort
  *         key. Default value: "shiftKey"
  *
- * @option Boolean sortLocaleCompare ( optional ) Boolean flag indicating whatever
- *         to use String.localeCampare method or not. Set to false.
- *
  * @option Boolean cancelSelection ( optional ) Boolean flag indicating if
  *         tablesorter should cancel selection of the table headers text.
  *         Default value: true
@@ -53,9 +47,6 @@
  *         { <Integer column index>: <String 'asc' or 'desc'> }
  *         Default value: []
  *
- * @option Boolean debug ( optional ) Boolean flag indicating if tablesorter
- *         should display debuging information usefull for development.
- *
  * @event sortEnd.tablesorter: Triggered as soon as any sorting has been applied.
  *
  * @type jQuery
                var i, j, $row, cols,
                        totalRows = ( table.tBodies[0] && table.tBodies[0].rows.length ) || 0,
                        totalCells = ( table.tBodies[0].rows[0] && table.tBodies[0].rows[0].cells.length ) || 0,
-                       parsers = table.config.parsers,
+                       config = $( table ).data( 'tablesorter' ).config,
+                       parsers = config.parsers,
                        cache = {
                                row: [],
                                normalized: []
 
                        // if this is a child row, add it to the last row's children and
                        // continue to the next row
-                       if ( $row.hasClass( table.config.cssChildRow ) ) {
+                       if ( $row.hasClass( config.cssChildRow ) ) {
                                cache.row[cache.row.length - 1] = cache.row[cache.row.length - 1].add( $row );
                                // go to the next for loop
                                continue;
        }
 
        function buildHeaders( table, msg ) {
-               var maxSeen = 0,
+               var config = $( table ).data( 'tablesorter' ).config,
+                       maxSeen = 0,
                        colspanOffset = 0,
                        columns,
                        i,
+                       $cell,
                        rowspan,
                        colspan,
                        headerCount,
 
                // as each header can span over multiple columns (using colspan=N),
                // we have to bidirectionally map headers to their columns and columns to their headers
-               table.headerToColumns = [];
-               table.columnToHeader = [];
-
                $tableHeaders.each( function ( headerIndex ) {
+                       $cell = $(this);
                        columns = [];
+
                        for ( i = 0; i < this.colSpan; i++ ) {
-                               table.columnToHeader[ colspanOffset + i ] = headerIndex;
+                               config.columnToHeader[ colspanOffset + i ] = headerIndex;
                                columns.push( colspanOffset + i );
                        }
 
-                       table.headerToColumns[ headerIndex ] = columns;
+                       config.headerToColumns[ headerIndex ] = columns;
                        colspanOffset += this.colSpan;
 
-                       this.headerIndex = headerIndex;
-                       this.order = 0;
-                       this.count = 0;
+                       $cell.data( {
+                               headerIndex: headerIndex,
+                               order: 0,
+                               count: 0
+                       });
 
-                       if ( $( this ).hasClass( table.config.unsortableClass ) ) {
-                               this.sortDisabled = true;
+                       if ( $cell.hasClass( config.unsortableClass ) ) {
+                               $cell.data( 'sortDisabled', true );
                        }
 
-                       if ( !this.sortDisabled ) {
-                               $( this )
-                                       .addClass( table.config.cssHeader )
+                       if ( !$cell.data( 'sortDisabled' ) ) {
+                               $cell
+                                       .addClass( config.cssHeader )
                                        .prop( 'tabIndex', 0 )
                                        .attr( {
                                                role: 'columnheader button',
                        }
 
                        // add cell to headerList
-                       table.config.headerList[headerIndex] = this;
+                       config.headerList[headerIndex] = this;
                } );
 
                return $tableHeaders;
                $.each( headerToColumns, function ( headerIndex, columns ) {
 
                        $.each( columns, function ( i, columnIndex ) {
-                               var header = $headers[headerIndex];
+                               var header = $headers[headerIndex],
+                                       $header = $( header );
 
                                if ( !isValueInArray( columnIndex, sortList ) ) {
                                        // Column shall not be sorted: Reset header count and order.
-                                       header.order = 0;
-                                       header.count = 0;
+                                       $header.data( {
+                                               order: 0,
+                                               count: 0
+                                       } );
                                } else {
                                        // Column shall be sorted: Apply designated count and order.
                                        $.each( sortList, function ( j, sortColumn ) {
                                                if ( sortColumn[0] === i ) {
-                                                       header.order = sortColumn[1];
-                                                       header.count = sortColumn[1] + 1;
+                                                       $header.data( {
+                                                               order: sortColumn[1],
+                                                               count: sortColumn[1] + 1
+                                                       } );
                                                        return false;
                                                }
                                        } );
         */
        function explodeRowspans( $table ) {
                var spanningRealCellIndex, rowSpan, colSpan,
-                       cell, i, $tds, $clone, $nextRows,
+                       cell, cellData, i, $tds, $clone, $nextRows,
                        rowspanCells = $table.find( '> tbody > tr > [rowspan]' ).get();
 
                // Short circuit
                                col = 0,
                                l = this.cells.length;
                        for ( i = 0; i < l; i++ ) {
-                               this.cells[i].realCellIndex = col;
-                               this.cells[i].realRowIndex = this.rowIndex;
+                               $( this.cells[i] ).data( 'tablesorter', {
+                                       realCellIndex: col,
+                                       realRowIndex: this.rowIndex
+                               } );
                                col += this.cells[i].colSpan;
                        }
                } );
                // Re-sort whenever a rowspanned cell's realCellIndex is changed, because it
                // might change the sort order.
                function resortCells() {
+                       var cellAData,
+                               cellBData,
+                               ret;
                        rowspanCells = rowspanCells.sort( function ( a, b ) {
-                               var ret = a.realCellIndex - b.realCellIndex;
+                               cellAData = $.data( a, 'tablesorter' );
+                               cellBData = $.data( b, 'tablesorter' );
+                               ret = cellAData.realCellIndex - cellBData.realCellIndex;
                                if ( !ret ) {
-                                       ret = a.realRowIndex - b.realRowIndex;
+                                       ret = cellAData.realRowIndex - cellBData.realRowIndex;
                                }
                                return ret;
                        } );
                        $.each( rowspanCells, function () {
-                               this.needResort = false;
+                               $.data( this, 'tablesorter' ).needResort = false;
                        } );
                }
                resortCells();
 
                function filterfunc() {
-                       return this.realCellIndex >= spanningRealCellIndex;
+                       return $.data( this, 'tablesorter' ).realCellIndex >= spanningRealCellIndex;
                }
 
                function fixTdCellIndex() {
-                       this.realCellIndex += colSpan;
+                       $.data( this, 'tablesorter' ).realCellIndex += colSpan;
                        if ( this.rowSpan > 1 ) {
-                               this.needResort = true;
+                               $.data( this, 'tablesorter' ).needResort = true;
                        }
                }
 
                while ( rowspanCells.length ) {
-                       if ( rowspanCells[0].needResort ) {
+                       if ( $.data( rowspanCells[0], 'tablesorter' ).needResort ) {
                                resortCells();
                        }
 
                        cell = rowspanCells.shift();
+                       cellData = $.data( cell, 'tablesorter' );
                        rowSpan = cell.rowSpan;
                        colSpan = cell.colSpan;
-                       spanningRealCellIndex = cell.realCellIndex;
+                       spanningRealCellIndex = cellData.realCellIndex;
                        cell.rowSpan = 1;
                        $nextRows = $( cell ).parent().nextAll();
                        for ( i = 0; i < rowSpan - 1; i++ ) {
                                $tds = $( $nextRows[i].cells ).filter( filterfunc );
                                $clone = $( cell ).clone();
-                               $clone[0].realCellIndex = spanningRealCellIndex;
+                               $clone.data( 'tablesorter', {
+                                       realCellIndex: spanningRealCellIndex,
+                                       realRowIndex: cellData.realRowIndex + i,
+                                       needResort: true
+                               } );
                                if ( $tds.length ) {
                                        $tds.each( fixTdCellIndex );
                                        $tds.first().before( $clone );
                                cssAsc: 'headerSortUp',
                                cssDesc: 'headerSortDown',
                                cssChildRow: 'expand-child',
-                               sortInitialOrder: 'asc',
                                sortMultiSortKey: 'shiftKey',
-                               sortLocaleCompare: false,
                                unsortableClass: 'unsortable',
                                parsers: {},
-                               widgets: [],
-                               headers: {},
                                cancelSelection: true,
                                sortList: [],
                                headerList: [],
-                               selectorHeaders: 'thead tr:eq(0) th',
-                               debug: false
+                               headerToColumns: [],
+                               columnToHeader: []
                        },
 
                        dateRegex: [],
                                        }
                                        $table.addClass( 'jquery-tablesorter' );
 
-                                       // FIXME config should probably not be stored in the plain table node
-                                       // New config object.
-                                       table.config = {};
-
-                                       // Merge and extend.
-                                       config = $.extend( table.config, $.tablesorter.defaultOptions, settings );
+                                       // Merge and extend
+                                       config = $.extend( {}, $.tablesorter.defaultOptions, settings );
 
                                        // Save the settings where they read
                                        $.data( table, 'tablesorter', { config: config } );
 
-                                       // Get the CSS class names, could be done else where.
+                                       // Get the CSS class names, could be done elsewhere
                                        sortCSS = [ config.cssDesc, config.cssAsc ];
                                        sortMsg = [ mw.msg( 'sort-descending' ), mw.msg( 'sort-ascending' ) ];
 
 
                                                explodeRowspans( $table );
 
-                                               // try to auto detect column type, and store in tables config
-                                               table.config.parsers = buildParserCache( table, $headers );
+                                               // Try to auto detect column type, and store in tables config
+                                               config.parsers = buildParserCache( table, $headers );
                                        }
 
                                        // Apply event handling to headers
                                        // this is too big, perhaps break it out?
-                                       $headers.not( '.' + table.config.unsortableClass ).on( 'keypress click', function ( e ) {
-                                               var cell, columns, newSortList, i,
+                                       $headers.not( '.' + config.unsortableClass ).on( 'keypress click', function ( e ) {
+                                               var cell, $cell, columns, newSortList, i,
                                                        totalRows,
                                                        j, s, o;
 
 
                                                totalRows = ( $table[0].tBodies[0] && $table[0].tBodies[0].rows.length ) || 0;
                                                if ( !table.sortDisabled && totalRows > 0 ) {
+                                                       cell = this;
+                                                       $cell = $( cell );
+
                                                        // Get current column sort order
-                                                       this.order = this.count % 2;
-                                                       this.count++;
+                                                       $cell.data( {
+                                                               order: $cell.data( 'count' ) % 2,
+                                                               count: $cell.data( 'count' ) + 1
+                                                       } );
 
                                                        cell = this;
                                                        // Get current column index
-                                                       columns = table.headerToColumns[ this.headerIndex ];
+                                                       columns = config.headerToColumns[ $cell.data( 'headerIndex' ) ];
                                                        newSortList = $.map( columns, function ( c ) {
                                                                // jQuery "helpfully" flattens the arrays...
-                                                               return [[c, cell.order]];
+                                                               return [[c, $cell.data( 'order' )]];
                                                        } );
                                                        // Index of first column belonging to this header
                                                        i = columns[0];
                                                                                s = config.sortList[j];
                                                                                o = config.headerList[s[0]];
                                                                                if ( isValueInArray( s[0], newSortList ) ) {
-                                                                                       o.count = s[1];
-                                                                                       o.count++;
-                                                                                       s[1] = o.count % 2;
+                                                                                       $(o).data( 'count', s[1] + 1 );
+                                                                                       s[1] = $( o ).data( 'count' ) % 2;
                                                                                }
                                                                        }
                                                                } else {
                                                        }
 
                                                        // Reset order/counts of cells not affected by sorting
-                                                       setHeadersOrder( $headers, config.sortList, table.headerToColumns );
+                                                       setHeadersOrder( $headers, config.sortList, config.headerToColumns );
 
                                                        // Set CSS for headers
-                                                       setHeadersCss( $table[0], $headers, config.sortList, sortCSS, sortMsg, table.columnToHeader );
+                                                       setHeadersCss( $table[0], $headers, config.sortList, sortCSS, sortMsg, config.columnToHeader );
                                                        appendToTable(
                                                                $table[0], multisort( $table[0], config.sortList, cache )
                                                        );
 
                                                // Set each column's sort count to be able to determine the correct sort
                                                // order when clicking on a header cell the next time
-                                               setHeadersOrder( $headers, sortList, table.headerToColumns );
+                                               setHeadersOrder( $headers, sortList, config.headerToColumns );
 
                                                // re-build the cache for the tbody cells
                                                cache = buildCache( table );
 
                                                // set css for headers
-                                               setHeadersCss( table, $headers, sortList, sortCSS, sortMsg, table.columnToHeader );
+                                               setHeadersCss( table, $headers, sortList, sortCSS, sortMsg, config.columnToHeader );
 
                                                // sort the table and append it to the dom
                                                appendToTable( table, multisort( table, sortList, cache ) );
index 092a597..0887476 100644 (file)
@@ -1,6 +1,6 @@
-/*
-** Diff rendering
-*/
+/*!
+ * Diff rendering
+ */
 table.diff {
        border: none;
        border-spacing: 4px;
diff --git a/resources/src/mediawiki.action/mediawiki.action.history.diff.print.css b/resources/src/mediawiki.action/mediawiki.action.history.diff.print.css
new file mode 100644 (file)
index 0000000..76b5c9b
--- /dev/null
@@ -0,0 +1,16 @@
+/*!
+ * Diff rendering
+ */
+td.diff-context,
+td.diff-addedline .diffchange,
+td.diff-deletedline .diffchange {
+       background-color: transparent;
+}
+
+td.diff-addedline .diffchange {
+       text-decoration: underline;
+}
+
+td.diff-deletedline .diffchange {
+       text-decoration: line-through;
+}
index 3c22851..2be29f0 100644 (file)
@@ -8,7 +8,11 @@
                        if ( parseInt( mw.user.options.get( 'editondblclick' ), 10 ) ) {
                                e.preventDefault();
                                // Trigger native HTMLElement click instead of opening URL (bug 43052)
-                               $( '#ca-edit a' ).get( 0 ).click();
+                               var $a = $( '#ca-edit a' );
+                               // Not every page has an edit link (bug 57713)
+                               if ( $a.length ) {
+                                       $a.get( 0 ).click();
+                               }
                        }
                } );
        } );
index 9405719..a52ccb6 100644 (file)
@@ -323,44 +323,6 @@ div.gallerytext {
        word-wrap: break-word;
 }
 
-/**
- * Diff rendering
- */
-table.diff {
-       background: white;
-}
-
-td.diff-otitle {
-       background: #ffffff;
-}
-
-td.diff-ntitle {
-       background: #ffffff;
-}
-
-td.diff-addedline {
-       background: #ccffcc;
-       font-size: smaller;
-       border: solid 2px black;
-}
-
-td.diff-deletedline {
-       background: #ffffaa;
-       font-size: smaller;
-       border: dotted 2px black;
-}
-
-td.diff-context {
-       background: #eeeeee;
-       font-size: smaller;
-}
-
-.diffchange {
-       color: silver;
-       font-weight: bold;
-       text-decoration: underline;
-}
-
 /**
  * Table rendering
  * As on shared.css but with white background.
index 4a87b74..c84c884 100644 (file)
@@ -1,14 +1,12 @@
-/**
- * Common LESS mixin library for MediaWiki
- *
- * By default the folder containing this file is included in $wgResourceLoaderLESSImportPaths,
- * which makes this file importable by all less files via '@import "mediawiki.mixins";'.
- *
- * The mixins included below are considered a public interface for MediaWiki extensions.
- * The signatures of parametrized mixins should be kept as stable as possible.
- *
- * See <http://lesscss.org/#-mixins> for more information about how to write mixins.
- */
+// Common LESS mixin library for MediaWiki
+//
+// By default the folder containing this file is included in $wgResourceLoaderLESSImportPaths,
+// which makes this file importable by all less files via '@import "mediawiki.mixins";'.
+//
+// The mixins included below are considered a public interface for MediaWiki extensions.
+// The signatures of parametrized mixins should be kept as stable as possible.
+//
+// See <http://lesscss.org/#-mixins> for more information about how to write mixins.
 
 .background-image(@url) {
        background-image: e('/* @embed */') url(@url);
        background-image: linear-gradient( @startColor @startPos, @endColor @endPos ); // Standard
 }
 
-/*
- * SVG support using a transparent gradient to guarantee cross-browser
- * compatibility (browsers able to understand gradient syntax support also SVG).
- * http://pauginer.tumblr.com/post/36614680636/invisible-gradient-technique
- *
- * We use gzip compression, which means that it is okay to embed twice.
- *
- * We do not embed the fallback image on the assumption that the gain for old browsers
- * is not worth the harm done to modern ones.
- */
+// SVG support using a transparent gradient to guarantee cross-browser
+// compatibility (browsers able to understand gradient syntax support also SVG).
+// http://pauginer.tumblr.com/post/36614680636/invisible-gradient-technique
+//
+// We use gzip compression, which means that it is okay to embed twice.
+//
+// We do not embed the fallback image on the assumption that the gain for old browsers
+// is not worth the harm done to modern ones.
 .background-image-svg(@svg, @fallback) {
        background-image: url(@fallback);
        background-image: -webkit-linear-gradient(transparent, transparent), e('/* @embed */') url(@svg);
index ec9888f..1f21b41 100644 (file)
        color: @colorButtonText;
        border: 1px solid @colorGray12;
 
+       &:hover,
+       &:active {
+               // make sure that is isn't inheriting from a general rule
+               color: @colorButtonText;
+       }
+
        &:disabled {
                color: @colorDisabledText;
 
 .button-colors(@bgColor) when (lightness(@bgColor) < 70%) {
        color: #fff;
        // border of the same color as background so that light background and
-       // dark background buttons are the same height (only top and bottom to
-       // make box shadow on hover cover the corners too)
+       // dark background buttons are the same height and width
        border: 1px solid @bgColor;
-       border-left: none;
-       border-right: none;
        text-shadow: 0 1px rgba(0, 0, 0, .1);
 
        &:disabled {
index 246cc81..36eb9d4 100644 (file)
@@ -7,7 +7,7 @@
                // it works only comparing to window.self or window.window (http://stackoverflow.com/q/4850978/319266)
                if ( window.top !== window.self ) {
                        // Un-trap us from framesets
-                       window.top.location = window.location;
+                       window.top.location.href = location.href;
                }
        }
 
index d3e8f29..3dd65fb 100644 (file)
@@ -25,7 +25,7 @@
                        // Bind onchange event handler and append to form
                        $html.append(
                                $( select ).change( function () {
-                                       window.location = QUnit.url( { useskin: $( this ).val() } );
+                                       location.href = QUnit.url( { useskin: $( this ).val() } );
                                } )
                        );
 
index 1f6429b..043d769 100644 (file)
@@ -57,7 +57,7 @@ jQuery( function ( $ ) {
                // therefore save and restore scrollTop to prevent jumping.
                scrollTop = $( window ).scrollTop();
                if ( mode !== 'noHash' ) {
-                       window.location.hash = '#mw-prefsection-' + name;
+                       location.hash = '#mw-prefsection-' + name;
                }
                $( window ).scrollTop( scrollTop );
 
@@ -127,7 +127,7 @@ jQuery( function ( $ ) {
 
        // If we've reloaded the page or followed an open-in-new-window,
        // make the selected tab visible.
-       hash = window.location.hash;
+       hash = location.hash;
        if ( hash.match( /^#mw-prefsection-[\w\-]+/ ) ) {
                switchPrefTab( hash.replace( '#mw-prefsection-', '' ) );
        }
@@ -142,7 +142,7 @@ jQuery( function ( $ ) {
                ( document.documentMode === undefined || document.documentMode >= 8 )
        ) {
                $( window ).on( 'hashchange', function () {
-                       var hash = window.location.hash;
+                       var hash = location.hash;
                        if ( hash.match( /^#mw-prefsection-[\w\-]+/ ) ) {
                                switchPrefTab( hash.replace( '#mw-prefsection-', '' ) );
                        } else if ( hash === '' ) {
index 913f901..b479020 100644 (file)
@@ -29,7 +29,7 @@
        vertical-align: middle;
 }
 
-@checkboxSize: 1.6em;
+@checkboxSize: 2em;
 
 // We use the not selector to cancel out styling on IE 8 and below
 .mw-ui-checkbox:not(#noop) {
@@ -38,6 +38,8 @@
        line-height: @checkboxSize;
 
        * {
+               // reset font sizes (see bug 72727)
+               font: inherit;
                vertical-align: middle;
        }
 
                margin-right: .4em;
 
                // the pseudo before element of the label after the checkbox now looks like a checkbox
-               & + label {
+               & + label::before {
+                       content: '';
                        cursor: pointer;
-
-                       &::before {
-                               content: '';
-                               position: absolute;
-                               left: 0;
-                               border-radius: @borderRadius;
-                               width: @checkboxSize;
-                               height: @checkboxSize;
-                               background-color: #fff;
-                               border: 1px solid grey;
-                       }
+                       position: absolute;
+                       left: 0;
+                       border-radius: @borderRadius;
+                       width: @checkboxSize;
+                       height: @checkboxSize;
+                       background-color: #fff;
+                       border: 1px solid @colorGray7;
+                       .box-sizing(border-box);
                }
 
                // when the input is checked, style the label pseudo before element that followed as a checked checkbox
-               &:checked {
-                       + label {
-                               &::before {
-                                       .background-image-svg('images/checked.svg', 'images/checked.png');
-                                       .background-size( @checkboxSize, @checkboxSize );
-                                       background-repeat: no-repeat;
-                                       background-position: center top;
-                               }
-                       }
+               &:checked + label::before {
+                       .background-image-svg('images/checked.svg', 'images/checked.png');
+                       .background-size( @checkboxSize - 0.2em, @checkboxSize - 0.2em );
+                       background-repeat: no-repeat;
+                       background-position: center center;
+                       background-origin: border-box;
+               }
+
+               &:active + label::before {
+                       background-color: @colorGray13;
+                       border-color: @colorGray13;
                }
 
-               @focusBottomBorderSize: 0.2em;
-               &:active,
-               &:focus {
-                       + label {
-                               &::before {
-                                       box-shadow: inset 0 -@focusBottomBorderSize 0 0 lightgrey;
-                               }
-                       }
+               &:focus + label::before {
+                       border-width: 2px;
                }
 
-               // disabled checked boxes have a gray background
-               &:disabled + label {
+               &:hover + label::before {
+                       border-bottom-width: 3px;
+               }
+
+               // disabled checkboxes have a gray background
+               &:disabled + label::before {
                        cursor: default;
+                       background-color: @colorGray14;
+                       border-color: @colorGray14;
+               }
 
-                       &::before {
-                               background-color: lightgrey;
-                       }
+               // disabled and checked checkboxes have a white circle
+               &:disabled:checked + label::before {
+                       .background-image-svg('images/checked_disabled.svg', 'images/checked_disabled.png');
                }
        }
 }
diff --git a/resources/src/mediawiki.ui/components/images/checked_disabled.png b/resources/src/mediawiki.ui/components/images/checked_disabled.png
new file mode 100644 (file)
index 0000000..7566a06
Binary files /dev/null and b/resources/src/mediawiki.ui/components/images/checked_disabled.png differ
diff --git a/resources/src/mediawiki.ui/components/images/checked_disabled.svg b/resources/src/mediawiki.ui/components/images/checked_disabled.svg
new file mode 100644 (file)
index 0000000..a7257f2
--- /dev/null
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"><path d="M4 12l5 5 11-12" stroke="#FFFFFF" stroke-width="3" fill="none"/></svg>
index c01dd36..9f3a77d 100644 (file)
@@ -89,7 +89,7 @@ textarea.mw-ui-input {
 //
 // Markup:
 // <input class="mw-ui-input mw-ui-input-inline">
-// <button class="mw-ui-button mw-ui-constructive">go</button>
+// <button class="mw-ui-button mw-ui-constructive">Submit</button>
 //
 // Styleguide 1.2.
 input[type="number"],
index 5566312..bb5ddfc 100644 (file)
                 * @param {Object|string} [uri] URI string, or an Object with appropriate properties (especially
                 *  another URI object to clone). Object must have non-blank `protocol`, `host`, and `path`
                 *  properties. If omitted (or set to `undefined`, `null` or empty string), then an object
-                *  will be created for the default `uri` of this constructor (`document.location` for
-                *  mw.Uri, other values for other instances -- see mw.UriRelative for details).
+                *  will be created for the default `uri` of this constructor (`location.href` for mw.Uri,
+                *  other values for other instances -- see mw.UriRelative for details).
                 * @param {Object|boolean} [options] Object with options, or (backwards compatibility) a boolean
                 *  for strictMode
                 * @param {boolean} [options.strictMode=false] Trigger strict mode parsing of the url.
                return Uri;
        };
 
-       // If we are running in a browser, inject the current document location (for relative URLs).
-       if ( document && document.location && document.location.href ) {
-               mw.Uri = mw.UriRelative( document.location.href );
-       }
+       // Default to the current browsing location (for relative URLs).
+       mw.Uri = mw.UriRelative( location.href );
 
 }( mediaWiki, jQuery ) );
index 4935984..bdff99f 100644 (file)
 
                        paneTriggerBitDiv( 'includes', 'PHP includes', this.data.includes.length );
 
-                       paneTriggerBitDiv( 'profile', 'Profile', this.data.profile.length );
-
                        gitInfo = '';
                        if ( this.data.gitRevision !== false ) {
                                gitInfo = '(' + this.data.gitRevision.slice( 0, 7 ) + ')';
                                querylist: this.buildQueryTable(),
                                debuglog: this.buildDebugLogTable(),
                                request: this.buildRequestPane(),
-                               includes: this.buildIncludesPane(),
-                               profile: this.buildProfilePane()
+                               includes: this.buildIncludesPane()
                        };
 
                        for ( id in panes ) {
                        }
 
                        return $table;
-               },
-
-               buildProfilePane: function () {
-                       return mw.Debug.profile.init();
                }
        };
 
diff --git a/resources/src/mediawiki/mediawiki.debug.profile.css b/resources/src/mediawiki/mediawiki.debug.profile.css
deleted file mode 100644 (file)
index ab27da9..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-.mw-debug-profile-tipsy .tipsy-inner {
-       /* undo max-width from vector on .tipsy-inner */
-       max-width: none;
-       /* needed for some browsers to provide space for the scrollbar without wrapping text */
-       min-width: 100%;
-       max-height: 150px;
-       overflow-y: auto;
-}
-
-.mw-debug-profile-underline {
-       stroke-width: 1;
-       stroke: #dfdfdf;
-}
-
-.mw-debug-profile-period {
-       fill: red;
-}
-
-/* connecting line between endpoints on long events */
-.mw-debug-profile-period line {
-       stroke: red;
-       stroke-width: 2;
-}
-
-.mw-debug-profile-tipsy,
-.mw-debug-profile-timeline text {
-       color: #444;
-       fill: #444;
-       /* using em's causes the two locations to have different sizes */
-       font-size: 12px;
-       font-family: sans-serif;
-}
-
-.mw-debug-profile-meta,
-.mw-debug-profile-timeline tspan {
-       /* using em's causes the two locations to have different sizes */
-       font-size: 10px;
-}
-
-.mw-debug-profile-no-data {
-       text-align: center;
-       padding-top: 5em;
-       font-weight: bold;
-       font-size: 1.2em;
-}
diff --git a/resources/src/mediawiki/mediawiki.debug.profile.js b/resources/src/mediawiki/mediawiki.debug.profile.js
deleted file mode 100644 (file)
index 04f7acd..0000000
+++ /dev/null
@@ -1,556 +0,0 @@
-/*!
- * JavaScript for the debug toolbar profiler, enabled through $wgDebugToolbar
- * and StartProfiler.php.
- *
- * @author Erik Bernhardson
- * @since 1.23
- */
-
-( function ( mw, $ ) {
-       'use strict';
-
-       /**
-        * @singleton
-        * @class mw.Debug.profile
-        */
-       var profile = mw.Debug.profile = {
-               /**
-                * Object containing data for the debug toolbar
-                *
-                * @property ProfileData
-                */
-               data: null,
-
-               /**
-                * @property DOMElement
-                */
-               container: null,
-
-               /**
-                * Initializes the profiling pane.
-                */
-               init: function ( data, width, mergeThresholdPx, dropThresholdPx ) {
-                       data = data || mw.config.get( 'debugInfo' ).profile;
-                       profile.width = width || $(window).width() - 20;
-                       // merge events from same pixel(some events are very granular)
-                       mergeThresholdPx = mergeThresholdPx || 2;
-                       // only drop events if requested
-                       dropThresholdPx = dropThresholdPx || 0;
-
-                       if (
-                               !Array.prototype.map ||
-                               !Array.prototype.reduce ||
-                               !Array.prototype.filter ||
-                               !document.createElementNS ||
-                               !document.createElementNS.bind
-                       ) {
-                               profile.container = profile.buildRequiresBrowserFeatures();
-                       } else if ( data.length === 0 ) {
-                               profile.container = profile.buildNoData();
-                       } else {
-                               // Initialize createSvgElement (now that we know we have
-                               // document.createElementNS and bind)
-                               this.createSvgElement = document.createElementNS.bind( document, 'http://www.w3.org/2000/svg' );
-
-                               // generate a flyout
-                               profile.data = new ProfileData( data, profile.width, mergeThresholdPx, dropThresholdPx );
-                               // draw it
-                               profile.container = profile.buildSvg( profile.container );
-                               profile.attachFlyout();
-                       }
-
-                       return profile.container;
-               },
-
-               buildRequiresBrowserFeatures: function () {
-                       return $( '<div>' )
-                               .text( 'Certain browser features, including parts of ECMAScript 5 and document.createElementNS, are required for the profile visualization.' )
-                               .get( 0 );
-               },
-
-               buildNoData: function () {
-                       return $( '<div>' ).addClass( 'mw-debug-profile-no-data' )
-                               .text( 'No events recorded, ensure profiling is enabled in StartProfiler.php.' )
-                               .get( 0 );
-               },
-
-               /**
-                * Creates DOM nodes appropriately namespaced for SVG.
-                * Initialized in init after checking support
-                *
-                * @param string tag to create
-                * @return DOMElement
-                */
-               createSvgElement: null,
-
-               /**
-                * @param DOMElement|undefined
-                */
-               buildSvg: function ( node ) {
-                       var container, group, i, g,
-                               timespan = profile.data.timespan,
-                               gapPerEvent = 38,
-                               space = 10.5,
-                               currentHeight = space,
-                               totalHeight = 0;
-
-                       profile.ratio = ( profile.width - space * 2 ) / ( timespan.end - timespan.start );
-                       totalHeight += gapPerEvent * profile.data.groups.length;
-
-                       if ( node ) {
-                               $( node ).empty();
-                       } else {
-                               node = profile.createSvgElement( 'svg' );
-                               node.setAttribute( 'version', '1.2' );
-                               node.setAttribute( 'baseProfile', 'tiny' );
-                       }
-                       node.style.height = totalHeight;
-                       node.style.width = profile.width;
-
-                       // use a container that can be transformed
-                       container = profile.createSvgElement( 'g' );
-                       node.appendChild( container );
-
-                       for ( i = 0; i < profile.data.groups.length; i++ ) {
-                               group = profile.data.groups[i];
-                               g = profile.buildTimeline( group );
-
-                               g.setAttribute( 'transform', 'translate( 0 ' + currentHeight + ' )' );
-                               container.appendChild( g );
-
-                               currentHeight += gapPerEvent;
-                       }
-
-                       return node;
-               },
-
-               /**
-                * @param Object group of periods to transform into graphics
-                */
-               buildTimeline: function ( group ) {
-                       var text, tspan, line, i,
-                               sum = group.timespan.sum,
-                               ms = ' ~ ' + ( sum < 1 ? sum.toFixed( 2 ) : sum.toFixed( 0 ) ) + ' ms',
-                               timeline = profile.createSvgElement( 'g' );
-
-                       timeline.setAttribute( 'class', 'mw-debug-profile-timeline' );
-
-                       // draw label
-                       text = profile.createSvgElement( 'text' );
-                       text.setAttribute( 'x', profile.xCoord( group.timespan.start ) );
-                       text.setAttribute( 'y', 0 );
-                       text.textContent = group.name;
-                       timeline.appendChild( text );
-
-                       // draw metadata
-                       tspan = profile.createSvgElement( 'tspan' );
-                       tspan.textContent = ms;
-                       text.appendChild( tspan );
-
-                       // draw timeline periods
-                       for ( i = 0; i < group.periods.length; i++ ) {
-                               timeline.appendChild( profile.buildPeriod( group.periods[i] ) );
-                       }
-
-                       // full-width line under each timeline
-                       line = profile.createSvgElement( 'line' );
-                       line.setAttribute( 'class', 'mw-debug-profile-underline' );
-                       line.setAttribute( 'x1', 0 );
-                       line.setAttribute( 'y1', 28 );
-                       line.setAttribute( 'x2', profile.width );
-                       line.setAttribute( 'y2', 28 );
-                       timeline.appendChild( line );
-
-                       return timeline;
-               },
-
-               /**
-                * @param Object period to transform into graphics
-                */
-               buildPeriod: function ( period ) {
-                       var node,
-                               head = profile.xCoord( period.start ),
-                               tail = profile.xCoord( period.end ),
-                               g = profile.createSvgElement( 'g' );
-
-                       g.setAttribute( 'class', 'mw-debug-profile-period' );
-                       $( g ).data( 'period', period );
-
-                       if ( head + 16 > tail ) {
-                               node = profile.createSvgElement( 'rect' );
-                               node.setAttribute( 'x', head );
-                               node.setAttribute( 'y', 8 );
-                               node.setAttribute( 'width', 2 );
-                               node.setAttribute( 'height', 9 );
-                               g.appendChild( node );
-
-                               node = profile.createSvgElement( 'rect' );
-                               node.setAttribute( 'x', head );
-                               node.setAttribute( 'y', 8 );
-                               node.setAttribute( 'width', ( period.end - period.start ) * profile.ratio || 2 );
-                               node.setAttribute( 'height', 6 );
-                               g.appendChild( node );
-                       } else {
-                               node = profile.createSvgElement( 'polygon' );
-                               node.setAttribute( 'points', pointList( [
-                                       [ head, 8 ],
-                                       [ head, 19 ],
-                                       [ head + 8, 8 ],
-                                       [ head, 8]
-                               ] ) );
-                               g.appendChild( node );
-
-                               node = profile.createSvgElement( 'polygon' );
-                               node.setAttribute( 'points', pointList( [
-                                       [ tail, 8 ],
-                                       [ tail, 19 ],
-                                       [ tail - 8, 8 ],
-                                       [ tail, 8 ]
-                               ] ) );
-                               g.appendChild( node );
-
-                               node = profile.createSvgElement( 'line' );
-                               node.setAttribute( 'x1', head );
-                               node.setAttribute( 'y1', 9 );
-                               node.setAttribute( 'x2', tail );
-                               node.setAttribute( 'y2', 9 );
-                               g.appendChild( node );
-                       }
-
-                       return g;
-               },
-
-               /**
-                * @param Object
-                */
-               buildFlyout: function ( period ) {
-                       var contained, sum, ms, mem, i,
-                               node = $( '<div>' );
-
-                       for ( i = 0; i < period.contained.length; i++ ) {
-                               contained = period.contained[i];
-                               sum = contained.end - contained.start;
-                               ms = '' + ( sum < 1 ? sum.toFixed( 2 ) : sum.toFixed( 0 ) ) + ' ms';
-                               mem = formatBytes( contained.memory );
-
-                               $( '<div>' ).text( contained.source.name )
-                                       .append( $( '<span>' ).text( ' ~ ' + ms + ' / ' + mem ).addClass( 'mw-debug-profile-meta' ) )
-                                       .appendTo( node );
-                       }
-
-                       return node;
-               },
-
-               /**
-                * Attach a hover flyout to all .mw-debug-profile-period groups.
-                */
-               attachFlyout: function () {
-                       // for some reason addClass and removeClass from jQuery
-                       // arn't working on svg elements in chrome <= 33.0 (possibly more)
-                       var $container = $( profile.container ),
-                               addClass = function ( node, value ) {
-                                       var current = node.getAttribute( 'class' ),
-                                               list = current ? current.split( ' ' ) : false,
-                                               idx = list ? list.indexOf( value ) : -1;
-
-                                       if ( idx === -1 ) {
-                                               node.setAttribute( 'class', current ? ( current + ' ' + value ) : value );
-                                       }
-                               },
-                               removeClass = function ( node, value ) {
-                                       var current = node.getAttribute( 'class' ),
-                                               list = current ? current.split( ' ' ) : false,
-                                               idx = list ? list.indexOf( value ) : -1;
-
-                                       if ( idx !== -1 ) {
-                                               list.splice( idx, 1 );
-                                               node.setAttribute( 'class', list.join( ' ' ) );
-                                       }
-                               },
-                               // hide all tipsy flyouts
-                               hide = function () {
-                                       $container.find( '.mw-debug-profile-period.tipsy-visible' )
-                                               .each( function () {
-                                                       removeClass( this, 'tipsy-visible' );
-                                                       $( this ).tipsy( 'hide' );
-                                               } );
-                               };
-
-                       $container.find( '.mw-debug-profile-period' ).tipsy( {
-                               fade: true,
-                               gravity: function () {
-                                       return $.fn.tipsy.autoNS.call( this ) + $.fn.tipsy.autoWE.call( this );
-                               },
-                               className: 'mw-debug-profile-tipsy',
-                               center: false,
-                               html: true,
-                               trigger: 'manual',
-                               title: function () {
-                                       return profile.buildFlyout( $( this ).data( 'period' ) ).html();
-                               }
-                       } ).on( 'mouseenter', function () {
-                               hide();
-                               addClass( this, 'tipsy-visible' );
-                               $( this ).tipsy( 'show' );
-                       } );
-
-                       $container.on( 'mouseleave', function ( event ) {
-                               var $from = $( event.relatedTarget ),
-                                       $to = $( event.target );
-                               // only close the tipsy if we are not
-                               if ( $from.closest( '.tipsy' ).length === 0 &&
-                                       $to.closest( '.tipsy' ).length === 0 &&
-                                       $to.get( 0 ).namespaceURI !== 'http://www.w4.org/2000/svg'
-                               ) {
-                                       hide();
-                               }
-                       } ).on( 'click', function () {
-                               // convenience method for closing
-                               hide();
-                       } );
-               },
-
-               /**
-                * @return number the x co-ordinate for the specified timestamp
-                */
-               xCoord: function ( msTimestamp ) {
-                       return ( msTimestamp - profile.data.timespan.start ) * profile.ratio;
-               }
-       };
-
-       function ProfileData( data, width, mergeThresholdPx, dropThresholdPx ) {
-               // validate input data
-               this.data = data.map( function ( event ) {
-                       event.periods = event.periods.filter( function ( period ) {
-                               return period.start && period.end
-                                       && period.start < period.end
-                                       // period start must be a reasonable ms timestamp
-                                       && period.start > 1000000;
-                       } );
-                       return event;
-               } ).filter( function ( event ) {
-                       return event.name && event.periods.length > 0;
-               } );
-
-               // start and end time of the data
-               this.timespan = this.data.reduce( function ( result, event ) {
-                       return event.periods.reduce( periodMinMax, result );
-               }, periodMinMax.initial() );
-
-               // transform input data
-               this.groups = this.collate( width, mergeThresholdPx, dropThresholdPx );
-
-               return this;
-       }
-
-       /**
-        * There are too many unique events to display a line for each,
-        * so this does a basic grouping.
-        */
-       ProfileData.groupOf = function ( label ) {
-               var pos, prefix = 'Profile section ended by close(): ';
-               if ( label.indexOf( prefix ) === 0 ) {
-                       label = label.slice( prefix.length );
-               }
-
-               pos = [ '::', ':', '-' ].reduce( function ( result, separator ) {
-                       var pos = label.indexOf( separator );
-                       if ( pos === -1 ) {
-                               return result;
-                       } else if ( result === -1 ) {
-                               return pos;
-                       } else {
-                               return Math.min( result, pos );
-                       }
-               }, -1 );
-
-               if ( pos === -1 ) {
-                       return label;
-               } else {
-                       return label.slice( 0, pos );
-               }
-       };
-
-       /**
-        * @return Array list of objects with `name` and `events` keys
-        */
-       ProfileData.groupEvents = function ( events ) {
-               var group, i,
-                       groups = {};
-
-               // Group events together
-               for ( i = events.length - 1; i >= 0; i-- ) {
-                       group = ProfileData.groupOf( events[i].name );
-                       if ( groups[group] ) {
-                               groups[group].push( events[i] );
-                       } else {
-                               groups[group] = [events[i]];
-                       }
-               }
-
-               // Return an array of groups
-               return Object.keys( groups ).map( function ( group ) {
-                       return {
-                               name: group,
-                               events: groups[group]
-                       };
-               } );
-       };
-
-       ProfileData.periodSorter = function ( a, b ) {
-               if ( a.start === b.start ) {
-                       return a.end - b.end;
-               }
-               return a.start - b.start;
-       };
-
-       ProfileData.genMergePeriodReducer = function ( mergeThresholdMs ) {
-               return function ( result, period ) {
-                       if ( result.length === 0 ) {
-                               // period is first result
-                               return [{
-                                       start: period.start,
-                                       end: period.end,
-                                       contained: [period]
-                               }];
-                       }
-                       var last = result[result.length - 1];
-                       if ( period.end < last.end ) {
-                               // end is contained within previous
-                               result[result.length - 1].contained.push( period );
-                       } else if ( period.start - mergeThresholdMs < last.end ) {
-                               // neighbors within merging distance
-                               result[result.length - 1].end = period.end;
-                               result[result.length - 1].contained.push( period );
-                       } else {
-                               // period is next result
-                               result.push( {
-                                       start: period.start,
-                                       end: period.end,
-                                       contained: [period]
-                               } );
-                       }
-                       return result;
-               };
-       };
-
-       /**
-        * Collect all periods from the grouped events and apply merge and
-        * drop transformations
-        */
-       ProfileData.extractPeriods = function ( events, mergeThresholdMs, dropThresholdMs ) {
-               // collect the periods from all events
-               return events.reduce( function ( result, event ) {
-                               if ( !event.periods.length ) {
-                                       return result;
-                               }
-                               result.push.apply( result, event.periods.map( function ( period ) {
-                                       // maintain link from period to event
-                                       period.source = event;
-                                       return period;
-                               } ) );
-                               return result;
-                       }, [] )
-                       // sort combined periods
-                       .sort( ProfileData.periodSorter )
-                       // Apply merge threshold. Original periods
-                       // are maintained in the `contained` property
-                       .reduce( ProfileData.genMergePeriodReducer( mergeThresholdMs ), [] )
-                       // Apply drop threshold
-                       .filter( function ( period ) {
-                               return period.end - period.start > dropThresholdMs;
-                       } );
-       };
-
-       /**
-        * runs a callback on all periods in the group.  Only valid after
-        * groups.periods[0..n].contained are populated. This runs against
-        * un-transformed data and is better suited to summing or other
-        * stat collection
-        */
-       ProfileData.reducePeriods = function ( group, callback, result ) {
-               return group.periods.reduce( function ( result, period ) {
-                       return period.contained.reduce( callback, result );
-               }, result );
-       };
-
-       /**
-        * Transforms this.data grouping by labels, merging neighboring
-        * events in the groups, and drops events and groups below the
-        * display threshold. Groups are returned sorted by starting time.
-        */
-       ProfileData.prototype.collate = function ( width, mergeThresholdPx, dropThresholdPx ) {
-               // ms to pixel ratio
-               var ratio = ( this.timespan.end - this.timespan.start ) / width,
-                       // transform thresholds to ms
-                       mergeThresholdMs = mergeThresholdPx * ratio,
-                       dropThresholdMs = dropThresholdPx * ratio;
-
-               return ProfileData.groupEvents( this.data )
-                       // generate data about the grouped events
-                       .map( function ( group ) {
-                               // Cleaned periods from all events
-                               group.periods = ProfileData.extractPeriods( group.events, mergeThresholdMs, dropThresholdMs );
-                               // min and max timestamp per group
-                               group.timespan = ProfileData.reducePeriods( group, periodMinMax, periodMinMax.initial() );
-                               // ms from first call to end of last call
-                               group.timespan.length = group.timespan.end - group.timespan.start;
-                               // collect the un-transformed periods
-                               group.timespan.sum = ProfileData.reducePeriods( group, function ( result, period ) {
-                                               result.push( period );
-                                               return result;
-                                       }, [] )
-                                       // sort by start time
-                                       .sort( ProfileData.periodSorter )
-                                       // merge overlapping
-                                       .reduce( ProfileData.genMergePeriodReducer( 0 ), [] )
-                                       // sum
-                                       .reduce( function ( result, period ) {
-                                               return result + period.end - period.start;
-                                       }, 0 );
-
-                               return group;
-                       }, this )
-                       // remove groups that have had all their periods filtered
-                       .filter( function ( group ) {
-                               return group.periods.length > 0;
-                       } )
-                       // sort events by first start
-                       .sort( function ( a, b ) {
-                               return ProfileData.periodSorter( a.timespan, b.timespan );
-                       } );
-       };
-
-       // reducer to find edges of period array
-       function periodMinMax( result, period ) {
-               if ( period.start < result.start ) {
-                       result.start = period.start;
-               }
-               if ( period.end > result.end ) {
-                       result.end = period.end;
-               }
-               return result;
-       }
-
-       periodMinMax.initial = function () {
-               return { start: Number.POSITIVE_INFINITY, end: Number.NEGATIVE_INFINITY };
-       };
-
-       function formatBytes( bytes ) {
-               var i, sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
-               if ( bytes === 0 ) {
-                       return '0 Bytes';
-               }
-               i = parseInt( Math.floor( Math.log( bytes ) / Math.log( 1024 ) ), 10 );
-               return Math.round( bytes / Math.pow( 1024, i ), 2 ) + ' ' + sizes[i];
-       }
-
-       // turns a 2d array into a point list for svg
-       // polygon points attribute
-       // ex: [[1,2],[3,4],[4,2]] = '1,2 3,4 4,2'
-       function pointList( pairs ) {
-               return pairs.map( function ( pair ) {
-                       return pair.join( ',' );
-               } ).join( ' ' );
-       }
-}( mediaWiki, jQuery ) );
index a53cbcb..cf56f29 100644 (file)
                 * Returns null if not found.
                 *
                 * @param {string} param The parameter name.
-                * @param {string} [url=document.location.href] URL to search through, defaulting to the current document's URL.
+                * @param {string} [url=location.href] URL to search through, defaulting to the current browsing location.
                 * @return {Mixed} Parameter value or null.
                 */
                getParamValue: function ( param, url ) {
                        if ( url === undefined ) {
-                               url = document.location.href;
+                               url = location.href;
                        }
                        // Get last match, stop at hash
                        var     re = new RegExp( '^[^#]*[&?]' + $.escapeRE( param ) + '=([^&#]*)' ),
index 8dfb4dd..39129cb 100644 (file)
@@ -16742,7 +16742,7 @@ subpage title=[[Subpage test/L1/L2/L3]]
 !! wikitext
 [[../../////]]
 !! html
-<p><a href="/index.php?title=Subpage_test/L1////&amp;action=edit&amp;redlink=1" class="new" title="Subpage test/L1//// (page does not exist)">///</a>
+<p><a href="/index.php?title=Subpage_test/L1&amp;action=edit&amp;redlink=1" class="new" title="Subpage test/L1 (page does not exist)">Subpage test/L1</a>
 </p>
 !! end
 
index 6e41de7..06951b7 100644 (file)
@@ -105,7 +105,7 @@ class MWDebugTest extends MediaWikiTestCase {
 
                $expectedKeys = array( 'mwVersion', 'phpEngine', 'phpVersion', 'gitRevision', 'gitBranch',
                        'gitViewUrl', 'time', 'log', 'debugLog', 'queries', 'request', 'memory',
-                       'memoryPeak', 'includes', 'profile', '_element' );
+                       'memoryPeak', 'includes', '_element' );
 
                foreach ( $expectedKeys as $expectedKey ) {
                        $this->assertArrayHasKey( $expectedKey, $data['debuginfo'], "debuginfo has $expectedKey" );
diff --git a/tests/phpunit/includes/debug/logging/legacy/LoggerTest.php b/tests/phpunit/includes/debug/logging/legacy/LoggerTest.php
new file mode 100644 (file)
index 0000000..bad8d8d
--- /dev/null
@@ -0,0 +1,70 @@
+<?php
+/**
+ * @section LICENSE
+ * 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
+ */
+
+class MWLoggerLegacyLoggerTest extends MediaWikiTestCase {
+
+       /**
+        * @covers MWLoggerLegacyLogger::interpolate
+        * @dataProvider provideInterpolate
+        */
+       public function testInterpolate( $message, $context, $expect ) {
+               $this->assertEquals(
+                       $expect, MWLoggerLegacyLogger::interpolate( $message, $context ) );
+       }
+
+       public function provideInterpolate() {
+               return array(
+                       array(
+                               'no-op',
+                               array(),
+                               'no-op',
+                       ),
+                       array(
+                               'Hello {world}!',
+                               array(
+                                       'world' => 'World',
+                               ),
+                               'Hello World!',
+                       ),
+                       array(
+                               '{greeting} {user}',
+                               array(
+                                       'greeting' => 'Goodnight',
+                                       'user' => 'Moon',
+                               ),
+                               'Goodnight Moon',
+                       ),
+                       array(
+                               'Oops {key_not_set}',
+                               array(),
+                               'Oops {key_not_set}',
+                       ),
+                       array(
+                               '{ not interpolated }',
+                               array(
+                                       'not interpolated' => 'This should NOT show up in the message',
+                               ),
+                               '{ not interpolated }',
+                       ),
+               );
+       }
+
+}
diff --git a/tests/phpunit/includes/libs/cdb/CdbTest.php b/tests/phpunit/includes/libs/cdb/CdbTest.php
deleted file mode 100644 (file)
index 487ee1f..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-<?php
-
-/**
- * Test the CDB reader/writer
- * @covers CdbWriterPHP
- * @covers CdbWriterDBA
- */
-class CdbTest extends MediaWikiTestCase {
-
-       protected function setUp() {
-               parent::setUp();
-               if ( !CdbReader::haveExtension() ) {
-                       $this->markTestSkipped( 'Native CDB support is not available' );
-               }
-       }
-
-       /**
-        * @group medium
-        */
-       public function testCdb() {
-               $dir = wfTempDir();
-               if ( !is_writable( $dir ) ) {
-                       $this->markTestSkipped( "Temp dir isn't writable" );
-               }
-
-               $phpcdbfile = $this->getNewTempFile();
-               $dbacdbfile = $this->getNewTempFile();
-
-               $w1 = new CdbWriterPHP( $phpcdbfile );
-               $w2 = new CdbWriterDBA( $dbacdbfile );
-
-               $data = array();
-               for ( $i = 0; $i < 1000; $i++ ) {
-                       $key = $this->randomString();
-                       $value = $this->randomString();
-                       $w1->set( $key, $value );
-                       $w2->set( $key, $value );
-
-                       if ( !isset( $data[$key] ) ) {
-                               $data[$key] = $value;
-                       }
-               }
-
-               $w1->close();
-               $w2->close();
-
-               $this->assertEquals(
-                       md5_file( $phpcdbfile ),
-                       md5_file( $dbacdbfile ),
-                       'same hash'
-               );
-
-               $r1 = new CdbReaderPHP( $phpcdbfile );
-               $r2 = new CdbReaderDBA( $dbacdbfile );
-
-               foreach ( $data as $key => $value ) {
-                       if ( $key === '' ) {
-                               // Known bug
-                               continue;
-                       }
-                       $v1 = $r1->get( $key );
-                       $v2 = $r2->get( $key );
-
-                       $v1 = $v1 === false ? '(not found)' : $v1;
-                       $v2 = $v2 === false ? '(not found)' : $v2;
-
-                       # cdbAssert( 'Mismatch', $key, $v1, $v2 );
-                       $this->cdbAssert( "PHP error", $key, $v1, $value );
-                       $this->cdbAssert( "DBA error", $key, $v2, $value );
-               }
-       }
-
-       private function randomString() {
-               $len = mt_rand( 0, 10 );
-               $s = '';
-               for ( $j = 0; $j < $len; $j++ ) {
-                       $s .= chr( mt_rand( 0, 255 ) );
-               }
-
-               return $s;
-       }
-
-       private function cdbAssert( $msg, $key, $v1, $v2 ) {
-               $this->assertEquals(
-                       $v2,
-                       $v1,
-                       $msg . ', k=' . bin2hex( $key )
-               );
-       }
-}
index 002e2cb..97aa0a3 100644 (file)
@@ -68,4 +68,36 @@ class FormatMetadataTest extends MediaWikiMediaTestCase {
                        // TODO: more test cases
                );
        }
+
+       /**
+        * @param mixed $input
+        * @param mixed $output
+        * @dataProvider provideResolveMultivalueValue
+        * @covers FormatMetadata::resolveMultivalueValue
+        */
+       public function testResolveMultivalueValue( $input, $output ) {
+               $formatMetadata = new FormatMetadata();
+               $class = new ReflectionClass( 'FormatMetadata' );
+               $method = $class->getMethod( 'resolveMultivalueValue' );
+               $method->setAccessible( true );
+               $actualInput = $method->invoke( $formatMetadata, $input );
+               $this->assertEquals( $output, $actualInput );
+       }
+
+       public function provideResolveMultivalueValue() {
+               return array(
+                       'nonArray' => array( 'foo', 'foo' ),
+                       'multiValue' => array( array( 'first', 'second', 'third', '_type' => 'ol' ), 'first' ),
+                       'noType' => array( array( 'first', 'second', 'third' ), 'first' ),
+                       'typeFirst' => array( array( '_type' => 'ol', 'first', 'second', 'third' ), 'first' ),
+                   'multilang' => array(
+                           array( 'en' => 'first', 'de' => 'Erste', '_type' => 'lang' ),
+                           array( 'en' => 'first', 'de' => 'Erste', '_type' => 'lang' ),
+                   ),
+                   'multilang-multivalue' => array(
+                           array( 'en' => array( 'first', 'second' ), 'de' => array( 'Erste', 'Zweite' ), '_type' => 'lang' ),
+                           array( 'en' => 'first', 'de' => 'Erste', '_type' => 'lang' ),
+                   ),
+               );
+       }
 }
index a189387..1c25211 100644 (file)
@@ -290,7 +290,7 @@ mw.loader.addSource( {
 
        /**
         * @dataProvider provideGetModuleRegistrations
-        * @covers ResourceLoaderStartupModule::optimizeDependencies
+        * @covers ResourceLoaderStartupModule::compileUnresolvedDependencies
         * @covers ResourceLoaderStartUpModule::getModuleRegistrations
         * @covers ResourceLoader::makeLoaderSourcesScript
         * @covers ResourceLoader::makeLoaderRegisterScript
index 50fa384..0e11cca 100644 (file)
@@ -35,14 +35,14 @@ class UIDGeneratorTest extends MediaWikiTestCase {
                        $lastId_bin = wfBaseConvert( $lastId, 10, 2 );
 
                        $this->assertGreaterThanOrEqual(
-                               substr( $id_bin, 0, $tbits ),
                                substr( $lastId_bin, 0, $tbits ),
+                               substr( $id_bin, 0, $tbits ),
                                "New ID timestamp ($id_bin) >= prior one ($lastId_bin)." );
 
                        if ( $hostbits ) {
                                $this->assertEquals(
-                                       substr( $id_bin, 0, -$hostbits ),
-                                       substr( $lastId_bin, 0, -$hostbits ),
+                                       substr( $id_bin, -$hostbits ),
+                                       substr( $lastId_bin, -$hostbits ),
                                        "Host ID of ($id_bin) is same as prior one ($lastId_bin)." );
                        }
 
index 415e11b..9e62751 100644 (file)
@@ -30,13 +30,15 @@ abstract class DumpTestCase extends MediaWikiLangTestCase {
         *
         * @param Page $page Page to add the revision to
         * @param string $text Revisions text
-        * @param string $summary Revisions summare
-        * @return array
+        * @param string $summary Revisions summary
+        * @param string $model The model ID (defaults to wikitext)
+        *
         * @throws MWException
+        * @return array
         */
-       protected function addRevision( Page $page, $text, $summary ) {
+       protected function addRevision( Page $page, $text, $summary, $model = CONTENT_MODEL_WIKITEXT ) {
                $status = $page->doEditContent(
-                       ContentHandler::makeContent( $text, $page->getTitle() ),
+                       ContentHandler::makeContent( $text, $page->getTitle(), $model ),
                        $summary
                );
 
index e620b08..26662d5 100644 (file)
@@ -27,6 +27,10 @@ class TextPassDumperTest extends DumpTestCase {
                $this->tablesUsed[] = 'revision';
                $this->tablesUsed[] = 'text';
 
+               $this->mergeMwGlobalArrayValue( 'wgContentHandlers', array(
+                       "BackupTextPassTestModel" => "BackupTextPassTestModelHandler"
+               ) );
+
                $ns = $this->getDefaultWikitextNS();
 
                try {
@@ -61,7 +65,8 @@ class TextPassDumperTest extends DumpTestCase {
                        $this->pageId3 = $page->getId();
                        $page->doDeleteArticle( "Testing ;)" );
 
-                       // Page from non-default namespace
+                       // Page from non-default namespace and model.
+                       // ExportTransform applies.
 
                        if ( $ns === NS_TALK ) {
                                // @todo work around this.
@@ -73,7 +78,8 @@ class TextPassDumperTest extends DumpTestCase {
                        $page = WikiPage::factory( $title );
                        list( $this->revId4_1, $this->textId4_1 ) = $this->addRevision( $page,
                                "Talk about BackupDumperTestP1 Text1",
-                               "Talk BackupDumperTestP1 Summary1" );
+                               "Talk BackupDumperTestP1 Summary1",
+                               "BackupTextPassTestModel" );
                        $this->pageId4 = $page->getId();
                } catch ( Exception $e ) {
                        // We'd love to pass $e directly. However, ... see
@@ -141,7 +147,10 @@ class TextPassDumperTest extends DumpTestCase {
                $this->assertPageStart( $this->pageId4, NS_TALK, "Talk:BackupDumperTestP1" );
                $this->assertRevision( $this->revId4_1, "Talk BackupDumperTestP1 Summary1",
                        $this->textId4_1, false, "nktofwzd0tl192k3zfepmlzxoax1lpe",
-                       "Talk about BackupDumperTestP1 Text1" );
+                       "TALK ABOUT BACKUPDUMPERTESTP1 TEXT1",
+                       false,
+                       "BackupTextPassTestModel",
+                       "text/plain" );
                $this->assertPageEnd();
 
                $this->assertDumpEnd();
@@ -209,7 +218,10 @@ class TextPassDumperTest extends DumpTestCase {
                $this->assertPageStart( $this->pageId4, NS_TALK, "Talk:BackupDumperTestP1" );
                $this->assertRevision( $this->revId4_1, "Talk BackupDumperTestP1 Summary1",
                        $this->textId4_1, false, "nktofwzd0tl192k3zfepmlzxoax1lpe",
-                       "Talk about BackupDumperTestP1 Text1" );
+                       "TALK ABOUT BACKUPDUMPERTESTP1 TEXT1",
+                       false,
+                       "BackupTextPassTestModel",
+                       "text/plain" );
                $this->assertPageEnd();
 
                $this->assertDumpEnd();
@@ -362,7 +374,10 @@ class TextPassDumperTest extends DumpTestCase {
                                        $this->assertRevision( $this->revId4_1 + $i * self::$numOfRevs,
                                                "Talk BackupDumperTestP1 Summary1",
                                                $this->textId4_1, false, "nktofwzd0tl192k3zfepmlzxoax1lpe",
-                                               "Talk about BackupDumperTestP1 Text1" );
+                                               "TALK ABOUT BACKUPDUMPERTESTP1 TEXT1",
+                                               false,
+                                               "BackupTextPassTestModel",
+                                               "text/plain" );
                                        $this->assertPageEnd();
 
                                        $lookingForPage = 1;
@@ -566,8 +581,8 @@ class TextPassDumperTest extends DumpTestCase {
         <ip>127.0.0.1</ip>
       </contributor>
       <comment>Talk BackupDumperTestP1 Summary1</comment>
-      <model>wikitext</model>
-      <format>text/x-wiki</format>
+      <model>BackupTextPassTestModel</model>
+      <format>text/plain</format>
       <text id="' . $this->textId4_1 . '" bytes="35" />
       <sha1>nktofwzd0tl192k3zfepmlzxoax1lpe</sha1>
     </revision>
@@ -582,3 +597,15 @@ class TextPassDumperTest extends DumpTestCase {
                return $fname;
        }
 }
+
+class BackupTextPassTestModelHandler extends TextContentHandler {
+
+       public function __construct() {
+               parent::__construct( 'BackupTextPassTestModel' );
+       }
+
+       public function exportTransform( $text, $format = null ) {
+               return strtoupper( $text );
+       }
+
+}
\ No newline at end of file
index 92dad9f..5464d22 100644 (file)
                                '</table>'
                );
                $table.tablesorter();
-               assert.equal( $table.find( '#A2' ).prop( 'headerIndex' ),
+               assert.equal( $table.find( '#A2' ).data( 'headerIndex' ),
                        undefined,
                        'A2 should not be a sort header'
                );
-               assert.equal( $table.find( '#C1' ).prop( 'headerIndex' ),
+               assert.equal( $table.find( '#C1' ).data( 'headerIndex' ),
                        2,
                        'C1 should be a sort header'
                );
                                '</table>'
                );
                $table.tablesorter();
-               assert.equal( $table.find( '#C2' ).prop( 'headerIndex' ),
+               assert.equal( $table.find( '#C2' ).data( 'headerIndex' ),
                        2,
                        'C2 should be a sort header'
                );
-               assert.equal( $table.find( '#C1' ).prop( 'headerIndex' ),
+               assert.equal( $table.find( '#C1' ).data( 'headerIndex' ),
                        undefined,
                        'C1 should not be a sort header'
                );
                        '</tbody></table>' );
 
                        $table.tablesorter();
-                       assert.equal( $table.find( 'tr:eq(1) th:eq(1)').prop('headerIndex'),
+                       assert.equal( $table.find( 'tr:eq(1) th:eq(1)').data('headerIndex'),
                                2,
-                               'Incorrect index of sort header' );
+                               'Incorrect index of sort header'
+                       );
                }
        );