rather than consume everything until the end of the page.
* New maintenance script resetUserEmail.php allows sysadmins to reset user emails in case
a user forgot password/account was stolen.
+* wfCheckEntropy() was removed (deprecated in 1.27).
== Compatibility ==
'LinksDeletionUpdate' => __DIR__ . '/includes/deferred/LinksDeletionUpdate.php',
'LinksUpdate' => __DIR__ . '/includes/deferred/LinksUpdate.php',
'ListDuplicatedFilesPage' => __DIR__ . '/includes/specials/SpecialListDuplicatedFiles.php',
+ 'ListToggle' => __DIR__ . '/includes/ListToggle.php',
'ListVariants' => __DIR__ . '/maintenance/language/listVariants.php',
'ListredirectsPage' => __DIR__ . '/includes/specials/SpecialListredirects.php',
'LoadBalancer' => __DIR__ . '/includes/db/loadbalancer/LoadBalancer.php',
"ext-iconv": "*",
"liuggio/statsd-php-client": "1.0.18",
"mediawiki/at-ease": "1.1.0",
- "oojs/oojs-ui": "0.15.4",
+ "oojs/oojs-ui": "0.16.0",
"oyejorge/less.php": "1.7.0.10",
"php": ">=5.5.9",
"psr/log": "1.0.0",
"monolog/monolog": "~1.17.2",
"nikic/php-parser": "1.4.1",
"nmred/kafka-php": "0.1.5",
- "phpunit/phpunit": "3.7.37",
+ "phpunit/phpunit": "4.8.23",
"wikimedia/avro": "1.7.7"
},
"suggest": {
/**
* Whether to use PHP session handling ($_SESSION and session_*() functions)
+ *
+ * If the constant MW_NO_SESSION is defined, this is forced to 'disable'.
+ *
+ * If the constant MW_NO_SESSION_HANDLER is defined, this is ignored and PHP
+ * session handling will function independently of SessionHandler.
+ * SessionHandler and PHP's session handling may attempt to override each
+ * others' cookies.
+ *
* @since 1.27
* @var string
* - 'enable': Integrate with PHP's session handling as much as possible.
return Wikimedia\base_convert( $input, $sourceBase, $destBase, $pad, $lowercase, $engine );
}
-/**
- * Check if there is sufficient entropy in php's built-in session generation
- *
- * @deprecated since 1.27, PHP's session generation isn't used with
- * MediaWiki\\Session\\SessionManager
- * @return bool True = there is sufficient entropy
- */
-function wfCheckEntropy() {
- wfDeprecated( __FUNCTION__, '1.27' );
- return (
- ( wfIsWindows() && version_compare( PHP_VERSION, '5.3.3', '>=' ) )
- || ini_get( 'session.entropy_file' )
- )
- && intval( ini_get( 'session.entropy_length' ) ) >= 32;
-}
-
/**
* @deprecated since 1.27, PHP's session generation isn't used with
* MediaWiki\\Session\\SessionManager
function wfSetupSession( $sessionId = false ) {
wfDeprecated( __FUNCTION__, '1.27' );
- // If they're calling this, they probably want our session management even
- // if NO_SESSION was set for Setup.php.
- if ( !MediaWiki\Session\PHPSessionHandler::isInstalled() ) {
- MediaWiki\Session\PHPSessionHandler::install( SessionManager::singleton() );
- }
-
if ( $sessionId ) {
session_id( $sessionId );
}
--- /dev/null
+<?php
+/**
+ * Class for generating clickable toggle links for a list of checkboxes.
+ *
+ * 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 for generating clickable toggle links for a list of checkboxes.
+ *
+ * This is only supported on clients that have JavaScript enabled; it is hidden
+ * for clients that have it disabled.
+ *
+ * @since 1.27
+ */
+class ListToggle {
+ /** @var OutputPage */
+ private $output;
+
+ public function __construct( OutputPage $output ) {
+ $this->output = $output;
+
+ $output->addModules( 'mediawiki.checkboxtoggle' );
+ $output->addModuleStyles( 'mediawiki.checkboxtoggle.styles' );
+ }
+
+ private function checkboxLink( $checkboxType ) {
+ return Html::element(
+ 'a', [ 'href' => '#', 'class' => 'mw-checkbox-' . $checkboxType ],
+ $this->output->msg( 'checkbox-' . $checkboxType )->text()
+ );
+ }
+
+ /**
+ * @return string
+ */
+ public function getHTML() {
+ // Select: All, None, Invert
+ $links = [];
+ $links[] = $this->checkboxLink( 'all' );
+ $links[] = $this->checkboxLink( 'none' );
+ $links[] = $this->checkboxLink( 'invert' );
+
+ return Html::rawElement( 'div',
+ [
+ 'class' => 'mw-checkbox-toggle-controls'
+ ],
+ $this->output->msg( 'checkbox-select' )
+ ->rawParams( $this->output->getLanguage()->commaList( $links ) )->escaped()
+ );
+ }
+}
*/
public static function transformResourcePath( Config $config, $path ) {
global $IP;
- $remotePath = $config->get( 'ResourceBasePath' );
+ $remotePathPrefix = $config->get( 'ResourceBasePath' );
+ if ( $remotePathPrefix === '' ) {
+ // The configured base path is required to be empty string for
+ // wikis in the domain root
+ $remotePath = '/';
+ } else {
+ $remotePath = $remotePathPrefix;
+ }
if ( strpos( $path, $remotePath ) !== 0 ) {
// Path is outside wgResourceBasePath, ignore.
return $path;
}
$path = RelPath\getRelativePath( $path, $remotePath );
- return self::transformFilePath( $remotePath, $IP, $path );
+ return self::transformFilePath( $remotePathPrefix, $IP, $path );
}
/**
* Caller is responsible for ensuring the file exists. Emits a PHP warning otherwise.
*
* @since 1.27
- * @param string $remotePath URL path that points to $localPath
+ * @param string $remotePath URL path prefix that points to $localPath
* @param string $localPath File directory exposed at $remotePath
* @param string $file Path to target file relative to $localPath
* @return string URL
*/
- public static function transformFilePath( $remotePath, $localPath, $file ) {
+ public static function transformFilePath( $remotePathPrefix, $localPath, $file ) {
$hash = md5_file( "$localPath/$file" );
if ( $hash === false ) {
wfLogWarning( __METHOD__ . ": Failed to hash $localPath/$file" );
$hash = '';
}
- return "$remotePath/$file?" . substr( $hash, 0, 5 );
+ return "$remotePathPrefix/$file?" . substr( $hash, 0, 5 );
}
/**
) {
$wgPHPSessionHandling = 'warn';
}
+if ( defined( 'MW_NO_SESSION' ) ) {
+ // If the entry point wants no session, force 'disable' here unless they
+ // specifically set it to the (undocumented) 'warn'.
+ $wgPHPSessionHandling = MW_NO_SESSION === 'warn' ? 'warn' : 'disable';
+}
Profiler::instance()->scopedProfileOut( $ps_default );
session_name( $wgSessionName ? $wgSessionName : $wgCookiePrefix . '_session' );
}
- // Create the SessionManager singleton and set up our session handler
- MediaWiki\Session\PHPSessionHandler::install(
- MediaWiki\Session\SessionManager::singleton()
- );
+ // Create the SessionManager singleton and set up our session handler,
+ // unless we're specifically asked not to.
+ if ( !defined( 'MW_NO_SESSION_HANDLER' ) ) {
+ MediaWiki\Session\PHPSessionHandler::install(
+ MediaWiki\Session\SessionManager::singleton()
+ );
+ }
// Initialize the session
try {
session_id( $session->getId() );
MediaWiki\quietCall( 'session_start' );
}
+
+ unset( $session );
+} else {
+ // Even if we didn't set up a global Session, still install our session
+ // handler unless specifically requested not to.
+ if ( !defined( 'MW_NO_SESSION_HANDLER' ) ) {
+ MediaWiki\Session\PHPSessionHandler::install(
+ MediaWiki\Session\SessionManager::singleton()
+ );
+ }
}
Profiler::instance()->scopedProfileOut( $ps_session );
$this->buttons .= Xml::tags( 'div', [ 'class' =>
'mw-history-revisionactions' ], $actionButtons );
}
+
+ if ( $user->isAllowed( 'deleterevision' ) || $this->showTagEditUI ) {
+ $this->buttons .= ( new ListToggle( $this->getOutput() ) )->getHTML();
+ }
+
$this->buttons .= '</div>';
$s .= $this->buttons;
"Red Winged Duck"
]
},
- "apihelp-main-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:API:Main_page|Дакумэнтацыя]]\n* [[mw:API:FAQ|Частыя пытаньні]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Сьпіс рассылкі]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API-аб’явы]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Памылкі і запыты]\n</div>\n<strong>Статус:</strong> усе магчымасьці на гэтай старонцы павінны працаваць, але API знаходзіцца ў актыўнай распрацоўцы і можа зьмяняцца ў любы момант. Падпісвайцеся на [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ рассылку mediawiki-api-announce] дзеля паведамленьняў пра абнаўленьні.\n\n<strong>Памылковыя запыты:</strong> калі да API дасылаюцца памылковыя запыты, HTTP-загаловак будзе дасланы з ключом «MediaWiki-API-Error», а потым значэньне загалоўку і код памылкі будуць выстаўленыя на аднолькавае значэньне. Дзеля дадатковай інфармацыі глядзіце [[mw:API:Errors_and_warnings|API: Памылкі і папярэджаньні]].",
+ "apihelp-main-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:API:Main_page|Дакумэнтацыя]]\n* [[mw:API:FAQ|Частыя пытаньні]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Сьпіс рассылкі]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API-аб’явы]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Памылкі і запыты]\n</div>\n<strong>Статус:</strong> усе магчымасьці на гэтай старонцы павінны працаваць, але API знаходзіцца ў актыўнай распрацоўцы і можа зьмяняцца ў любы момант. Падпісвайцеся на [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ рассылку mediawiki-api-announce] дзеля паведамленьняў пра абнаўленьні.\n\n<strong>Памылковыя запыты:</strong> калі да API дасылаюцца памылковыя запыты, HTTP-загаловак будзе дасланы з ключом «MediaWiki-API-Error», а потым значэньне загалоўку і код памылкі будуць выстаўленыя на аднолькавае значэньне. Дзеля дадатковай інфармацыі глядзіце [[mw:API:Errors_and_warnings|API: Памылкі і папярэджаньні]].\n\n<strong>Тэставаньне:</strong> для зручнасьці праверкі API-запытаў, глядзіце [[Special:ApiSandbox]].",
"apihelp-main-param-action": "Дзеяньне для выкананьня.",
"apihelp-main-param-format": "Фармат вываду.",
"apihelp-main-param-maxlag": "Максымальная затрымка можа ўжывацца, калі MediaWiki ўсталяваная ў клястэр з рэплікаванай базай зьвестак. Дзеля захаваньня дзеяньняў, якія выклікаюць затрымку рэплікацыі, гэты парамэтар можа прымусіць кліента чакаць, пакуль затрымка рэплікацыі меншая за яго значэньне. У выпадку доўгай затрымкі, вяртаецца код памылкі <samp>maxlag</samp> з паведамленьнем кшталту <samp>Чаканьне $host: $lag сэкундаў затрымкі</samp>.<br />Глядзіце [[mw:Manual:Maxlag_parameter|Інструкцыя:Парамэтар maxlag]] дзеля дадатковай інфармацыі.",
"apihelp-createaccount-param-reason": "Raggiona, a facoltativa, d' 'a criaziona 'e nu cunto a mpizzà int' 'e reggistre.",
"apihelp-createaccount-param-language": "Codece 'e llengua a mpustà comme predefinita pe' n'utente (opzionale, 'e default fosse 'a lengue d' 'e cuntenute).",
"apihelp-delete-description": "Scancella 'na paggena.",
+ "apihelp-edit-param-prependtext": "Azzecca stu testo addò 'o cap' 'e paggena. Se mettesse ncuoll'a $1text.",
+ "apihelp-edit-param-appendtext": "Azzecca stu testo addò 'o cap' 'e paggena. Se mettesse ncuoll'a $1text.\n\nAusate $1section=new pe' ne puté appennere na seziona nova, ato che ausà stu parammetro.",
+ "apihelp-edit-param-undo": "Torna arrèto sta verziona. Miette ncuollo 'o $1text, $1prependtext e $1appendtext.",
+ "apihelp-edit-param-undoafter": "Torna arreto tuttuquante verziune 'e $1undo a cchesta. Si chesto nun fosse mpustato, avit'a ffà surtanto turnà arreto na verziona.",
+ "apihelp-edit-param-redirect": "Risolve automaticamente 'e redirect.",
+ "apihelp-edit-param-contentformat": "Serializaziona 'e furmatt' 'e cuntenute ausata p' 'o testo trasuto.",
+ "apihelp-edit-param-contentmodel": "Mudell' 'e cuntenute d' 'e cuntenute nuove nuove.",
+ "apihelp-edit-param-token": "'O token s'avess'a mannà sempe comm'a ll'urdemo parammetro, o minimo minimo aropp'a 'o parammetro 'e $1text.",
"apihelp-edit-example-edit": "Cagna paggena.",
+ "apihelp-edit-example-prepend": "Pre-appenne <kbd>__NOTOC__</kbd> a na paggena.",
+ "apihelp-edit-example-undo": "Torna arreto 'e verziune 13579 nfin'a 13585 cu n'autosommario.",
"apihelp-emailuser-description": "E-mail a n'utente.",
+ "apihelp-emailuser-param-target": "Utente a 'e quale s'avess'a mannà na mmasciata mail.",
+ "apihelp-emailuser-param-subject": "Oggetto d' 'a mail.",
+ "apihelp-emailuser-param-text": "Testo d' 'a mail.",
+ "apihelp-emailuser-param-ccme": "Manna na copia 'e sta mail a mme.",
+ "apihelp-emailuser-example-email": "Manna na e-mail a ll'utente <kbd>WikiSysop</kbd> c' 'o testo <kbd>Content</kbd>.",
+ "apihelp-expandtemplates-description": "Spannere tuttuquante 'e template dint' 'o wikitesto.",
+ "apihelp-expandtemplates-param-title": "Titolo d' 'a paggena.",
+ "apihelp-expandtemplates-param-text": "Wikitesto 'a scagnà/convertire.",
+ "apihelp-expandtemplates-param-revid": "ID 'e cagnamento, pe' <nowiki>{{REVISIONID}}</nowiki> e variabbele ca s'assummigliassero.",
+ "apihelp-expandtemplates-param-prop": "Quale nfurmaziune s'avess'a piglià.\n\nTenite a mmente ca nun s'è scigliuto valore nisciuno, 'o risultato cuntenesse 'o codice wiki, ma l'output sarrà fatto comm'a nu furmato obsoleto.",
+ "apihelp-expandtemplates-paramvalue-prop-wikitext": "'O wikitext spannuto.",
+ "apihelp-expandtemplates-paramvalue-prop-categories": "Ogne categurìa prisente int'a 'o valore 'e trasuta nun fosse rappresentato comm'asciuta 'e wikitesto.",
+ "apihelp-expandtemplates-paramvalue-prop-properties": "'E pruprietà 'e pagena definite p' 'e parole magiche spannute dint' 'o wikitesto.",
+ "apihelp-expandtemplates-paramvalue-prop-volatile": "Si l'output fosse volatile e nun s'avess'ausà n'atavota addò servesse dint' 'a paggena.",
+ "apihelp-expandtemplates-paramvalue-prop-ttl": "'O tiempo massimo aropp' 'o quale 'e caches d' 'o risultato s'avessero a nzegnà invalide.",
"apihelp-feedwatchlist-param-feedformat": "'O furmato d' 'o feed.",
"apihelp-login-example-login": "Tràse.",
"apihelp-move-description": "Mòve paggena.",
"apihelp-expandtemplates-paramvalue-prop-parsetree": "Cây phân tích XML của đầu vào.",
"apihelp-feedcontributions-description": "Trả về nguồn cấp đóng góp người dùng.",
"apihelp-feedcontributions-param-feedformat": "Định dạng nguồn cấp.",
+ "apihelp-feedcontributions-param-user": "Người dùng nhận được những đóng góp gì.",
"apihelp-feedcontributions-param-year": "Từ năm (trở về trước).",
"apihelp-feedcontributions-param-month": "Từ tháng (trở về trước).",
"apihelp-feedcontributions-param-deletedonly": "Chỉ hiện các đóng góp đã xóa.",
"apihelp-feedcontributions-example-simple": "Trả về các đóng góp của người dùng <kbd>Ví dụ</kbd>.",
"apihelp-feedrecentchanges-description": "Trả về nguồn cấp thay đổi gần đây.",
"apihelp-feedrecentchanges-param-feedformat": "Định dạng nguồn cấp.",
+ "apihelp-feedrecentchanges-param-days": "Ngày để giới hạn kết quả.",
+ "apihelp-feedrecentchanges-param-limit": "Số kết quả lớn nhất để cho ra.",
"apihelp-feedrecentchanges-param-hideminor": "Ẩn thay đổi nhỏ.",
"apihelp-feedrecentchanges-param-hidebots": "Ẩn thay đổi do bot thực hiện.",
"apihelp-feedrecentchanges-param-hideanons": "Ẩn thay đổi do người dùng vô danh thực hiện.",
"apihelp-feedrecentchanges-param-hidemyself": "Ẩn thay đổi do người dùng hiện tại thực hiện.",
"apihelp-feedrecentchanges-param-tagfilter": "Lọc theo thẻ.",
"apihelp-feedrecentchanges-example-simple": "Xem thay đổi gần đây.",
+ "apihelp-feedrecentchanges-example-30days": "Hiển thị các thay đổi trong 30 ngày gần đây.",
"apihelp-feedwatchlist-description": "Trả về nguồn cấp danh sách theo dõi.",
"apihelp-feedwatchlist-param-feedformat": "Định dạng nguồn cấp.",
"apihelp-feedwatchlist-example-default": "Xem nguồn cấp danh sách theo dõi.",
+ "apihelp-filerevert-description": "Phục hồi một tập tin sang một phiên bản cũ.",
"apihelp-filerevert-param-comment": "Tải lên bình luận.",
"apihelp-filerevert-param-archivename": "Tên lưu trữ của bản sửa đổi để trở lại .",
"apihelp-filerevert-example-revert": "Hoàn nguyên <kbd>Wiki.png</kbd> veef phiên bản <kbd>2011-03-05T15:27:40Z</kbd>.",
"apihelp-help-description": "Hiển thị trợ giúp cho các mô-đun xác định.",
"apihelp-help-param-helpformat": "Định dạng của văn bản trợ giúp được cho ra.",
+ "apihelp-help-example-main": "Trợ giúp cho các mô-đun chính.",
"apihelp-help-example-recursive": "Tất cả trợ giúp trong một trang",
"apihelp-help-example-help": "Trợ giúp cho chính bản thân module trợ giúp",
"apihelp-help-example-query": "Trợ giúp cho hai module con truy vấn",
"apihelp-imagerotate-param-rotation": "Độ xoay hình ảnh theo chiều kim đồng hồ.",
"apihelp-imagerotate-example-simple": "Xoay <kbd>Tập tin:Ví dụ.jpg</kbd> <kbd>90</kbd> độ.",
"apihelp-imagerotate-example-generator": "Xoay tất cả các hình ảnh trong <kbd>Thể loại:Búng</kbd> <kbd>180</kbd> độ.",
+ "apihelp-import-param-interwikisource": "Dành cho các nhập khẩu interwiki: wiki để nhập từ.",
"apihelp-login-param-name": "Tên người dùng.",
"apihelp-login-param-password": "Mật khẩu.",
"apihelp-login-param-domain": "Tên miền (tùy chọn).",
* Override the necessary bits of the config to run an installation.
*/
public static function overrideConfig() {
- define( 'MW_NO_SESSION', 1 );
+ // Use PHP's built-in session handling, since MediaWiki's
+ // SessionHandler can't work before we have an object cache set up.
+ define( 'MW_NO_SESSION_HANDLER', 1 );
// Don't access the database
$GLOBALS['wgUseDatabaseMessages'] = false;
// Some of the environment checks make shell requests, remove limits
$GLOBALS['wgMaxShellMemory'] = 0;
+ // Override the default CookieSessionProvider with a dummy
+ // implementation that won't stomp on PHP's cookies.
$GLOBALS['wgSessionProviders'] = [
[
'class' => 'InstallerSessionProvider',
] ]
]
];
+
+ // Don't try to use any object cache for SessionManager either.
+ $GLOBALS['wgSessionCacheType'] = CACHE_NONE;
}
/**
protected function doGet( $key, $flags = 0 ) {
$ret = parent::doGet( $key, $flags );
- if ( $ret === false ) {
+ if ( $ret === false && !$this->hasKey( $key ) ) {
$ret = $this->backend->doGet( $key, $flags );
- if ( $ret !== false ) {
- $this->set( $key, $ret, 0, self::WRITE_CACHE_ONLY );
- }
+ $this->set( $key, $ret, 0, self::WRITE_CACHE_ONLY );
}
return $ret;
}
return true;
}
+ /**
+ * Does this bag have a non-null value for the given key?
+ *
+ * @param string $key
+ * @return bool
+ * @since 1.27
+ */
+ protected function hasKey( $key ) {
+ return isset( $this->bag[$key] );
+ }
+
protected function doGet( $key, $flags = 0 ) {
- if ( !isset( $this->bag[$key] ) ) {
+ if ( !$this->hasKey( $key ) ) {
return false;
}
return;
}
+ if ( defined( 'MW_NO_SESSION_HANDLER' ) ) {
+ throw new \BadMethodCallException( 'MW_NO_SESSION_HANDLER is defined' );
+ }
+
self::$instance = new self( $manager );
// Close any auto-started session, before we replace it
* @return Session
*/
public function getSessionFromInfo( SessionInfo $info, WebRequest $request ) {
+ if ( defined( 'MW_NO_SESSION' ) ) {
+ if ( MW_NO_SESSION === 'warn' ) {
+ // Undocumented safety case for converting existing entry points
+ $this->logger->error( 'Sessions are supposed to be disabled for this entry point' );
+ } else {
+ throw new \BadMethodCallException( 'Sessions are disabled for this entry point' );
+ }
+ }
+
$id = $info->getId();
if ( !isset( $this->allSessionBackends[$id] ) ) {
function doBatchLookups() {
# Do a link batch query
$this->mResult->seek( 0 );
- $revIds = [];
+ $parentRevIds = [];
+ $this->mParentLens = [];
$batch = new LinkBatch();
# Give some pointers to make (last) links
foreach ( $this->mResult as $row ) {
if ( isset( $row->rev_parent_id ) && $row->rev_parent_id ) {
- $revIds[] = $row->rev_parent_id;
+ $parentRevIds[] = $row->rev_parent_id;
}
if ( isset( $row->rev_id ) ) {
+ $this->mParentLens[$row->rev_id] = $row->rev_len;
if ( $this->contribs === 'newbie' ) { // multiple users
$batch->add( NS_USER, $row->user_name );
$batch->add( NS_USER_TALK, $row->user_name );
$batch->add( $row->page_namespace, $row->page_title );
}
}
- $this->mParentLens = Revision::getParentLengths( $this->mDbSecondary, $revIds );
+ # Fetch rev_len for revisions not already scanned above
+ $this->mParentLens += Revision::getParentLengths(
+ $this->mDbSecondary,
+ array_diff( $parentRevIds, array_keys( $this->mParentLens ) )
+ );
$batch->execute();
$this->mResult->seek( 0 );
}
) . "\n";
}
- // Select: All, None, Invert
- $links = [];
- $links[] = Html::element(
- 'a', [ 'href' => '#', 'class' => 'mw-checkbox-all' ],
- $this->msg( 'checkbox-all' )->text()
- );
- $links[] = Html::element(
- 'a', [ 'href' => '#', 'class' => 'mw-checkbox-none' ],
- $this->msg( 'checkbox-none' )->text()
- );
- $links[] = Html::element(
- 'a', [ 'href' => '#', 'class' => 'mw-checkbox-invert' ],
- $this->msg( 'checkbox-invert' )->text()
- );
-
- $buttons .= Html::rawElement( 'p',
- [
- 'class' => "mw-checkbox-toggle-controls"
- ],
- $this->msg( 'checkbox-select' )
- ->rawParams( $this->getLanguage()->commaList( $links ) )->escaped()
- );
-
- $this->getOutput()->addModules( 'mediawiki.checkboxtoggle' );
- $this->getOutput()->addModuleStyles( 'mediawiki.checkboxtoggle.styles' );
+ $buttons .= ( new ListToggle( $this->getOutput() ) )->getHTML();
$s .= $buttons . $formcontents . $buttons;
$s .= Html::closeElement( 'form' );
$this->mOptionOverrides = null;
$this->mOptionsLoaded = false;
- $loggedOut = $this->mRequest ? $this->mRequest->getSession()->getLoggedOutTimestamp() : 0;
+ $loggedOut = $this->mRequest && !defined( 'MW_NO_SESSION' )
+ ? $this->mRequest->getSession()->getLoggedOutTimestamp() : 0;
if ( $loggedOut !== 0 ) {
$this->mTouched = wfTimestamp( TS_MW, $loggedOut );
} else {
if ( is_null( $this->mRights ) ) {
$this->mRights = self::getGroupPermissions( $this->getEffectiveGroups() );
- $allowedRights = $this->getRequest()->getSession()->getAllowedUserRights();
- if ( $allowedRights !== null ) {
- $this->mRights = array_intersect( $this->mRights, $allowedRights );
+ // Deny any rights denied by the user's session, unless this
+ // endpoint has no sessions.
+ if ( !defined( 'MW_NO_SESSION' ) ) {
+ $allowedRights = $this->getRequest()->getSession()->getAllowedUserRights();
+ if ( $allowedRights !== null ) {
+ $this->mRights = array_intersect( $this->mRights, $allowedRights );
+ }
}
Hooks::run( 'UserGetRights', [ $this, &$this->mRights ] );
}
}
- // Remove any rights that aren't allowed to the global-session user
- $allowedRights = SessionManager::getGlobalSession()->getAllowedUserRights();
- if ( $allowedRights !== null && !in_array( $right, $allowedRights, true ) ) {
- $cache[$right] = false;
- return false;
+ // Remove any rights that aren't allowed to the global-session user,
+ // unless there are no sessions for this endpoint.
+ if ( !defined( 'MW_NO_SESSION' ) ) {
+ $allowedRights = SessionManager::getGlobalSession()->getAllowedUserRights();
+ if ( $allowedRights !== null && !in_array( $right, $allowedRights, true ) ) {
+ $cache[$right] = false;
+ return false;
+ }
}
// Allow extensions to say false
"right-changetags": "дадаваць і выдаляць адвольныя [[Special:Tags|меткі]] да асобных вэрсіяў і запісаў у журнале падзеяў",
"grant-generic": "Набор правоў «$1»",
"grant-group-page-interaction": "Узаемадзеньне з старонкамі",
+ "grant-group-file-interaction": "Узаемадзеяньне з мэдыяфайламі",
"grant-createaccount": "Стварыць рахункі",
"grant-createeditmovepage": "Ствараць, рэдагаваць і пераносіць старонкі",
"grant-delete": "Выдаляць старонкі, вэрсіі і запісы журналу",
"passwordreset-emailsent-capture": "Ніжэй прыведзены адпраўлены ліст пра скід пароля.",
"passwordreset-emailerror-capture": "Ніжэй прыведзены створаны ліст пра скід пароля, яго адпраўка не атрымалася па прычыне: $1",
"changeemail": "Змяніць або выдаліць адрас электроннай пошты",
- "changeemail-header": "Змена электроннага адрасу акаўнта",
+ "changeemail-header": "Запоўніце гэтую форму, каб змяніць свой адрас электроннай пошты. Калі хочаце выдаліць адрас электроннай пошты, злучаны з вашым уліковым запісам, пакіньце поле новага адраса электроннай пошты пустым пры адпраўцы формы.",
+ "changeemail-passwordrequired": "Вам трэба будзе ўвесці свой пароль, каб пацвердзіць гэта змяненне.",
"changeemail-no-info": "Каб звяртацца непасрэдна да гэтай старонкі, вам варта прадставіцца сістэме.",
"changeemail-oldemail": "Бягучы адрас электроннай пошты:",
"changeemail-newemail": "Новы адрас электроннай пошты:",
+ "changeemail-newemail-help": "Гэта поле павінна застацца пустым, калі вы хочаце выдаліць свой адрас электроннай пошты. Вы не зможаце скінуць забыты пароль і не будзеце атрымліваць лісты з гэтай Вікі пасля выдалення.",
"changeemail-none": "(няма)",
"changeemail-password": "Ваш {{SITENAME}} пароль:",
"changeemail-submit": "Змяніць адрас электроннай пошты:",
"changeemail-throttled": "Надта многа спроб увайсці пад гэтым рахункам. Пачакайце $1 перад тым, як спрабаваць ізноў.",
+ "changeemail-nochange": "Калі ласка, увядзіце іншы адрас электроннай пошты.",
"resettokens": "Скінуць токены",
"resettokens-text": "Вы можаце пераўстанавіць токены, якія дазваляюць атрымліваць доступ да пэўных прыватных звестак, звязаных з вашым уліковым запісам.\n\nВы мусіце скінуць токены, калі выпадкова падзяліліся імі з кім-небудзь, ці ваш уліковы запіс быў скампраметаваны.",
"resettokens-no-tokens": "Няма чаго скідваць.",
"Դավիթ Սարոյան",
"Beko",
"Vahe Gharakhanyan",
- "Aram1985"
+ "Aram1985",
+ "KeepingCalm"
]
},
"tog-underline": "ընդգծել հղումները՝",
"actionthrottledtext": "Որպես հակա-սպամային միջոց, այս գործողության չափից շատ կատարումը կարճ ժամանակահատվածի ընթացքում սահմանափակված է։ Խնդրում ենք փորձել կրկին մի քանի րոպե անց։",
"protectedpagetext": "Այս էջը պաշտպանված է խմբագումներից։",
"viewsourcetext": "Դուք կարող եք դիտել և պատճենել այս էջի ելատեքստը.",
- "viewyourtext": "Դուք կարող եք դիտել «ձեր ներդրումների» աղբյուրը և պատճենել այս էջ",
+ "viewyourtext": "Դուք կարող եք դիտել և պատճենել <strong>ձեր խմբագրումների</strong> ելատեքստը այդ էջում։",
"protectedinterface": "Այս էջը պարունակում է ծրագրային ապահովման միջերեսի տեքստ, և պաշտպանված է չարաշահումների կանխարգելման նպատակով։\nԲոլոր վիքիների թարգմանությունները փոփոխելու կամ ավելացնելու համար, խնդրում ենք այցելել ՄեդիաՎիքիի տեղայնացման նախագիծը՝ [//translatewiki.net/ translatewiki.net]։",
"editinginterface": "'''Ուշադրություն՝''' Դուք խմբագրում եք ծրագրային ապահովման միջերեսի տեքստ պարունակող էջ։\nԱյս էջի փոփոխությունը կանդրադառնա այլ մասնակիցներին տեսանելի միջերեսը այս վիքիի վրա։\nԹարգմանությունների համար նախընտրելի է օգտագործել [//translatewiki.net/wiki/Main_Page?setlang=hy translatewiki.net]՝ Մեդիավիքի ծրագրի տեղայնացման նախագիծը։",
- "cascadeprotected": "Ô±ÕµÕ½ Õ§Õ»Õ¨ ÕºÕ¡Õ·Õ¿ÕºÕ¡Õ¶Õ¾Õ¡Õ® Õ§ ÕÕ´Õ¢Õ¡Õ£Ö\80Õ¸Ö\82Õ´Õ«Ö\81, Ö\84Õ¡Õ¶Õ« Õ¸Ö\80 Õ¨Õ¶Õ¤Õ£Ö\80Õ¯Õ¾Õ¡Õ® Õ§ Õ°Õ¥Õ¿Ö\87ÕµÕ¡Õ¬ {{PLURAL:$1|Õ§Õ»Õ«|Õ§Õ»Õ¥Ö\80Õ«}} Õ¿Õ¥Ö\84Õ½Õ¿Õ¸Ö\82Õ´, {{PLURAL:$1|Õ¸Ö\80Õ¨|Õ¸Ö\80Õ¸Õ¶Ö\84}} ÕºÕ¡Õ·Õ¿ÕºÕ¡Õ¶Õ¾Õ¥Õ¬ {{PLURAL:$1|Õ§|Õ¥Õ¶}} Õ¯Õ¡Õ½Õ¯Õ¡Õ¤Õ¡ÕµÕ«Õ¶ Õ°Õ¶Õ¡Ö\80Õ¡Õ¾Õ¸Ö\80ությամբ.\n$2",
+ "cascadeprotected": "Ô±ÕµÕ½ Õ§Õ»Õ¨ ÕºÕ¡Õ·Õ¿ÕºÕ¡Õ¶Õ¾Õ¡Õ® Õ§ ÕÕ´Õ¢Õ¡Õ£Ö\80Õ¸Ö\82Õ´Õ«Ö\81, Ö\84Õ¡Õ¶Õ« Õ¸Ö\80 Õ¨Õ¶Õ¤Õ£Ö\80Õ¯Õ¾Õ¡Õ® Õ§ Õ°Õ¥Õ¿Ö\87ÕµÕ¡Õ¬ {{PLURAL:$1|Õ§Õ»Õ«|Õ§Õ»Õ¥Ö\80Õ«}} Õ¿Õ¥Ö\84Õ½Õ¿Õ¸Ö\82Õ´, {{PLURAL:$1|Õ¸Ö\80Õ¨|Õ¸Ö\80Õ¸Õ¶Ö\84}} ÕºÕ¡Õ·Õ¿ÕºÕ¡Õ¶Õ¾Õ¥Õ¬ {{PLURAL:$1|Õ§|Õ¥Õ¶}} Õ¯Õ¡Õ½Õ¯Õ¡Õ¤Õ¡ÕµÕ«Õ¶ ÕºÕ¡Õ·Õ¿ÕºÕ¡Õ¶ությամբ.\n$2",
"namespaceprotected": "Դուք չունեք «$1» անվանատարածքի էջերի խմբագրման իրավունք։",
"customcssprotected": "Դուք չեք կարող խմբագրել այս CSS էջը, քանի որ այն պարունակում է այլ մասնակցի անձնական նախընտրանքներ։",
"customjsprotected": "Դուք չեք կարող խմբագրել այս ՋավաՍկրիպտ էջը, քանի որ այն պարունակում է այլ մասնակցի անձնական նախընտրանքներ։",
"import-nonewrevisions": "Nisciuna verziona mpurtata (Tutt' 'e verziune so' state già mpurtate o pure zumpajeno pe' bbia 'e cocch'errore).",
"xml-error-string": "$1 a 'a linea $2, culonne $3 (byte $4): $5",
"import-upload": "Carreca 'e date 'e XML",
- "import-token-mismatch": "Se so' perdut' 'e date d' 'a sessione.\nProva n'ata vota.",
+ "import-token-mismatch": "Se so' perdut' 'e date d' 'a sessione.\n\nPuò darse ca site asciuto/a. <strong>Pe' piacere cuntrullate si site ancora dinto e tentate n'ata vota</strong>.\n\nSi chesto nun funziunasse ancora, tentate 'e ve n'[[Special:UserLogout|ascì]] e trasì n'ata vota dinto, cuntrullate si 'o navigatore vuosto premmettesse 'e cookies 'e stu sito.",
"import-invalid-interwiki": "Nun se può mpurtà d' 'a wiki specificata.",
"import-error-edit": "'A paggena \"$1\" nun è stata mpurtata pecché nun avite 'o permesso p' 'a putè cagnà.",
"import-error-create": "'A paggena \"$1\" nun è stata mpurtata pecché nun avite 'o permesso p' 'a putè crià.",
"expand_templates_generate_xml": "Fà vedè l'arvero 'e l'analisi XML",
"expand_templates_generate_rawhtml": "Fà verè 'o codece HTML 'n cruro",
"expand_templates_preview": "Anteprimma",
- "expand_templates_preview_fail_html": "<em>Siccomme {{SITENAME}} téne 'o HTML 'ncruro appicciato e se songhe spierze 'e date d' 'a sessiona, 'a previsualizzaziona s'è annascunnuta comm'a na prutezione annanz'e uerre 'e JavaScript.</em>\n\n<strong>Si chist'è nu tentativo giustificato 'e previsualizzaziona, pe' piacere facite n'ata vota.</strong>\nSi nun funziona ancora, facite d'[[Special:UserLogout|ascì]] e trasì n'ata vota.",
+ "expand_templates_preview_fail_html": "<em>Siccomme {{SITENAME}} téne 'o HTML 'ncruro appicciato e se songhe spierze 'e date d' 'a sessiona, 'a previsualizzaziona s'è annascunnuta comm'a na prutezione annanz'e uerre 'e JavaScript.</em>\n\n<strong>Si chist'è nu tentativo giustificato 'e previsualizzaziona, pe' piacere facite n'ata vota.</strong>\nSi nun funziona ancora, facite d'[[Special:UserLogout|ascì]] e trasì n'ata vota.\n\nSi chesto nun funziunasse ancora, tentate 'e ve n'[[Special:UserLogout|ascì]] e trasì n'ata vota dinto, cuntrullate si 'o navigatore vuosto premmettesse 'e cookies 'e stu sito.",
"expand_templates_preview_fail_html_anon": "<em>Siccomme {{SITENAME}} téne 'o HTML 'ncruro e vuje nun site trasute 'o sito, 'a previsualizzaziona s'è annascunnuta comm'a na prutezione annanz'e uerre 'e JavaScript.</em>\n\n<strong>Si chist'è nu tentativo giustificato 'e previsualizzaziona, pe' piacere facite d'[[Special:UserLogout|ascì]] e trasì n'ata vota.</strong>",
"expand_templates_input_missing": "Avita dà minimo nu poco 'e testo scritto.",
"pagelanguage": "Cagna 'o nomme d' 'a paggena",
"tog-hideminor": "Amagar los cambiaments menors dins los darrièrs cambiaments",
"tog-hidepatrolled": "Amagar las modificacions susvelhadas dins los darrièrs cambiaments",
"tog-newpageshidepatrolled": "Amagar las paginas susvelhadas de la lista de las paginas novèlas",
+ "tog-hidecategorization": "Amagar la categorizacion de las paginas",
"tog-extendwatchlist": "Espandir la lista de seguiment per afichar totas las modificacions e non pas solament las mai recentas",
"tog-usenewrc": "Agropar los cambiaments per pagina dins los darrièrs cambiaments e la lista de seguiment",
"tog-numberheadings": "Numerotar automaticament los títols",
"october-date": "$1 d'octobre",
"november-date": "$1 de novembre",
"december-date": "$1 de decembre",
+ "period-am": "AM",
+ "period-pm": "PM",
"pagecategories": "{{PLURAL:$1|Categoria|Categorias}}",
"category_header": "Articles dins la categoria « $1 »",
"subcategories": "Soscategorias",
"actions": "Accions",
"namespaces": "Espacis de noms",
"variants": "Variantas",
- "navigation-heading": "Menú de navigacion",
+ "navigation-heading": "Menut de navigacion",
"errorpagetitle": "Error de títol",
"returnto": "Tornar a la pagina $1.",
"tagline": "Un article de {{SITENAME}}.",
"viewsourceold": "veire la font",
"editlink": "modificar",
"viewsourcelink": "veire la font",
- "editsectionhint": "Modificar la seccion : $1",
+ "editsectionhint": "Modificar la seccion: $1",
"toc": "Somari",
"showtoc": "afichar",
"hidetoc": "amagar",
"virus-scanfailed": "Fracàs de la recèrca (còde $1)",
"virus-unknownscanner": "antivirús desconegut :",
"logouttext": "'''Ara, sètz desconnectat.'''\n\nNotatz que d'unas paginas pòdon èsser encara afichadas coma s'eratz encara connectat, fins al moment qu'escafaretz l'escondedor de vòstre navigador.",
+ "cannotlogoutnow-title": "Impossible de se desconnectar ara",
"welcomeuser": "Benvenguda, $1 !",
"welcomecreation-msg": "Vòstre compte d'utilizaire es estat creat.\nDoblidetz pas de modificar [[Special:Preferences|vòstras preferéncias per {{SITENAME}}]].",
"yourname": "Nom d'utilizaire :",
"remembermypassword": "Me reconnectar automaticament a las visitas venentas (al maximum $1 {{PLURAL:$1|jorn|jorns}})",
"userlogin-remembermypassword": "Gardar ma sesilha activa",
"userlogin-signwithsecure": "Utilizar una connexion securizada",
+ "cannotloginnow-title": "Impossible de se connectar ara",
"yourdomainname": "Vòstre domeni",
"password-change-forbidden": "Podètz pas modificar los senhals sus aqueste wiki.",
"externaldberror": "Siá una error s’es producha amb la banca de donadas d’autentificacion extèrna, siá sètz pas autorizat a metre a jorn vòstre compte extèrne.",
"apisandbox-examples": "Exemples",
"apisandbox-results": "Resultats",
"apisandbox-request-url-label": "Requèsta URL :",
- "apisandbox-request-time": "Durada de la demanda : {{PLURAL:$1|$1 ms}}",
+ "apisandbox-request-time": "Durada de la demanda: {{PLURAL:$1|$1 ms}}",
"booksources": "Obratges de referéncia",
"booksources-search-legend": "Recercar demest d'obratges de referéncia",
"booksources-isbn": "ISBN :",
"cant-move-user-page": "Avètz pas la permission de renomenar las paginas principalas d'utilizaires.",
"cant-move-to-user-page": "Avètz pas la permission de tornar nomenar una pagina cap a una pagina d'utilizaire (a l'excepcion d'una sospagina).",
"cant-move-category-page": "Avètz pas la permission de renomenar las paginas de categorias.",
- "newtitle": "Títol novèl :",
+ "newtitle": "Títol novèl:",
"move-watch": "Seguir aquesta pagina",
"movepagebtn": "Tornar nomenar l'article",
"pagemovedsub": "Cambiament de nom capitat",
"special-characters-group-greek": "Grèc",
"special-characters-group-cyrillic": "Cirillic",
"special-characters-group-arabic": "Arabi",
- "special-characters-group-arabicextended": "arabi espandit",
+ "special-characters-group-arabicextended": "Arabi espandit",
"special-characters-group-persian": "Pèrse",
"special-characters-group-hebrew": "Ebrieu",
"special-characters-group-bangla": "Bengali",
"allmessages-filter-translate": "Kiännä",
"thumbnail-more": "Suurendua",
"filemissing": "Failua ei ole",
- "tooltip-pt-userpage": "Sinun käyttäisivu",
- "tooltip-pt-mytalk": "Sinun paginsivu",
- "tooltip-pt-preferences": "Sinun azetukset",
+ "tooltip-pt-userpage": "{{GENDER:|}} käyttäisivu",
+ "tooltip-pt-mytalk": "{{GENDER:|}} paginsivu",
+ "tooltip-pt-preferences": "{{GENDER:|}} azetukset",
"tooltip-pt-watchlist": "Listu sivulois kudamien muutoksii valvot",
- "tooltip-pt-mycontris": "Luvettelo sinun kirjutuksis",
+ "tooltip-pt-mycontris": "Luvettelo {{GENDER:|}} kirjutuksis",
"tooltip-pt-login": "Täs voibi registriiruijakseh, ga se ei ole vältämätöi",
"tooltip-pt-logout": "Kirjuttai ullos",
"tooltip-pt-createaccount": "Voit registriiruijakseh da kirjuttuakseh järjestelmäh, ga se ei ole vältämätöi",
"tooltip-t-whatlinkshere": "Kaikkien sivuloin luvettelo, kudamis on linki täh sivuh",
"tooltip-t-recentchangeslinked": "Jälgimäzet muutokset sivuloil, kudamile on linki täl sivul",
"tooltip-feed-atom": "Atom-syöttö täh sivuh",
- "tooltip-t-contributions": "Listu tämän käyttäjän kirjutuksis",
+ "tooltip-t-contributions": "Listu tämän käyttäjän kirjutuksis {{GENDER:$1|this user}}",
"tooltip-t-upload": "Ližiä tiijostot",
"tooltip-t-specialpages": "Kaikkien erikozien sivuloin luvettelo",
"tooltip-t-print": "Tämän sivun tulostettavu versii",
"previewnote": "<strong>To jest tylko podgląd.</strong>\nZmiany nie zostały jeszcze zapisane!",
"continue-editing": "Przejdź do pola edycji",
"previewconflict": "Podgląd odnosi się do tekstu z górnego pola edycji. Tak będzie wyglądać strona, jeśli zdecydujesz się ją zapisać.",
- "session_fail_preview": "'''Uwaga! Serwer nie może przetworzyć tej edycji z powodu utraty danych sesji.\nSpróbuj jeszcze raz.\nJeśli to nie pomoże – [[Special:UserLogout|wyloguj się]] i zaloguj ponownie.'''",
- "session_fail_preview_html": "'''Uwaga! Serwer nie może przetworzyć tej edycji z powodu utraty danych sesji.'''\n\n''Ponieważ w {{GRAMMAR:MS.lp|{{SITENAME}}}} włączona została opcja „surowy HTML”, podgląd został ukryty w celu zabezpieczenia przed atakami z użyciem JavaScriptu.''\n\n'''Jeśli jest to uprawniona próba dokonania edycji, spróbuj jeszcze raz.\nJeśli to nie pomoże – [[Special:UserLogout|wyloguj się]] i zaloguj ponownie.'''",
+ "session_fail_preview": "Uwaga! Serwer nie może przetworzyć tej edycji z powodu utraty danych sesji.\n\nByć może doszło do wylogowania. <strong>Proszę upewnij się, czy nadal jesteś zalogowany (zalogowana) i wtedy spróbuj ponownie.</strong>\nJeśli to nie pomoże – spróbuj [[Special:UserLogout|wylogować się]] i zalogować ponownie, a także upewnij się, że twoja przeglądarka akceptuje ciasteczka z tej witryny.",
+ "session_fail_preview_html": "Uwaga! Serwer nie może przetworzyć tej edycji z powodu utraty danych sesji.\n\n<em>Ponieważ w {{GRAMMAR:MS.lp|{{SITENAME}}}} włączona została opcja „surowy HTML”, podgląd został ukryty w celu zabezpieczenia przed atakami z użyciem JavaScriptu.</em>\n\n<strong>Jeśli jest to uprawniona próba dokonania edycji, spróbuj jeszcze raz.<strong>\nJeśli to nie pomoże – spróbuj [[Special:UserLogout|wylogować się]] i zalogować ponownie, a także upewnij się, że twoja przeglądarka akceptuje ciasteczka z tej witryny.",
"token_suffix_mismatch": "'''Twoja edycja została odrzucona, ponieważ twój klient pomieszał znaki interpunkcyjne w żetonie edycyjnym.\nTwoja edycja została odrzucona by zapobiec zniszczeniu tekstu strony.\nTakie problemy zdarzają się w wypadku korzystania z wadliwych anonimowych sieciowych usług proxy.'''",
"edit_form_incomplete": "'''Niektóre informacje wprowadzone do formularza nie dotarły do serwera. Upewnij się, że wprowadzone dane nie uległy uszkodzeniu i spróbuj ponownie.'''",
"editing": "Edytujesz $1",
"grant-uploadfile": "Przesyłanie nowych plików",
"grant-basic": "Podstawowe uprawnienia",
"grant-viewdeleted": "Wyświetlanie usuniętych plików i stron",
- "grant-viewmywatchlist": "Zobacz listę obserwowanych",
+ "grant-viewmywatchlist": "Zobacz swoją listę obserwowanych",
"newuserlogpage": "Nowi użytkownicy",
"newuserlogpagetext": "To jest rejestr ostatnio utworzonych kont użytkowników",
"rightslog": "Uprawnienia",
"import-nonewrevisions": "Nie zaimportowano żadnych wersji (wszystkie były już obecne albo pominięte z powodu błędów).",
"xml-error-string": "$1 linia $2, kolumna $3 (bajt $4): $5",
"import-upload": "Prześlij dane w formacie XML",
- "import-token-mismatch": "Utracono dane sesji. Proszę spróbować ponownie.",
+ "import-token-mismatch": "Utracono dane sesji. \n\nByć może doszło do wylogowania. <strong>Proszę upewnij się, czy nadal jesteś zalogowany (zalogowana) i wtedy spróbuj ponownie.</strong>\nJeśli to nie pomoże – spróbuj [[Special:UserLogout|wylogować się]] i zalogować ponownie, a także upewnij się, że twoja przeglądarka akceptuje ciasteczka z tej witryny.",
"import-invalid-interwiki": "Nie można importować z podanej wiki.",
"import-error-edit": "Strona „$1” nie została zaimportowana, ponieważ nie jesteś uprawniony do jej edytowania.",
"import-error-create": "Strona „$1” nie została zaimportowana, ponieważ nie jesteś uprawniony do jej utworzenia.",
"lastmodifiedatby": "Ostatnia edycja tej strony: $2, $1 (autor zmian: $3)",
"othercontribs": "Inni autorzy: $1.",
"others": "inni",
- "siteusers": "{{PLURAL:$2|użytkownik|użytkownicy}} {{GRAMMAR:D.lp|{{SITENAME}}}}{{PLURAL:$2||:}} $1",
+ "siteusers": "{{PLURAL:$2|użytkownik |użytkownicy}}{{GRAMMAR:D.lp|{{SITENAME}}}}{{PLURAL:$2||:}} $1",
"anonusers": "{{PLURAL:$2|niezalogowany użytkownik|niezalogowani użytkownicy}} {{GRAMMAR:D.lp|{{SITENAME}}}}{{PLURAL:$2||:}} $1",
"creditspage": "Autorzy",
"nocredits": "Brak informacji o autorach tej strony.",
"version-libraries-license": "Licencja",
"version-libraries-description": "Opis",
"version-libraries-authors": "Autorzy",
- "redirect": "Przekierowanie według pliku, użytkownika, strony lub identyfikatora wersji",
+ "redirect": "Przekierowanie z identyfikatora pliku, użytkownika, strony, wersji lub wpisu rejestru",
"redirect-legend": "Przekieruj do pliku lub strony",
"redirect-summary": "Ta strona specjalna przekierowuje do: pliku (o podanej nazwie), do strony (o podanym numerze wersji lub identyfikatorze strony) albo do strony użytkownika (o podanym identyfikatorze numerycznym). Sposób użycia: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/revision/328429]] albo [[{{#Special:Redirect}}/user/101]].",
"redirect-submit": "Przejdź",
"userrights-user-editname": "Forneça um nome de usuário:",
"editusergroup": "Editar grupos {{GENDER:$1|do(a) usuário(a)}}",
"editinguser": "Modificando privilégios d{{GENDER:$1|o usuário|a usuária|o(a) usuário(a)}} <strong>[[User:$1|$1]]</Strong> $2",
- "userrights-editusergroup": "Editar grupos do usuário",
+ "userrights-editusergroup": "Editar grupos {{GENDER:$1|do usuário|da usuária|do(a) usuário(a)}}",
"saveusergroups": "Salvar grupos de{{GENDER:$1|usuário}}",
"userrights-groupsmember": "Membro de:",
"userrights-groupsmember-auto": "Membro implícito de:",
"mergehistory-fail-bad-timestamp": "Registo data/hora inválido",
"mergehistory-fail-invalid-source": "Página de origem inválida.",
"mergehistory-fail-invalid-dest": "Página de destino inválida.",
+ "mergehistory-fail-permission": "Permissões insuficientes para fundir os históricos.",
"mergehistory-fail-self-merge": "As páginas de origem e de destino não podem ser a mesma.",
"mergehistory-fail-toobig": "Não é possível fundir o histórico, já que um número de revisão(ões) acima do limite ($1 {{PLURAL:$1|revisão|revisões}}) seriam movidos.",
"mergehistory-no-source": "A página de origem $1 não existe.",
"userrights-user-editname": "Introduza um nome de utilizador:",
"editusergroup": "Editar grupos {{GENDER:$1|do utilizador|da utilizadora|do(a) utilizador(a)}}",
"editinguser": "A modificar os privilégios {{GENDER:$1|do utilizador|da utilizadora|do(a) utilizador(a)}} <strong>[[User:$1|$1]]</strong> $2",
- "userrights-editusergroup": "Editar grupos do utilizador",
+ "userrights-editusergroup": "Editar grupos {{GENDER:$1|do utilizador|da utilizadora|do(a) utilizador(a)}}",
"saveusergroups": "Gravar grupos {{GENDER:$1|do utilizador|da utilizadora|do(a) utilizador(a)}}",
"userrights-groupsmember": "Membro de:",
"userrights-groupsmember-auto": "Membro implícito de:",
"right-createpage": "Criar páginas (que não sejam páginas de discussão)",
"right-createtalk": "Criar páginas de discussão",
"right-createaccount": "Criar novas contas de utilizador",
+ "right-autocreateaccount": "Aceder ao sistema automaticamente com uma conta de usuario externa",
"right-minoredit": "Marcar edições como menores",
"right-move": "Mover páginas",
"right-move-subpages": "Mover páginas com as suas subpáginas",
"right-managechangetags": "Criar e eliminar [[Special:Tags|etiquetas]] da base de dados",
"right-applychangetags": "Aplicar [[Special:Tags|etiquetas]] juntamente com as alterações",
"right-changetags": "Adicionar ou remover [[Special:Tags|etiquetas]] arbitrárias em revisões e entradas de registo individuais",
- "grant-generic": "Pacote de direitos \"$1\"",
+ "grant-generic": "Conjunto de direitos \"$1\"",
"grant-group-page-interaction": "Interagir com páginas",
"grant-group-file-interaction": "Interagir com conteúdo multimédia",
"grant-group-watchlist-interaction": "Interagir com a sua lista de vigiados",
"sqlite-no-fts": "Shown on [[Special:Version]].\nParameters:\n* $1 - version",
"logentry-delete-delete": "{{Logentry|[[Special:Log/delete]]}}",
"logentry-delete-restore": "{{Logentry|[[Special:Log/delete]]}}",
- "logentry-delete-event": "{{Logentry|[[Special:Log/delete]]}}\n{{Logentryparam}}\n* $3 - the name of the log page inside parenthesis",
+ "logentry-delete-event": "{{Logentry|[[Special:Log/delete]]}}\n{{Logentryparam}}\n* $5 - count of affected log events",
"logentry-delete-revision": "{{Logentry|[[Special:Log/delete]]}}\n{{Logentryparam}}\n* $5 - the number of affected revisions of the page $3",
- "logentry-delete-event-legacy": "{{Logentry|[[Special:Log/delete]]}}\n* $3 - the name of the log page inside parenthesis",
+ "logentry-delete-event-legacy": "{{Logentry|[[Special:Log/delete]]}}",
"logentry-delete-revision-legacy": "{{Logentry|[[Special:Log/delete]]}}",
"logentry-suppress-delete": "{{Logentry}}\n\n'Hid' is a possible alternative to 'suppressed' in this message.",
- "logentry-suppress-event": "{{Logentry}}\n{{Logentryparam}}\n$3 is the name of the log page inside parenthesis",
+ "logentry-suppress-event": "{{Logentry}}\n{{Logentryparam}}\n* $5 - count of affected log events",
"logentry-suppress-revision": "{{Logentry}}\n{{Logentryparam}}\n* $5 - the number of affected revisions of the page $3.",
- "logentry-suppress-event-legacy": "{{Logentry}}\n$3 is the name of the log page inside parenthesis",
+ "logentry-suppress-event-legacy": "{{Logentry}}",
"logentry-suppress-revision-legacy": "{{Logentry}}",
"revdelete-content-hid": "Used on\n* {{msg-mw|logentry-delete-event}}\n* {{msg-mw|logentry-delete-revision}}\n* {{msg-mw|logentry-suppress-event}}\n* {{msg-mw|logentry-suppress-event}}",
"revdelete-summary-hid": "Used on\n* {{msg-mw|logentry-delete-event}}\n* {{msg-mw|logentry-delete-revision}}\n* {{msg-mw|logentry-suppress-event}}\n* {{msg-mw|logentry-suppress-event}}",
"pageinfo-robot-index": "İzin verilmiş",
"pageinfo-robot-noindex": "İzin verilmedi",
"pageinfo-watchers": "Sayfanın izleyici sayısı",
+ "pageinfo-visiting-watchers": "Son değişiklikleri görüntüleyen izleyici sayısı",
"pageinfo-few-watchers": "$1 {{PLURAL:$1|izleyiciden|izleyiciden}} az",
"pageinfo-redirects-name": "Bu sayfaya yönlendirme sayısı",
"pageinfo-redirects-value": "$1",
"previewnote": "<strong>Исегездә тотыгыз, бу алдан карау гына.</strong>\nТәзәтмәләрегез әлегә сакланмаган!",
"continue-editing": "Үзгәртүне дәвам итү",
"previewconflict": "Әлеге алдан карау битендә сакланачак текстның ничек күренәчәге күрсәтелә.",
- "session_fail_preview": "'''Кызганычка, сезнең сессия идентификаторыгыз югалды. Нәтиҗәдә сервер үзгәртүләрегезне кабул итә алмый.\nТагын бер тапкыр кабатлавыгыз сорала.\nБу хата тагын кабатланса, [[Special:UserLogout|чыгыгыз]] һәм яңадан керегез.'''",
+ "session_fail_preview": "Кызганычка каршы сессия барышы югалы, шуңа күрә без сезнең төзәтмәләрнегезне кабул итә алмадык.\n\nБәлки сез хисап язмагыздан чыккансыздыр. <strong>Зинһар, керүегез турында инаныгыз һәм тагын бер тапкыр кабатлап карагыз.</strong>\nӘгәрдә бу ысул ярдәм итмәсәс, системадан [[Special:UserLogout|чыгыгыз]] һәм яңадан керегез. Шулай ук сезгә браузерыгызның cookies файлларын кабул итүне тикшерүне карап чыгуны тәкъдим итәбез.",
"session_fail_preview_html": "'''Кызганычка, сезнең сессия турында мәгълүматлар югалды. Нәтиҗәдә сервер үзгәртүләрегезне кабул итә алмый.'''\n\n''{{SITENAME}} чиста HTML кулланырга рөхсәт итә, ә бу үз чиратында JavaScript-атакалар оештыру өчен кулланылырга мөмкин. Шул сәбәпле сезнең өчен алдан карау мөмкинлеге ябык.''\n\n'''Әгәр сез үзгәртүне яхшы ният белән башкарасыз икән, тагын бер тапкыр кабатлап карагыз. Хата кабатланса, сайттан [[Special:UserLogout|чыгыгыз]] һәм яңадан керегез.'''",
"token_suffix_mismatch": "'''Сезнең үзгәртү кабул ителмәде.'''\nСәбәбе: браузерыгыз үзгәртү өлкәсендәге пунктуацияне дөрес күрсәтми, нәтиҗәдә текст бозылырга мөмкин.\nМондый хаталар аноним web-проксилар кулланганда килеп чыгарга мөмкин.",
"edit_form_incomplete": "'''Төзәтү кырларының кайбер өлешләре серверга барып ирешмәде. Сезнең үзгәртүләр бозылмаганмы - игътибар белән тикшерегез һәм яңадан җибәреп карагыз.'''",
"october-date": "$1 tháng 10",
"november-date": "$1 tháng 11",
"december-date": "$1 tháng 12",
+ "period-am": "Sáng",
+ "period-pm": "Chiều",
"pagecategories": "{{PLURAL:$1}}Thể loại",
"category_header": "Các trang trong thể loại “$1”",
"subcategories": "Thể loại con",
"botpasswords-label-delete": "Xoá",
"botpasswords-label-resetpassword": "Đặt lại mật khẩu",
"botpasswords-label-grants": "Các quyền có liên quan:",
+ "botpasswords-insert-failed": "Không thể thêm tên bot \"$1\". Nó đã được thêm vào chưa?",
"botpasswords-created-title": "Mật khẩu bot đã được tạo",
+ "botpasswords-created-body": "Mật khẩu bot \"$1\" đã được tạo thành công.",
"botpasswords-updated-title": "Mật khẩu Bot đã được cập nhật",
+ "botpasswords-updated-body": "Mật khẩu bot \"$1\" đã được cập nhật thành công.",
"botpasswords-deleted-title": "Bot mật khẩu đã bị xóa",
+ "botpasswords-deleted-body": "Mật khẩu bot \"$1\" đã bị xóa.",
+ "botpasswords-no-provider": "BotPasswordsSessionProvider không có sẵn.",
"botpasswords-restriction-failed": "Mật khẩu bot giới hạn ngăn chặn đăng nhập này.",
"resetpass_forbidden": "Không được đổi mật khẩu",
"resetpass-no-info": "Bạn phải đăng nhập mới có thể truy cập trực tiếp trang này.",
"right-createpage": "Tạo trang (không phải trang thảo luận)",
"right-createtalk": "Tạo trang thảo luận",
"right-createaccount": "Mở tài khoản mới",
+ "right-autocreateaccount": "Tự động đăng nhập bằng một tài khoản người dùng bên ngoài",
"right-minoredit": "Đánh dấu sửa đổi nhỏ",
"right-move": "Di chuyển trang",
"right-move-subpages": "Di chuyển trang cùng với các trang con của nó",
"action-createpage": "tạo trang",
"action-createtalk": "tạo trang thảo luận",
"action-createaccount": "mở tài khoản này",
+ "action-autocreateaccount": "tự động tạo tài khoản người dùng bên ngoài này",
"action-history": "xem lịch sử của trang này",
"action-minoredit": "đánh dấu đây là sửa đổi nhỏ",
"action-move": "di chuyển trang này",
"apisandbox-api-disabled": "API đã bị vô hiệu hóa trên trang web này.",
"apisandbox-intro": "Trang này dùng để thử nghiệm với '''API dịch vụ Web của MediaWiki'''.\nHãy tra cứu [//www.mediawiki.org/wiki/API:Main_page tài liệu API] để biết chi tiết về cách sử dụng API. Ví dụ: [//www.mediawiki.org/wiki/API#A_simple_example lấy nội dung của Trang Chính]. Chọn một tác vụ để xem thêm ví dụ.\n\nLưu ý rằng, mặc dù đây là một chỗ thử, nhưng các tác vụ của bạn tại trang này có thể thực hiện các thay đổi trên wiki.",
"apisandbox-fullscreen": "Mở rộng bảng điều khiển",
+ "apisandbox-unfullscreen": "Hiển thị trang",
"apisandbox-submit": "Yêu cầu",
"apisandbox-reset": "Tẩy trống",
"apisandbox-retry": "Thử lại",
"apisandbox-loading": "Đang tải thông tin cho mô-đun API \"$1\"...",
"apisandbox-no-parameters": "Mô-đun API này không có thông số.",
+ "apisandbox-helpurls": "Các đường dẫn trợ giúp",
"apisandbox-examples": "Các ví dụ",
"apisandbox-dynamic-parameters": "Tham số bổ sung",
"apisandbox-dynamic-parameters-add-label": "Thêm tham số:",
"apisandbox-dynamic-parameters-add-placeholder": "Tên tham số",
"apisandbox-dynamic-error-exists": "Một tham số có tên \"$1\" đã tồn tại.",
+ "apisandbox-fetch-token": "Tự động điền token này",
"apisandbox-results": "Kết quả",
+ "apisandbox-sending-request": "Đang gửi yêu cầu API...",
"apisandbox-loading-results": "Nhận kết quả API...",
"apisandbox-results-error": "Một lỗi xuất hiện khi tải các đáp ứng truy vấn API: $1.",
"apisandbox-request-url-label": "URL của yêu cầu:",
- "apisandbox-request-time": "Thời gian xử lý: $1",
+ "apisandbox-request-time": "Thời gian yêu cầu: {{PLURAL:$1|$1 ms}}",
+ "apisandbox-alert-page": "Các miền trên Trang này là không hợp lệ.",
"booksources": "Nguồn sách",
"booksources-search-legend": "Tìm kiếm nguồn sách",
"booksources-search": "Tìm kiếm",
"log-title-wildcard": "Tìm các tựa trang bắt đầu bằng các chữ này",
"showhideselectedlogentries": "Thay đổi mức khả kiến của các mục nhật trình đã chọn",
"log-edit-tags": "Sửa đổi thẻ đánh dấu của các mục nhật trình đã chọn.",
+ "checkbox-select": "Chọn: $1",
+ "checkbox-all": "Tất cả",
+ "checkbox-none": "Không",
+ "checkbox-invert": "Đảo ngược",
"allpages": "Mọi trang",
"nextpage": "Trang sau ($1)",
"prevpage": "Trang trước ($1)",
"hebrew-calendar-m11": "Av",
"hebrew-calendar-m12": "Elul",
"signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|thảo luận]])",
+ "timezone-local": "Địa phương",
"duplicate-defaultsort": "Cảnh báo: Từ khóa xếp mặc định “$2” ghi đè từ khóa trước, “$1”.",
"duplicate-displaytitle": "<strong>Cảnh báo:</strong> Tên hiển thị “$2” ghi đè tên hiển thị “$1” bên trên.",
"invalid-indicator-name": "<strong>Lỗi:</strong> Không thể để trống thuộc tính <code>name</code> của cái chỉ trạng thái trang.",
"version-libraries-license": "Giấy phép",
"version-libraries-description": "Miêu tả",
"version-libraries-authors": "Tác giả",
- "redirect": "Đổi hướng đến tập tin, người dùng, trang, hoặc số phiên bản",
+ "redirect": "Đổi hướng đến tập tin, người dùng, trang, hoặc ID đăng nhập",
"redirect-legend": "Đổi hướng đến tập tin hoặc trang",
"redirect-summary": "Trang đặc biệt này đổi hướng đến một tập tin (theo tên tập tin được cho vào), trang (theo số phiên bản hoặc số trang được cho vào), hoặc trang cá nhân (theo số thành viên). Cách sử dụng: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]], hoặc [[{{#Special:Redirect}}/user/101]].",
"redirect-submit": "Đi",
"expand_templates_preview": "Xem trước",
"expand_templates_preview_fail_html": "<em>{{SITENAME}} cho phép mã nguồn HTML thô và dữ liệu phiên bị mất, nên bản xem trước bị ẩn để tránh tấn công JavaScript.</em>\n\n<strong>Nếu bạn thực sự muốn xem trước mã nguồn này, xin hãy thử lại nữa.</strong>\nNếu vẫn không được, hãy thử [[Special:UserLogout|đăng xuất]] rồi đăng nhập lại.",
"expand_templates_preview_fail_html_anon": "<em>{{SITENAME}} cho phép mã nguồn HTML thô và dữ liệu phiên bị mất, nên bản xem trước bị ẩn để tránh tấn công JavaScript.</em>\n\n<strong>Nếu bạn thực sự muốn xem trước mã nguồn này, xin hãy thử lại nữa.</strong>\nNếu vẫn không được, hãy [[Special:UserLogin|đăng nhập]] và thử lại lần nữa.",
- "pagelanguage": "Chọn ngôn ngữ trang",
+ "pagelanguage": "Thay đổi ngôn ngữ của trang",
"pagelang-name": "Trang",
"pagelang-language": "Ngôn ngữ",
"pagelang-use-default": "Sử dụng ngôn ngữ mặc định",
"mw-widgets-titleinput-description-new-page": "trang này chưa tồn tại",
"mw-widgets-titleinput-description-redirect": "đổi hướng đến $1",
"api-error-blacklisted": "Xin vui lòng chọn một tên khác miêu tả đầy đủ.",
+ "sessionprovider-nocookies": "Cookie có thể bị vô hiệu hóa. Đảm bảo bạn đã bật cookie và bắt đầu một lần nữa.",
"randomrootpage": "Trang gốc ngẫu nhiên"
}
"grunt-cli": "0.1.13",
"grunt-banana-checker": "0.4.0",
"grunt-contrib-copy": "0.8.2",
- "grunt-contrib-jshint": "0.12.0",
+ "grunt-contrib-jshint": "1.0.0",
"grunt-contrib-watch": "0.6.1",
"grunt-jscs": "2.7.0",
"grunt-jsonlint": "1.0.7",
--- /dev/null
+{
+ "@metadata": {
+ "authors": [
+ "Luuva"
+ ]
+ },
+ "ooui-dialog-message-accept": "Liáu-kái",
+ "ooui-dialog-message-reject": "Chhú-siau",
+ "ooui-dialog-process-error": "Ū mi̍h bô hó-sè",
+ "ooui-dialog-process-dismiss": "Koaiⁿ tiāu",
+ "ooui-dialog-process-retry": "Koh chhì khòaⁿ-māi",
+ "ooui-dialog-process-continue": "Kè-sio̍k",
+ "ooui-selectfile-button-select": "Soán-tek 1-ê tóng-àn",
+ "ooui-selectfile-not-supported": "Só͘ soán ê tóng-àn bô siū chi-chhî",
+ "ooui-selectfile-placeholder": "Iáu-bē soán tóng-àn",
+ "ooui-selectfile-dragdrop-placeholder": "Kā tóng-àn tàn chia"
+}
/*!
- * OOjs UI v0.15.4
+ * OOjs UI v0.16.0
* https://www.mediawiki.org/wiki/OOjs_UI
*
* Copyright 2011–2016 OOjs UI Team and other contributors.
* Released under the MIT license
* http://oojs.mit-license.org
*
- * Date: 2016-02-17T02:03:23Z
+ * Date: 2016-02-22T22:33:33Z
*/
( function ( OO ) {
/*!
- * OOjs UI v0.15.4
+ * OOjs UI v0.16.0
* https://www.mediawiki.org/wiki/OOjs_UI
*
* Copyright 2011–2016 OOjs UI Team and other contributors.
* Released under the MIT license
* http://oojs.mit-license.org
*
- * Date: 2016-02-17T02:03:27Z
+ * Date: 2016-02-22T22:33:37Z
*/
.oo-ui-element-hidden {
display: none !important;
.oo-ui-indicatorElement.oo-ui-indicatorElement-indicator {
opacity: 0.8;
}
+.oo-ui-labelElement .oo-ui-labelElement-label-highlight {
+ font-weight: bold;
+}
.oo-ui-pendingElement-pending {
background-image: /* @embed */ url(themes/apex/images/textures/pending.gif);
}
width: 100%;
max-width: 50em;
}
+.oo-ui-dropdownInputWidget .oo-ui-dropdownWidget,
+.oo-ui-dropdownInputWidget select {
+ display: block;
+}
.oo-ui-dropdownInputWidget select {
- display: inline-block;
width: 100%;
resize: none;
-webkit-box-sizing: border-box;
}
.oo-ui-textInputWidget input,
.oo-ui-textInputWidget textarea {
- display: inline-block;
+ display: block;
width: 100%;
resize: none;
-webkit-box-sizing: border-box;
}
.oo-ui-dropdownWidget-handle {
width: 100%;
- display: inline-block;
+ display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
cursor: default;
opacity: 0.2;
}
-.oo-ui-comboBoxInputWidget > .oo-ui-selectWidget {
- margin-top: -3px;
-}
/*!
- * OOjs UI v0.15.4
+ * OOjs UI v0.16.0
* https://www.mediawiki.org/wiki/OOjs_UI
*
* Copyright 2011–2016 OOjs UI Team and other contributors.
* Released under the MIT license
* http://oojs.mit-license.org
*
- * Date: 2016-02-17T02:03:27Z
+ * Date: 2016-02-22T22:33:37Z
*/
.oo-ui-element-hidden {
display: none !important;
background-position: center center;
background-repeat: no-repeat;
}
+.oo-ui-labelElement .oo-ui-labelElement-label-highlight {
+ font-weight: bold;
+}
.oo-ui-pendingElement-pending {
background-image: /* @embed */ url(themes/mediawiki/images/textures/pending.gif);
}
width: 100%;
max-width: 50em;
}
+.oo-ui-dropdownInputWidget .oo-ui-dropdownWidget,
+.oo-ui-dropdownInputWidget select {
+ display: block;
+}
.oo-ui-dropdownInputWidget select {
- display: inline-block;
width: 100%;
resize: none;
-webkit-box-sizing: border-box;
}
.oo-ui-textInputWidget input,
.oo-ui-textInputWidget textarea {
- display: inline-block;
+ display: block;
width: 100%;
resize: none;
-webkit-box-sizing: border-box;
}
.oo-ui-dropdownWidget-handle {
width: 100%;
- display: inline-block;
+ display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
.oo-ui-dropdownWidget.oo-ui-indicatorElement .oo-ui-dropdownWidget-handle .oo-ui-labelElement-label {
margin-right: 2em;
}
-.oo-ui-dropdownWidget .oo-ui-selectWidget {
- border-top-color: #ffffff;
-}
.oo-ui-comboBoxInputWidget {
display: inline-block;
position: relative;
/*!
- * OOjs UI v0.15.4
+ * OOjs UI v0.16.0
* https://www.mediawiki.org/wiki/OOjs_UI
*
* Copyright 2011–2016 OOjs UI Team and other contributors.
* Released under the MIT license
* http://oojs.mit-license.org
*
- * Date: 2016-02-17T02:03:23Z
+ * Date: 2016-02-22T22:33:33Z
*/
( function ( OO ) {
if ( immediate && !timeout ) {
func.apply( context, args );
}
- clearTimeout( timeout );
- timeout = setTimeout( later, wait );
+ if ( !timeout || wait ) {
+ clearTimeout( timeout );
+ timeout = setTimeout( later, wait );
+ }
};
};
* @param {HTMLElement} node
* @param {string} eventName
* @param {Function} handler
- * @deprecated
+ * @deprecated since 0.15.0
*/
OO.ui.addCaptureEventListener = function ( node, eventName, handler ) {
node.addEventListener( eventName, handler, true );
* @param {HTMLElement} node
* @param {string} eventName
* @param {Function} handler
- * @deprecated
+ * @deprecated since 0.15.0
*/
OO.ui.removeCaptureEventListener = function ( node, eventName, handler ) {
node.removeEventListener( eventName, handler, true );
* as a plaintext string, a jQuery selection of elements, or a function that will produce a string
* in the future. See the [OOjs UI documentation on MediaWiki] [2] for examples.
* [2]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Icons,_Indicators,_and_Labels#Labels
- * @cfg {boolean} [autoFitLabel=true] Fit the label to the width of the parent element.
- * The label will be truncated to fit if necessary.
*/
OO.ui.mixin.LabelElement = function OoUiMixinLabelElement( config ) {
// Configuration initialization
// Properties
this.$label = null;
this.label = null;
- this.autoFitLabel = config.autoFitLabel === undefined || !!config.autoFitLabel;
// Initialization
this.setLabel( config.label || this.constructor.static.label );
*/
OO.ui.mixin.LabelElement.static.label = null;
+/* Static methods */
+
+/**
+ * Highlight the first occurrence of the query in the given text
+ *
+ * @param {string} text Text
+ * @param {string} query Query to find
+ * @return {jQuery} Text with the first match of the query
+ * sub-string wrapped in highlighted span
+ */
+OO.ui.mixin.LabelElement.static.highlightQuery = function ( text, query ) {
+ var $result = $( '<span>' ),
+ offset = text.toLowerCase().indexOf( query.toLowerCase() );
+
+ if ( !query.length || offset === -1 ) {
+ return $result.text( text );
+ }
+ $result.append(
+ document.createTextNode( text.slice( 0, offset ) ),
+ $( '<span>' )
+ .addClass( 'oo-ui-labelElement-label-highlight' )
+ .text( text.slice( offset, offset + query.length ) ),
+ document.createTextNode( text.slice( offset + query.length ) )
+ );
+ return $result.contents();
+};
+
/* Methods */
/**
return this;
};
+/**
+ * Set the label as plain text with a highlighted query
+ *
+ * @param {string} text Text label to set
+ * @param {string} query Substring of text to highlight
+ * @chainable
+ */
+OO.ui.mixin.LabelElement.prototype.setHighlightedQuery = function ( text, query ) {
+ return this.setLabel( this.constructor.static.highlightQuery( text, query ) );
+};
+
/**
* Get the label.
*
* Fit the label.
*
* @chainable
+ * @deprecated since 0.16.0
*/
OO.ui.mixin.LabelElement.prototype.fitLabel = function () {
- if ( this.$label && this.$label.autoEllipsis && this.autoFitLabel ) {
- this.$label.autoEllipsis( { hasSpan: false, tooltip: true } );
- }
-
return this;
};
OO.ui.mixin.ClippableElement.call( this, $.extend( {}, config, { $clippable: this.$group } ) );
// Properties
- this.newItems = null;
this.autoHide = config.autoHide === undefined || !!config.autoHide;
this.filterFromInput = !!config.filterFromInput;
this.$input = config.$input ? config.$input : config.input ? config.input.$input : null;
* @inheritdoc
*/
OO.ui.MenuSelectWidget.prototype.addItems = function ( items, index ) {
- var i, len, item;
-
// Parent method
OO.ui.MenuSelectWidget.parent.prototype.addItems.call( this, items, index );
- // Auto-initialize
- if ( !this.newItems ) {
- this.newItems = [];
- }
-
- for ( i = 0, len = items.length; i < len; i++ ) {
- item = items[ i ];
- if ( this.isVisible() ) {
- // Defer fitting label until item has been attached
- item.fitLabel();
- } else {
- this.newItems.push( item );
- }
- }
-
// Reevaluate clipping
this.clip();
* @inheritdoc
*/
OO.ui.MenuSelectWidget.prototype.toggle = function ( visible ) {
- var i, len, change;
+ var change;
visible = ( visible === undefined ? !this.visible : !!visible ) && !!this.items.length;
change = visible !== this.isVisible();
this.bindKeyDownListener();
this.bindKeyPressListener();
- if ( this.newItems && this.newItems.length ) {
- for ( i = 0, len = this.newItems.length; i < len; i++ ) {
- this.newItems[ i ].fitLabel();
- }
- this.newItems = null;
- }
this.toggleClipping( true );
if ( this.getSelectedItem() ) {
/**
* Set the directionality of the input, either RTL (right-to-left) or LTR (left-to-right).
*
- * @deprecated since v0.13.1, use #setDir directly
+ * @deprecated since v0.13.1; use #setDir directly
* @param {boolean} isRTL Directionality is right-to-left
* @chainable
*/
* @protected
*/
OO.ui.CheckboxInputWidget.prototype.getInputElement = function () {
- return $( '<input type="checkbox" />' );
+ return $( '<input>' ).attr( 'type', 'checkbox' );
};
/**
if ( config.$input ) {
return config.$input.addClass( 'oo-ui-element-hidden' );
}
- return $( '<input type="hidden">' );
+ return $( '<input>' ).attr( 'type', 'hidden' );
};
/**
* @protected
*/
OO.ui.RadioInputWidget.prototype.getInputElement = function () {
- return $( '<input type="radio" />' );
+ return $( '<input>' ).attr( 'type', 'radio' );
};
/**
* @protected
*/
OO.ui.RadioSelectInputWidget.prototype.getInputElement = function () {
- return $( '<input type="hidden">' );
+ return $( '<input>' ).attr( 'type', 'hidden' );
};
/**
OO.ui.TextInputWidget.prototype.getInputElement = function ( config ) {
return config.multiline ?
$( '<textarea>' ) :
- $( '<input type="' + this.getSaneType( config ) + '" />' );
+ $( '<input>' ).attr( 'type', this.getSaneType( config ) );
};
/**
* This method returns a promise that resolves with a boolean `true` if the current value is
* considered valid according to the supplied {@link #validate validation pattern}.
*
- * @deprecated
+ * @deprecated since v0.12.3
* @return {jQuery.Promise} A promise that resolves to a boolean `true` if the value is valid.
*/
OO.ui.TextInputWidget.prototype.isValid = function () {
*/
OO.ui.TextInputWidget.prototype.setLabelPosition = function ( labelPosition ) {
this.labelPosition = labelPosition;
- this.updatePosition();
+ if ( this.label ) {
+ // If there is no label and we only change the position, #updatePosition is a no-op,
+ // but it takes really a lot of work to do nothing.
+ this.updatePosition();
+ }
return this;
};
/**
* @class
- * @deprecated Use OO.ui.ComboBoxInputWidget instead.
+ * @deprecated since 0.13.2; use OO.ui.ComboBoxInputWidget instead
*/
OO.ui.ComboBoxWidget = OO.ui.ComboBoxInputWidget;
/*!
- * OOjs UI v0.15.4
+ * OOjs UI v0.16.0
* https://www.mediawiki.org/wiki/OOjs_UI
*
* Copyright 2011–2016 OOjs UI Team and other contributors.
* Released under the MIT license
* http://oojs.mit-license.org
*
- * Date: 2016-02-17T02:03:23Z
+ * Date: 2016-02-22T22:33:33Z
*/
( function ( OO ) {
/*!
- * OOjs UI v0.15.4
+ * OOjs UI v0.16.0
* https://www.mediawiki.org/wiki/OOjs_UI
*
* Copyright 2011–2016 OOjs UI Team and other contributors.
* Released under the MIT license
* http://oojs.mit-license.org
*
- * Date: 2016-02-17T02:03:27Z
+ * Date: 2016-02-22T22:33:37Z
*/
.oo-ui-popupTool .oo-ui-popupWidget-popup,
.oo-ui-popupTool .oo-ui-popupWidget-anchor {
/*!
- * OOjs UI v0.15.4
+ * OOjs UI v0.16.0
* https://www.mediawiki.org/wiki/OOjs_UI
*
* Copyright 2011–2016 OOjs UI Team and other contributors.
* Released under the MIT license
* http://oojs.mit-license.org
*
- * Date: 2016-02-17T02:03:27Z
+ * Date: 2016-02-22T22:33:37Z
*/
.oo-ui-popupTool .oo-ui-popupWidget-popup,
.oo-ui-popupTool .oo-ui-popupWidget-anchor {
/*!
- * OOjs UI v0.15.4
+ * OOjs UI v0.16.0
* https://www.mediawiki.org/wiki/OOjs_UI
*
* Copyright 2011–2016 OOjs UI Team and other contributors.
* Released under the MIT license
* http://oojs.mit-license.org
*
- * Date: 2016-02-17T02:03:23Z
+ * Date: 2016-02-22T22:33:33Z
*/
( function ( OO ) {
/*!
- * OOjs UI v0.15.4
+ * OOjs UI v0.16.0
* https://www.mediawiki.org/wiki/OOjs_UI
*
* Copyright 2011–2016 OOjs UI Team and other contributors.
* Released under the MIT license
* http://oojs.mit-license.org
*
- * Date: 2016-02-17T02:03:27Z
+ * Date: 2016-02-22T22:33:37Z
*/
-.oo-ui-draggableElement {
- cursor: -webkit-grab -moz-grab, url(images/grab.cur), move;
+.oo-ui-draggableElement-handle.oo-ui-widget-enabled {
+ cursor: move;
+ cursor: url(images/grab.cur );
+ cursor: -webkit-grab;
+ cursor: -moz-grab;
+ cursor: grab;
+}
+.oo-ui-draggableElement-placeholder {
+ opacity: 0.2;
}
-.oo-ui-draggableElement-dragging {
- cursor: -webkit-grabbing -moz-grabbing, url(images/grabbing.cur), move;
- background: rgba(0, 0, 0, 0.2);
- opacity: 0.4;
+.oo-ui-draggableElement.oo-ui-widget-enabled:active {
+ cursor: move;
+ cursor: url(images/grabbing.cur );
+ cursor: -webkit-grabbing;
+ cursor: -moz-grabbing;
+ cursor: grabbing;
}
.oo-ui-draggableGroupElement-horizontal .oo-ui-draggableElement.oo-ui-optionWidget {
display: inline-block;
}
-.oo-ui-draggableGroupElement-placeholder {
- position: absolute;
- display: block;
- background: rgba(0, 0, 0, 0.4);
-}
.oo-ui-lookupElement > .oo-ui-menuSelectWidget {
z-index: 1;
width: 100%;
}
.oo-ui-capsuleMultiSelectWidget-handle {
width: 100%;
- display: inline-block;
+ display: block;
position: relative;
}
.oo-ui-capsuleMultiSelectWidget-content {
.oo-ui-capsuleMultiSelectWidget.oo-ui-widget-disabled .oo-ui-capsuleMultiSelectWidget-handle > .oo-ui-indicatorElement-indicator {
opacity: 0.2;
}
-.oo-ui-capsuleMultiSelectWidget .oo-ui-selectWidget {
- border-top-color: #ffffff;
-}
.oo-ui-capsuleItemWidget {
position: relative;
display: inline-block;
text-overflow: ellipsis;
overflow: hidden;
}
-.oo-ui-capsuleItemWidget .oo-ui-buttonElement {
- margin-top: -1.6em;
- padding-left: 0.3em;
-}
.oo-ui-capsuleItemWidget:focus {
outline: none;
border-color: #087ecc;
}
-.oo-ui-capsuleItemWidget.oo-ui-indicatorElement > .oo-ui-labelElement-label {
- padding-right: 1.3375em;
-}
-.oo-ui-capsuleItemWidget.oo-ui-indicatorElement > .oo-ui-indicatorElement-indicator {
- position: absolute;
- right: 0.4em;
- top: 0;
- width: 0.9375em;
- height: 100%;
- background-repeat: no-repeat;
-}
-.oo-ui-capsuleItemWidget.oo-ui-indicatorElement > .oo-ui-indicator-clear {
- cursor: pointer;
-}
.oo-ui-capsuleItemWidget.oo-ui-widget-disabled {
opacity: 0.5;
-webkit-transform: translate3d(0, 0, 0);
background: #eeeeee;
border-color: #cccccc;
}
-.oo-ui-capsuleItemWidget.oo-ui-widget-disabled > .oo-ui-indicatorElement-indicator {
- opacity: 0.2;
+.oo-ui-capsuleItemWidget > .oo-ui-buttonElement {
+ float: right;
+ margin-top: -0.1em;
+ margin-right: -0.4em;
}
.oo-ui-searchWidget-query {
position: absolute;
.oo-ui-numberInputWidget-field > .oo-ui-buttonWidget {
width: 2.25em;
}
-.oo-ui-numberInputWidget-minusButton.oo-ui-buttonElement-framed.oo-ui-widget-enabled > .oo-ui-buttonElement-button {
+.oo-ui-numberInputWidget-minusButton.oo-ui-buttonElement-framed > .oo-ui-buttonElement-button {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
border-right-width: 0;
}
-.oo-ui-numberInputWidget-plusButton.oo-ui-buttonElement-framed.oo-ui-widget-enabled > .oo-ui-buttonElement-button {
+.oo-ui-numberInputWidget-plusButton.oo-ui-buttonElement-framed > .oo-ui-buttonElement-button {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
border-left-width: 0;
/*!
- * OOjs UI v0.15.4
+ * OOjs UI v0.16.0
* https://www.mediawiki.org/wiki/OOjs_UI
*
* Copyright 2011–2016 OOjs UI Team and other contributors.
* Released under the MIT license
* http://oojs.mit-license.org
*
- * Date: 2016-02-17T02:03:27Z
+ * Date: 2016-02-22T22:33:37Z
*/
-.oo-ui-draggableElement {
- cursor: -webkit-grab -moz-grab, url(images/grab.cur), move;
+.oo-ui-draggableElement-handle.oo-ui-widget-enabled {
+ cursor: move;
+ cursor: url(images/grab.cur );
+ cursor: -webkit-grab;
+ cursor: -moz-grab;
+ cursor: grab;
+}
+.oo-ui-draggableElement-placeholder {
+ opacity: 0.2;
}
-.oo-ui-draggableElement-dragging {
- cursor: -webkit-grabbing -moz-grabbing, url(images/grabbing.cur), move;
- background: rgba(0, 0, 0, 0.2);
- opacity: 0.4;
+.oo-ui-draggableElement.oo-ui-widget-enabled:active {
+ cursor: move;
+ cursor: url(images/grabbing.cur );
+ cursor: -webkit-grabbing;
+ cursor: -moz-grabbing;
+ cursor: grabbing;
}
.oo-ui-draggableGroupElement-horizontal .oo-ui-draggableElement.oo-ui-optionWidget {
display: inline-block;
}
-.oo-ui-draggableGroupElement-placeholder {
- position: absolute;
- display: block;
- background: rgba(0, 0, 0, 0.4);
-}
.oo-ui-lookupElement > .oo-ui-menuSelectWidget {
z-index: 1;
width: 100%;
}
.oo-ui-capsuleMultiSelectWidget-handle {
width: 100%;
- display: inline-block;
+ display: block;
position: relative;
}
.oo-ui-capsuleMultiSelectWidget-content {
.oo-ui-capsuleMultiSelectWidget.oo-ui-widget-disabled .oo-ui-capsuleMultiSelectWidget-handle > .oo-ui-indicatorElement-indicator {
opacity: 0.2;
}
-.oo-ui-capsuleMultiSelectWidget .oo-ui-selectWidget {
- border-top-color: #ffffff;
-}
.oo-ui-capsuleItemWidget {
position: relative;
display: inline-block;
text-overflow: ellipsis;
overflow: hidden;
}
-.oo-ui-capsuleItemWidget .oo-ui-buttonElement {
- margin-top: -1.6em;
- padding-left: 0.3em;
-}
.oo-ui-capsuleItemWidget:focus {
outline: none;
border-color: #347bff;
}
-.oo-ui-capsuleItemWidget.oo-ui-indicatorElement > .oo-ui-labelElement-label {
- padding-right: 1.3375em;
-}
-.oo-ui-capsuleItemWidget.oo-ui-indicatorElement > .oo-ui-indicatorElement-indicator {
- position: absolute;
- right: 0.4em;
- top: 0;
- width: 0.9375em;
- height: 100%;
- background-repeat: no-repeat;
-}
-.oo-ui-capsuleItemWidget.oo-ui-indicatorElement > .oo-ui-indicator-clear {
- cursor: pointer;
-}
.oo-ui-capsuleItemWidget.oo-ui-widget-disabled {
color: #cccccc;
text-shadow: 0 1px 1px #ffffff;
border-color: #dddddd;
background-color: #f3f3f3;
}
-.oo-ui-capsuleItemWidget.oo-ui-widget-disabled > .oo-ui-indicatorElement-indicator {
- opacity: 0.2;
+.oo-ui-capsuleItemWidget > .oo-ui-buttonElement {
+ float: right;
+ margin-top: -0.2em;
+ margin-left: 0.4em;
}
.oo-ui-searchWidget-query {
position: absolute;
/*!
- * OOjs UI v0.15.4
+ * OOjs UI v0.16.0
* https://www.mediawiki.org/wiki/OOjs_UI
*
* Copyright 2011–2016 OOjs UI Team and other contributors.
* Released under the MIT license
* http://oojs.mit-license.org
*
- * Date: 2016-02-17T02:03:23Z
+ * Date: 2016-02-22T22:33:33Z
*/
( function ( OO ) {
* @class
*
* @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {jQuery} [$handle] The part of the element which can be used for dragging, defaults to the whole element
*/
-OO.ui.mixin.DraggableElement = function OoUiMixinDraggableElement() {
+OO.ui.mixin.DraggableElement = function OoUiMixinDraggableElement( config ) {
+ config = config || {};
+
// Properties
this.index = null;
+ this.$handle = config.$handle || this.$element;
+ this.wasHandleUsed = null;
// Initialize and events
- this.$element
+ this.$element.addClass( 'oo-ui-draggableElement' )
+ // We make the entire element draggable, not just the handle, so that
+ // the whole element appears to move. wasHandleUsed prevents drags from
+ // starting outside the handle
.attr( 'draggable', true )
- .addClass( 'oo-ui-draggableElement' )
.on( {
+ mousedown: this.onDragMouseDown.bind( this ),
dragstart: this.onDragStart.bind( this ),
dragover: this.onDragOver.bind( this ),
dragend: this.onDragEnd.bind( this ),
drop: this.onDrop.bind( this )
} );
+ this.$handle.addClass( 'oo-ui-draggableElement-handle' );
};
OO.initClass( OO.ui.mixin.DraggableElement );
/* Methods */
+/**
+ * Respond to mousedown event.
+ *
+ * @private
+ * @param {jQuery.Event} event jQuery event
+ */
+OO.ui.mixin.DraggableElement.prototype.onDragMouseDown = function ( e ) {
+ this.wasHandleUsed =
+ // Optimization: if the handle is the whole element this is always true
+ this.$handle[ 0 ] === this.$element[ 0 ] ||
+ // Check the mousedown occurred inside the handle
+ OO.ui.contains( this.$handle[ 0 ], e.target, true );
+};
+
/**
* Respond to dragstart event.
*
* @fires dragstart
*/
OO.ui.mixin.DraggableElement.prototype.onDragStart = function ( e ) {
- var dataTransfer = e.originalEvent.dataTransfer;
+ var element = this,
+ dataTransfer = e.originalEvent.dataTransfer;
+
+ if ( !this.wasHandleUsed ) {
+ return false;
+ }
+
// Define drop effect
dataTransfer.dropEffect = 'none';
dataTransfer.effectAllowed = 'move';
} catch ( err ) {
// The above is only for Firefox. Move on if it fails.
}
- // Add dragging class
- this.$element.addClass( 'oo-ui-draggableElement-dragging' );
+ // Briefly add a 'clone' class to style the browser's native drag image
+ this.$element.addClass( 'oo-ui-draggableElement-clone' );
+ // Add placeholder class after the browser has rendered the clone
+ setTimeout( function () {
+ element.$element
+ .removeClass( 'oo-ui-draggableElement-clone' )
+ .addClass( 'oo-ui-draggableElement-placeholder' );
+ } );
// Emit event
this.emit( 'dragstart', this );
return true;
* @fires dragend
*/
OO.ui.mixin.DraggableElement.prototype.onDragEnd = function () {
- this.$element.removeClass( 'oo-ui-draggableElement-dragging' );
+ this.$element.removeClass( 'oo-ui-draggableElement-placeholder' );
this.emit( 'dragend' );
};
// Properties
this.orientation = config.orientation || 'vertical';
this.dragItem = null;
- this.itemDragOver = null;
this.itemKeys = {};
- this.sideInsertion = '';
+ this.dir = null;
+ this.itemsOrder = null;
// Events
this.aggregate( {
} );
this.connect( this, {
itemDragStart: 'onItemDragStart',
- itemDrop: 'onItemDrop',
- itemDragEnd: 'onItemDragEnd'
- } );
- this.$element.on( {
- dragover: this.onDragOver.bind( this ),
- dragleave: this.onDragLeave.bind( this )
+ itemDrop: 'onItemDropOrDragEnd',
+ itemDragEnd: 'onItemDropOrDragEnd'
} );
// Initialize
if ( Array.isArray( config.items ) ) {
this.addItems( config.items );
}
- this.$placeholder = $( '<div>' )
- .addClass( 'oo-ui-draggableGroupElement-placeholder' );
this.$element
.addClass( 'oo-ui-draggableGroupElement' )
.append( this.$status )
- .toggleClass( 'oo-ui-draggableGroupElement-horizontal', this.orientation === 'horizontal' )
- .prepend( this.$placeholder );
+ .toggleClass( 'oo-ui-draggableGroupElement-horizontal', this.orientation === 'horizontal' );
};
/* Setup */
/* Events */
/**
- * A 'reorder' event is emitted when the order of items in the group changes.
+ * An item has been dragged to a new position, but not yet dropped.
+ *
+ * @event drag
+ * @param {OO.ui.mixin.DraggableElement} item Dragged item
+ * @param {number} [newIndex] New index for the item
+ */
+
+/**
+ * And item has been dropped at a new position.
*
* @event reorder
* @param {OO.ui.mixin.DraggableElement} item Reordered item
* @param {OO.ui.mixin.DraggableElement} item Dragged item
*/
OO.ui.mixin.DraggableGroupElement.prototype.onItemDragStart = function ( item ) {
- var i, len;
-
- // Map the index of each object
- for ( i = 0, len = this.items.length; i < len; i++ ) {
- this.items[ i ].setIndex( i );
- }
-
+ // Make a shallow copy of this.items so we can re-order it during previews
+ // without affecting the original array.
+ this.itemsOrder = this.items.slice();
+ this.updateIndexes();
if ( this.orientation === 'horizontal' ) {
- // Set the height of the indicator
- this.$placeholder.css( {
- height: item.$element.outerHeight(),
- width: 2
- } );
- } else {
- // Set the width of the indicator
- this.$placeholder.css( {
- height: 2,
- width: item.$element.outerWidth()
- } );
+ // Calculate and cache directionality on drag start - it's a little
+ // expensive and it shouldn't change while dragging.
+ this.dir = this.$element.css( 'direction' );
}
this.setDragItem( item );
};
/**
- * Respond to item drag end event
- *
- * @private
+ * Update the index properties of the items
*/
-OO.ui.mixin.DraggableGroupElement.prototype.onItemDragEnd = function () {
- this.unsetDragItem();
- return false;
+OO.ui.mixin.DraggableGroupElement.prototype.updateIndexes = function () {
+ var i, len;
+
+ // Map the index of each object
+ for ( i = 0, len = this.itemsOrder.length; i < len; i++ ) {
+ this.itemsOrder[ i ].setIndex( i );
+ }
};
/**
- * Handle drop event and switch the order of the items accordingly
+ * Handle drop or dragend event and switch the order of the items accordingly
*
* @private
* @param {OO.ui.mixin.DraggableElement} item Dropped item
- * @fires reorder
*/
-OO.ui.mixin.DraggableGroupElement.prototype.onItemDrop = function ( item ) {
- var toIndex = item.getIndex();
- // Check if the dropped item is from the current group
+OO.ui.mixin.DraggableGroupElement.prototype.onItemDropOrDragEnd = function () {
+ var targetIndex, originalIndex,
+ item = this.getDragItem();
+
// TODO: Figure out a way to configure a list of legally droppable
// elements even if they are not yet in the list
- if ( this.getDragItem() ) {
- // If the insertion point is 'after', the insertion index
- // is shifted to the right (or to the left in RTL, hence 'after')
- if ( this.sideInsertion === 'after' ) {
- toIndex++;
- }
- // Emit change event
- this.emit( 'reorder', this.getDragItem(), toIndex );
+ if ( item ) {
+ originalIndex = this.items.indexOf( item );
+ // If the item has moved forward, add one to the index to account for the left shift
+ targetIndex = item.getIndex() + ( item.getIndex() > originalIndex ? 1 : 0 );
+ this.reorder( this.getDragItem(), targetIndex );
+ this.emit( 'reorder', this.getDragItem(), targetIndex );
+ this.updateIndexes();
}
this.unsetDragItem();
// Return false to prevent propogation
return false;
};
-/**
- * Handle dragleave event.
- *
- * @private
- */
-OO.ui.mixin.DraggableGroupElement.prototype.onDragLeave = function () {
- // This means the item was dragged outside the widget
- this.$placeholder
- .css( 'left', 0 )
- .addClass( 'oo-ui-element-hidden' );
-};
-
/**
* Respond to dragover event
*
* @private
- * @param {jQuery.Event} event Event details
+ * @param {jQuery.Event} event Dragover event
+ * @fires reorder
*/
OO.ui.mixin.DraggableGroupElement.prototype.onDragOver = function ( e ) {
var dragOverObj, $optionWidget, itemOffset, itemMidpoint, itemBoundingRect,
- itemSize, cssOutput, dragPosition, itemIndex, itemPosition,
+ itemSize, cssOutput, dragPosition, overIndex, itemPosition, after,
+ targetIndex = null,
+ item = this.getDragItem(),
+ dragItemIndex = item.getIndex(),
clientX = e.originalEvent.clientX,
clientY = e.originalEvent.clientY;
itemOffset = $optionWidget.offset();
itemBoundingRect = $optionWidget[ 0 ].getBoundingClientRect();
itemPosition = $optionWidget.position();
- itemIndex = $optionWidget.data( 'index' );
+ overIndex = $optionWidget.data( 'index' );
}
if (
itemOffset &&
- this.isDragging() &&
- itemIndex !== this.getDragItem().getIndex()
+ overIndex !== dragItemIndex
) {
if ( this.orientation === 'horizontal' ) {
// Calculate where the mouse is relative to the item width
itemMidpoint = itemBoundingRect.left + itemSize / 2;
dragPosition = clientX;
// Which side of the item we hover over will dictate
- // where the placeholder will appear, on the left or
+ // where to drop the selected item, on the left or
// on the right
cssOutput = {
left: dragPosition < itemMidpoint ? itemPosition.left : itemPosition.left + itemSize,
itemMidpoint = itemBoundingRect.top + itemSize / 2;
dragPosition = clientY;
// Which side of the item we hover over will dictate
- // where the placeholder will appear, on the top or
+ // where to drop the selected item, on the top or
// on the bottom
cssOutput = {
top: dragPosition < itemMidpoint ? itemPosition.top : itemPosition.top + itemSize,
}
// Store whether we are before or after an item to rearrange
// For horizontal layout, we need to account for RTL, as this is flipped
- if ( this.orientation === 'horizontal' && this.$element.css( 'direction' ) === 'rtl' ) {
- this.sideInsertion = dragPosition < itemMidpoint ? 'after' : 'before';
+ if ( this.orientation === 'horizontal' && this.dir === 'rtl' ) {
+ after = dragPosition < itemMidpoint;
} else {
- this.sideInsertion = dragPosition < itemMidpoint ? 'before' : 'after';
+ after = dragPosition > itemMidpoint;
+ }
+ targetIndex = overIndex + ( after ? 1 : 0 );
+ // Check the targetIndex isn't immediately to the left or right of the current item (a no-op)
+ if ( targetIndex === dragItemIndex || targetIndex === dragItemIndex + 1 ) {
+ targetIndex = null;
}
- // Add drop indicator between objects
- this.$placeholder
- .css( cssOutput )
- .removeClass( 'oo-ui-element-hidden' );
- } else {
- // This means the item was dragged outside the widget
- this.$placeholder
- .css( 'left', 0 )
- .addClass( 'oo-ui-element-hidden' );
+ }
+ if ( targetIndex !== null ) {
+ if ( targetIndex > 0 ) {
+ this.$group.children().eq( targetIndex - 1 ).after( item.$element );
+ } else {
+ this.$group.prepend( item.$element );
+ }
+ // Move item in itemsOrder array. Needs to account for left shift if the item is moved forward.
+ this.itemsOrder.splice( targetIndex - ( targetIndex > dragItemIndex ? 1 : 0 ), 0,
+ this.itemsOrder.splice( dragItemIndex, 1 )[ 0 ]
+ );
+ this.updateIndexes();
+ this.emit( 'drag', item, targetIndex );
}
// Prevent default
e.preventDefault();
};
+/**
+ * Reorder the items in the group
+ *
+ * @param {OO.ui.mixin.DraggableElement} item Reordered item
+ * @param {number} newIndex New index
+ */
+OO.ui.mixin.DraggableGroupElement.prototype.reorder = function ( item, newIndex ) {
+ this.addItems( [ item ], newIndex );
+};
+
/**
* Set a dragged item
*
*/
OO.ui.mixin.DraggableGroupElement.prototype.setDragItem = function ( item ) {
this.dragItem = item;
+ this.$element.on( 'dragover', this.onDragOver.bind( this ) );
+ this.$element.addClass( 'oo-ui-draggableGroupElement-dragging' );
};
/**
*/
OO.ui.mixin.DraggableGroupElement.prototype.unsetDragItem = function () {
this.dragItem = null;
- this.itemDragOver = null;
- this.$placeholder.addClass( 'oo-ui-element-hidden' );
- this.sideInsertion = '';
+ this.$element.off( 'dragover' );
+ this.$element.removeClass( 'oo-ui-draggableGroupElement-dragging' );
};
/**
return this.dragItem;
};
-/**
- * Check if an item in the group is currently being dragged.
- *
- * @return {Boolean} Item is being dragged
- */
-OO.ui.mixin.DraggableGroupElement.prototype.isDragging = function () {
- return this.getDragItem() !== null;
-};
-
/**
* RequestManager is a mixin that manages the lifecycle of a promise-backed request for a widget, such as
* the {@link OO.ui.mixin.LookupElement}.
* @cfg {string} [notsupported] Text to display when file support is missing in the browser.
* @cfg {boolean} [droppable=true] Whether to accept files by drag and drop.
* @cfg {boolean} [showDropTarget=false] Whether to show a drop target. Requires droppable to be true.
- * @cfg {boolean} [dragDropUI=false] Deprecated alias for showDropTarget
* @cfg {Number} [thumbnailSizeLimit=20] File size limit in MiB above which to not try and show a
* preview (for performance)
*/
OO.ui.SelectFileWidget = function OoUiSelectFileWidget( config ) {
var dragHandler;
- // TODO: Remove in next release
- if ( config && config.dragDropUI ) {
- config.showDropTarget = true;
- }
-
// Configuration initialization
config = $.extend( {
accept: null,
OO.ui.mixin.IconElement.call( this, config );
OO.ui.mixin.IndicatorElement.call( this, config );
OO.ui.mixin.PendingElement.call( this, $.extend( {}, config, { $pending: this.$info } ) );
- OO.ui.mixin.LabelElement.call( this, $.extend( {}, config, { autoFitLabel: true } ) );
+ OO.ui.mixin.LabelElement.call( this, config );
// Properties
this.$info = $( '<span>' );
OO.ui.SelectFileWidget.static.isSupported = function () {
var $input;
if ( OO.ui.SelectFileWidget.static.isSupportedCache === null ) {
- $input = $( '<input type="file">' );
+ $input = $( '<input>' ).attr( 'type', 'file' );
OO.ui.SelectFileWidget.static.isSupportedCache = $input[ 0 ].files !== undefined;
}
return OO.ui.SelectFileWidget.static.isSupportedCache;
return;
}
- this.$input = $( '<input type="file">' );
+ this.$input = $( '<input>' ).attr( 'type', 'file' );
this.$input.on( 'change', this.onFileSelectedHandler );
this.$input.on( 'click', function ( e ) {
// Prevents dropTarget to get clicked which calls
/*!
- * OOjs UI v0.15.4
+ * OOjs UI v0.16.0
* https://www.mediawiki.org/wiki/OOjs_UI
*
* Copyright 2011–2016 OOjs UI Team and other contributors.
* Released under the MIT license
* http://oojs.mit-license.org
*
- * Date: 2016-02-17T02:03:27Z
+ * Date: 2016-02-22T22:33:37Z
*/
.oo-ui-actionWidget.oo-ui-pendingElement-pending {
background-image: /* @embed */ url(themes/apex/images/textures/pending.gif);
/*!
- * OOjs UI v0.15.4
+ * OOjs UI v0.16.0
* https://www.mediawiki.org/wiki/OOjs_UI
*
* Copyright 2011–2016 OOjs UI Team and other contributors.
* Released under the MIT license
* http://oojs.mit-license.org
*
- * Date: 2016-02-17T02:03:27Z
+ * Date: 2016-02-22T22:33:37Z
*/
.oo-ui-window {
background: transparent;
/*!
- * OOjs UI v0.15.4
+ * OOjs UI v0.16.0
* https://www.mediawiki.org/wiki/OOjs_UI
*
* Copyright 2011–2016 OOjs UI Team and other contributors.
* Released under the MIT license
* http://oojs.mit-license.org
*
- * Date: 2016-02-17T02:03:23Z
+ * Date: 2016-02-22T22:33:33Z
*/
( function ( OO ) {
<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
- <path d="M12 8C7 8 1 14 1 14s6 6 11 6l11-6s-6-6-11-6zm0 10c-2.2 0-4-1.8-4-4s1.8-4 4-4 4 1.8 4 4-1.8 4-4 4z"/>
+ <path d="M12 8c-5 0-11 6-11 6s6 6 11 6 11-6 11-6-6-6-11-6zm0 10c-2.2 0-4-1.8-4-4s1.8-4 4-4 4 1.8 4 4-1.8 4-4 4z"/>
<circle cx="12" cy="14" r="2"/>
</svg>
<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
- <path d="M12 8C7 8 1 14 1 14s6 6 11 6l11-6s-6-6-11-6zm0 10c-2.2 0-4-1.8-4-4s1.8-4 4-4 4 1.8 4 4-1.8 4-4 4z"/>
+ <path d="M12 8c-5 0-11 6-11 6s6 6 11 6 11-6 11-6-6-6-11-6zm0 10c-2.2 0-4-1.8-4-4s1.8-4 4-4 4 1.8 4 4-1.8 4-4 4z"/>
<circle cx="12" cy="14" r="2"/>
</svg>
$wgUser, $wgLang, $wgOut, $wgRequest, $wgStyleDirectory,
$wgExtraNamespaces, $wgNamespaceAliases, $wgNamespaceProtection, $wgLocalFileRepo,
$wgExtraInterlanguageLinkPrefixes, $wgLocalInterwikis,
- $parserMemc, $wgThumbnailScriptPath, $wgScriptPath,
+ $parserMemc, $wgThumbnailScriptPath, $wgScriptPath, $wgResourceBasePath,
$wgArticlePath, $wgScript, $wgStylePath, $wgExtensionAssetsPath,
$wgMainCacheType, $wgMessageCacheType, $wgParserCacheType, $wgLockManagers;
+ $wgScriptPath = '';
$wgScript = '/index.php';
- $wgScriptPath = '/';
- $wgArticlePath = '/wiki/$1';
$wgStylePath = '/skins';
+ $wgResourceBasePath = '';
$wgExtensionAssetsPath = '/extensions';
+ $wgArticlePath = '/wiki/$1';
$wgThumbnailScriptPath = false;
$wgLockManagers = [ [
'name' => 'fsLockManager',
!! wikitext
{{SCRIPTPATH}}
!! html
-<p>/
-</p>
+
!! end
!! test
* @ingroup Testing
*/
-$otions = [ 'quick', 'color', 'quiet', 'help', 'show-output',
+$options = [ 'quick', 'color', 'quiet', 'help', 'show-output',
'record', 'run-disabled', 'run-parsoid' ];
-$optionsWithArgs = [ 'regex', 'filter', 'seed', 'setversion' ];
+$optionsWithArgs = [ 'regex', 'filter', 'seed', 'setversion', 'file' ];
require_once __DIR__ . '/../maintenance/commandLine.inc';
require_once __DIR__ . '/TestsAutoLoader.php';
class LegacyLoggerTest extends MediaWikiTestCase {
/**
- * @covers LegacyLogger::interpolate
+ * @covers MediaWiki\Logger\LegacyLogger::interpolate
* @dataProvider provideInterpolate
*/
public function testInterpolate( $message, $context, $expect ) {
}
/**
- * @covers LegacyLogger::shouldEmit
+ * @covers MediaWiki\Logger\LegacyLogger::shouldEmit
* @dataProvider provideShouldEmit
*/
public function testShouldEmit( $level, $config, $expected ) {
class MonologSpiTest extends MediaWikiTestCase {
/**
- * @covers MonologSpi::mergeConfig
+ * @covers MediaWiki\Logger\MonologSpi::mergeConfig
*/
public function testMergeConfig() {
$base = [
}
/**
- * @covers LineFormatter::normalizeException
+ * @covers MediaWiki\Logger\Monolog\LineFormatter::normalizeException
*/
public function testNormalizeExceptionNoTrace() {
$fixture = new LineFormatter();
}
/**
- * @covers LineFormatter::normalizeException
+ * @covers MediaWiki\Logger\Monolog\LineFormatter::normalizeException
*/
public function testNormalizeExceptionTrace() {
$fixture = new LineFormatter();
}
/**
- * @dataProvider provider_testSanitzeHdrs
- * @covers SwiftFileBackend::sanitzeHdrs
+ * @dataProvider provider_testSanitizeHdrs
+ * @covers SwiftFileBackend::sanitizeHdrs
* @covers SwiftFileBackend::getCustomHeaders
*/
- public function testSanitzeHdrs( $raw, $sanitized ) {
+ public function testSanitizeHdrs( $raw, $sanitized ) {
$hdrs = $this->backend->sanitizeHdrs( [ 'headers' => $raw ] );
$this->assertEquals( $hdrs, $sanitized, 'sanitizeHdrs() has expected result' );
}
- public static function provider_testSanitzeHdrs() {
+ public static function provider_testSanitizeHdrs() {
return [
[
[
/**
* @covers ProcessCacheLRU::get
* @covers ProcessCacheLRU::set
- * @covers ProcessCacheLRU::het
+ * @covers ProcessCacheLRU::has
*/
public function testAddAndGetAKey() {
$oneCache = new ProcessCacheLRUTestable( 1 );
/**
* @covers ProcessCacheLRU::get
* @covers ProcessCacheLRU::set
- * @covers ProcessCacheLRU::het
+ * @covers ProcessCacheLRU::has
*/
public function testRecentlyAccessedKeyStickIn() {
$cache = new ProcessCacheLRUTestable( 2 );
$cache->delete( 'foo', CachedBagOStuff::WRITE_CACHE_ONLY );
$this->assertEquals( 'old', $cache->get( 'foo' ) ); // Reloaded from backend
}
+
+ public function testCacheBackendMisses() {
+ $backend = new HashBagOStuff;
+ $cache = new CachedBagOStuff( $backend );
+
+ // First hit primes the cache with miss from the backend
+ $this->assertEquals( false, $cache->get( 'foo' ) );
+
+ // Change the value in the backend
+ $backend->set( 'foo', true );
+
+ // Second hit returns the cached miss
+ $this->assertEquals( false, $cache->get( 'foo' ) );
+
+ // But a fresh value is read from the backend
+ $backend->set( 'bar', true );
+ $this->assertEquals( true, $cache->get( 'bar' ) );
+ }
}
/**
* @dataProvider provideSwappingICCProfile
- * @covers BitmapHandler::swapICCProfile
+ * @covers ExifBitmapHandler::swapICCProfile
*/
public function testSwappingICCProfile(
$sourceFilename, $controlFilename, $newProfileFilename, $oldProfileName
/**
* Test for multi-section, hostile XML
- * @covers checkParseSafety
+ * @covers XMPReader::checkParseSafety
*/
public function testCheckParseSafety() {
$this->assertEquals( 2, $this->article->mLatest, "Article __set magic" );
}
- /**
- * @depends testImplementsSetMagic
- * @covers Article::__call
- */
- public function testImplementsCallMagic() {
- $this->article->mLatest = 33;
- $this->article->mDataLoaded = true;
- $this->assertEquals( 33, $this->article->getLatest(), "Article __call magic" );
- }
-
/**
* @covers Article::__get
* @covers Article::__set
$tmpGlobals['wgSitename'] = 'MediaWiki';
$tmpGlobals['wgServer'] = 'http://example.org';
$tmpGlobals['wgServerName'] = 'example.org';
+ $tmpGlobals['wgScriptPath'] = '';
$tmpGlobals['wgScript'] = '/index.php';
- $tmpGlobals['wgScriptPath'] = '/';
+ $tmpGlobals['wgResourceBasePath'] = '';
+ $tmpGlobals['wgStylePath'] = '/skins';
+ $tmpGlobals['wgExtensionAssetsPath'] = '/extensions';
$tmpGlobals['wgArticlePath'] = '/wiki/$1';
$tmpGlobals['wgActionPaths'] = [];
$tmpGlobals['wgVariantArticlePath'] = false;
- $tmpGlobals['wgExtensionAssetsPath'] = '/extensions';
- $tmpGlobals['wgStylePath'] = '/skins';
$tmpGlobals['wgEnableUploads'] = true;
$tmpGlobals['wgUploadNavigationUrl'] = false;
$tmpGlobals['wgThumbnailScriptPath'] = false;
/**
* @group large
*/
-class BcryptPasswordTestCase extends PasswordTestCase {
+class BcryptPasswordTest extends PasswordTestCase {
protected function getTypeConfigs() {
return [ 'bcrypt' => [
'class' => 'BcryptPassword',
$this->assertEquals( 'somevalue', $extracted['globals']['egBar'] );
}
- public static function provideExtracttExtensionMessagesFiles() {
+ public static function provideExtractExtensionMessagesFiles() {
$dir = __DIR__ . '/FooBar/';
return [
[
}
/**
- * @covers ExtensionProcessor::extracttExtensionMessagesFiles
- * @dataProvider provideExtracttExtensionMessagesFiles
+ * @covers ExtensionProcessor::extractExtensionMessagesFiles
+ * @dataProvider provideExtractExtensionMessagesFiles
*/
- public function testExtracttExtensionMessagesFiles( $input, $expected ) {
+ public function testExtractExtensionMessagesFiles( $input, $expected ) {
$processor = new ExtensionProcessor();
$processor->extractInfo( $this->dir, $input + self::$default, 1 );
$out = $processor->getExtractedInfo();
}
/**
- * @covers FileContentsHasher::getFileContentHash
+ * @covers FileContentsHasher::getFileContentsHash
* @covers FileContentsHasher::getFileContentsHashInternal
* @dataProvider provideSingleFile
*/
}
/**
- * @covers FileContentsHasher::getFileContentHash
+ * @covers FileContentsHasher::getFileContentsHash
* @covers FileContentsHasher::getFileContentsHashInternal
* @dataProvider provideMultipleFiles
*/
$_SERVER['argv'] = array_values( $_SERVER['argv'] );
}
- if ( !wfIsWindows() ) {
- # If we are not running on windows then we can enable phpunit colors
- # Windows does not come anymore with ANSI.SYS loaded by default
- # PHPUnit uses the suite.xml parameters to enable/disable colors
- # which can be then forced to be enabled with --colors.
- # The below code injects a parameter just like if the user called
- # Probably fix bug 29226
- $key = array_search( '--colors', $_SERVER['argv'] );
- if ( $key === false ) {
- array_splice( $_SERVER['argv'], 1, 0, '--colors' );
- }
- }
-
# Makes MediaWiki PHPUnit directory includable so the PHPUnit will
# be able to resolve relative files inclusion such as suites/*
# PHPUnit uses stream_resolve_include_path() internally
<?xml version="1.0" encoding="UTF-8"?>
-
-<!--
-Colors don't work on Windows!
-phpunit.php enables colors for other OSs at runtime
--->
<phpunit bootstrap="./bootstrap.php"
- colors="false"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.8/phpunit.xsd"
+
+ colors="true"
backupGlobals="false"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
timeoutForSmallTests="10"
timeoutForMediumTests="30"
timeoutForLargeTests="60"
- strict="true"
+ beStrictAboutTestsThatDoNotTestAnything="true"
+ beStrictAboutOutputDuringTests="true"
+ beStrictAboutTestSize="true"
verbose="true">
<testsuites>
<testsuite name="includes">