"OO": false
},
"rules": {
- "max-len": 0
+ "quote-props": [ "error", "as-needed" ],
+ "max-len": "off",
+ "jquery/no-global-selector": "off"
}
}
Étienne Beaulé <beauleetienne0@gmail.com>
Željko Filipin <zeljko.filipin@gmail.com>
Željko Filipin <zeljko.filipin@gmail.com> <zfilipin@wikimedia.org>
+星耀晨曦 <razesoldier@outlook.com>
+星耀晨曦 <razesoldier@outlook.com> <liguangjie4399@hotmail.com>
* rahul21
* Raimond Spekking
* Ramunas Geciauskas
-* RazeSoldier
* Remember the dot
* René Kijewski
* Reza
* Updated wikimedia/ip-set from 1.2.0 to 2.0.0.
* The deprecated IPSet\IPSet alias was removed, Wikimedia\IPSet must be
used instead.
+* Updated qunitjs from 2.6.2 to 2.9.1.
* …
==== Removed external libraries ====
* The PasswordPolicy 'PasswordCannotBePopular' has been deprecated. To
follow best practices, it is reccommended to use 'PasswordNotInLargeBlacklist'
instead which blacklists 100,000 commonly used passwords.
+* (T208862) Action::requiresUnblock() is now called from
+ Title::getUserPermissionsErrors() and Title::userCan(). Previously, the method
+ was only called in Action::checkCanExecute(). Actions should ensure that their
+ requiresUnblock() returns the proper result (the default is `true`).
* …
=== Other changes in 1.33 ===
* prohibited from editing any page on the site (other than their own talk
* page).
*
+ * @since 1.33
* @param null|bool $x
* @return bool
*/
/**
* Get block information used in different block error messages
*
+ * @since 1.33
* @param IContextSource $context
* @return array
*/
* Getting the restrictions will perform a database query if the restrictions
* are not already loaded.
*
+ * @since 1.33
* @return Restriction[]
*/
public function getRestrictions() {
/**
* Set Restrictions.
*
+ * @since 1.33
* @param Restriction[] $restrictions
- *
* @return self
*/
public function setRestrictions( array $restrictions ) {
* Password policy for the wiki.
* Structured as
* [
- * 'policies' => [ <group> => [ <policy> => <value>, ... ], ... ],
+ * 'policies' => [ <group> => [ <policy> => <settings>, ... ], ... ],
* 'checks' => [ <policy> => <callback>, ... ],
* ]
* where <group> is a user group, <policy> is a password policy name
* (arbitrary string) defined in the 'checks' part, <callback> is the
- * PHP callable implementing the policy check, <value> is a number,
- * boolean or null that gets passed to the callback.
+ * PHP callable implementing the policy check, <settings> is an array
+ * of options with the following keys:
+ * - value: (number, boolean or null) the value to pass to the callback
+ * - forceChange: (bool, default false) if the password is invalid, do
+ * not let the user log in without changing the password
+ * As a shorthand for [ 'value' => <value> ], simply <value> can be written.
+ * When multiple password policies are defined for a user, the settings
+ * arrays are merged, and for fields which are set in both arrays, the
+ * larger value (as understood by PHP's 'max' method) is taken.
*
* A user's effective policy is the superset of all policy statements
* from the policies for the groups where the user is a member. If more
* Flag to enable Partial Blocks. This allows an admin to prevent a user from editing specific pages
* or namespaces.
*
- * @since 1.32
- * @deprecated 1.32
+ * @since 1.33
+ * @deprecated 1.33
* @var bool
*/
$wgEnablePartialBlocks = false;
*
* For example:
*
- * $wgOut->wrapWikiMsg( "<div class='error'>\n$1\n</div>", 'some-error' );
+ * $wgOut->wrapWikiMsg( "<div class='error'>\n$1\n</div>", 'some-error' );
*
* Is equivalent to:
*
- * $wgOut->addWikiTextAsInterface( "<div class='error'>\n"
- * . wfMessage( 'some-error' )->plain() . "\n</div>" );
+ * $wgOut->addWikiTextAsInterface( "<div class='error'>\n"
+ * . wfMessage( 'some-error' )->plain() . "\n</div>" );
*
* The newline after the opening div is needed in some wikitext. See T21226.
*
}
/**
- * SlotRecord constructor.
- *
* The following fields are supported by the $row parameter:
*
* $row->blob_data
private $handlers;
/**
- * SlotRoleRegistry constructor.
- *
* @param NameTableStore $roleNamesStore
*/
public function __construct( NameTableStore $roleNamesStore ) {
* @note Consider using a TitleValue object instead. TitleValue is more lightweight
* and does not rely on global state or the database.
*/
-class Title implements LinkTarget {
+class Title implements LinkTarget, IDBAccessObject {
/** @var MapCacheLRU */
static private $titleCache = null;
}
$useReplica = ( $rigor !== 'secure' );
- if ( ( $action == 'edit' || $action == 'create' )
- && !$user->isBlockedFrom( $this, $useReplica )
- ) {
- // Don't block the user from editing their own talk page unless they've been
- // explicitly blocked from that too.
- } elseif ( $user->isBlocked() && $user->getBlock()->prevents( $action ) !== false ) {
+ $block = $user->getBlock( $useReplica );
+
+ // The block may explicitly allow an action (like "read" or "upload").
+ if ( $block && $block->prevents( $action ) === false ) {
+ return $errors;
+ }
+
+ // Determine if the user is blocked from this action on this page.
+ // What gets passed into this method is a user right, not an action nmae.
+ // There is no way to instantiate an action by restriction. However, this
+ // will get the action where the restriction is the same. This may result
+ // in actions being blocked that shouldn't be.
+ if ( Action::exists( $action ) ) {
// @todo FIXME: Pass the relevant context into this function.
- $errors[] = $user->getBlock()->getPermissionsError( RequestContext::getMain() );
+ $action = Action::factory( $action, WikiPage::factory( $this ), RequestContext::getMain() );
+ } else {
+ $action = null;
+ }
+
+ // If no action object is returned, assume that the action requires unblock
+ // which is the default.
+ if ( !$action || $action->requiresUnblock() ) {
+ if ( $user->isBlockedFrom( $this, $useReplica ) ) {
+ // @todo FIXME: Pass the relevant context into this function.
+ $errors[] = $block
+ ? $block->getPermissionsError( RequestContext::getMain() )
+ : [ 'actionblockedtext' ];
+ }
}
return $errors;
* indicating who can move or edit the page from the page table, (pre 1.10) rows.
* Edit and move sections are separated by a colon
* Example: "edit=autoconfirmed,sysop:move=sysop"
- * @param bool $readLatest When true, skip replicas and read from the master DB.
*/
- public function loadRestrictionsFromRows(
- $rows, $oldFashionedRestrictions = null, $readLatest = false
- ) {
- $whichDb = $readLatest ? DB_MASTER : DB_REPLICA;
- $dbr = wfGetDB( $whichDb );
+ public function loadRestrictionsFromRows( $rows, $oldFashionedRestrictions = null ) {
+ // This function will only read rows from a table that we migrated away
+ // from before adding READ_LATEST support to loadRestrictions, so we
+ // don't need to support reading from DB_MASTER here.
+ $dbr = wfGetDB( DB_REPLICA );
$restrictionTypes = $this->getRestrictionTypes();
* indicating who can move or edit the page from the page table, (pre 1.10) rows.
* Edit and move sections are separated by a colon
* Example: "edit=autoconfirmed,sysop:move=sysop"
- * @param bool $readLatest When true, skip replicas and read from the master DB.
+ * @param int $flags A bit field. If self::READ_LATEST is set, skip replicas and read
+ * from the master DB.
*/
- public function loadRestrictions( $oldFashionedRestrictions = null, $readLatest = false ) {
+ public function loadRestrictions( $oldFashionedRestrictions = null, $flags = 0 ) {
+ $readLatest = DBAccessObjectUtils::hasFlags( $flags, self::READ_LATEST );
if ( $this->mRestrictionsLoaded && !$readLatest ) {
return;
}
+ // TODO: should probably pass $flags into getArticleID, but it seems hacky
+ // to mix READ_LATEST and GAID_FOR_UPDATE, even if they have the same value.
+ // Maybe deprecate GAID_FOR_UPDATE now that we implement IDBAccessObject?
$id = $this->getArticleID();
if ( $id ) {
- $cache = ObjectCache::getMainWANInstance();
$fname = __METHOD__;
- $rows = $cache->getWithSetCallback(
- // Page protections always leave a new null revision
- $cache->makeKey( 'page-restrictions', $id, $this->getLatestRevID(), $readLatest ),
- $cache::TTL_DAY,
- function ( $curValue, &$ttl, array &$setOpts ) use ( $fname, $readLatest ) {
- $whichDb = $readLatest ? DB_MASTER : DB_REPLICA;
- $dbr = wfGetDB( $whichDb );
-
- $setOpts += Database::getCacheSetOptions( $dbr );
-
- return iterator_to_array(
- $dbr->select(
- 'page_restrictions',
- [ 'pr_type', 'pr_expiry', 'pr_level', 'pr_cascade' ],
- [ 'pr_page' => $this->getArticleID() ],
- $fname
- )
- );
- }
- );
+ $loadRestrictionsFromDb = function ( Database $dbr ) use ( $fname, $id ) {
+ return iterator_to_array(
+ $dbr->select(
+ 'page_restrictions',
+ [ 'pr_type', 'pr_expiry', 'pr_level', 'pr_cascade' ],
+ [ 'pr_page' => $id ],
+ $fname
+ )
+ );
+ };
+
+ if ( $readLatest ) {
+ $dbr = wfGetDB( DB_MASTER );
+ $rows = $loadRestrictionsFromDb( $dbr );
+ } else {
+ $cache = ObjectCache::getMainWANInstance();
+ $rows = $cache->getWithSetCallback(
+ // Page protections always leave a new null revision
+ $cache->makeKey( 'page-restrictions', $id, $this->getLatestRevID() ),
+ $cache::TTL_DAY,
+ function ( $curValue, &$ttl, array &$setOpts ) use ( $loadRestrictionsFromDb ) {
+ $dbr = wfGetDB( DB_REPLICA );
+
+ $setOpts += Database::getCacheSetOptions( $dbr );
+
+ return $loadRestrictionsFromDb( $dbr );
+ }
+ );
+ }
- $this->loadRestrictionsFromRows( $rows, $oldFashionedRestrictions, $readLatest );
+ $this->loadRestrictionsFromRows( $rows, $oldFashionedRestrictions );
} else {
$title_protection = $this->getTitleProtectionInternal();
"apihelp-query+filearchive-example-simple": "Mostrar una lista de todos los archivos eliminados.",
"apihelp-query+filerepoinfo-summary": "Devuelve metainformación sobre los repositorios de imágenes configurados en el wiki.",
"apihelp-query+filerepoinfo-param-prop": "Qué propiedades del repositorio obtener (las propiedades disponibles pueden variar en otras wikis).",
+ "apihelp-query+filerepoinfo-paramvalue-prop-rootUrl": "Ruta de la URL raíz para las rutas de las imágenes.",
+ "apihelp-query+filerepoinfo-paramvalue-prop-scriptDirUrl": "Ruta de la URL raíz para la instalación MediaWiki del wiki del repositorio.",
+ "apihelp-query+filerepoinfo-paramvalue-prop-server": "<var>[[mw:Special:MyLanguage/Manual:$wgServer|$wgServer]]</var> o equivalente del wiki del repositorio.",
+ "apihelp-query+filerepoinfo-paramvalue-prop-thumbUrl": "Ruta de la URL raíz para las rutas de las miniaturas.",
+ "apihelp-query+filerepoinfo-paramvalue-prop-url": "Ruta de la URL de la zona pública.",
"apihelp-query+filerepoinfo-example-simple": "Obtener información acerca de los repositorios de archivos.",
"apihelp-query+fileusage-summary": "Encontrar todas las páginas que utilizan los archivos dados.",
"apihelp-query+fileusage-param-prop": "Qué propiedades se obtendrán:",
"apihelp-main-param-servedby": "在結果中包括提出請求的主機名。",
"apihelp-main-param-curtimestamp": "在結果中包括目前的時間戳。",
"apihelp-main-param-responselanginfo": "在結果中包括<var>uselang</var>和<var>errorlang</var>所用的語言。",
+ "apihelp-main-param-errorformat": "用於警告和錯誤文字輸出的格式。\n; plaintext:移除掉 HTML 標籤,且實體已替換的 wiki 文字。\n; wikitext:未解析的 wiki 文字。\n; html:HTML。\n; raw:訊息鍵值與參數。\n; none:無文字輸出,僅含有錯誤代碼。\n; bc:用於 MediaWiki 1.29 之前版本的格式。會忽略 <var>errorlang</var> 與 <var>errorsuselocal</var>。",
"apihelp-main-param-errorsuselocal": "若有指定,錯誤文字會使用來自 {{ns:MediaWiki}} 命名空間的本地自定義訊息。",
"apihelp-block-summary": "封鎖使用者。",
"apihelp-block-param-user": "要封鎖的使用者名稱、IP 位址或 IP 範圍。不能與 <var>$1userid</var> 一起使用",
"apihelp-compare-param-fromid": "要比對的第一個頁面 ID。",
"apihelp-compare-param-fromrev": "要比對的第一個修訂。",
"apihelp-compare-param-frompst": "在 <var>fromtext-{slot}</var> 進行預先儲存轉換。",
+ "apihelp-compare-param-fromslots": "覆蓋由 <var>fromtitle</var>、<var>fromid</var> 或 <var>fromrev</var> 指定的修訂內容。\n\n此參數指定要變動的間隔。使用 <var>fromtext-{slot}</var>、<var>fromcontentmodel-{slot}</var>、與 <var>fromcontentformat-{slot}</var> 來指定各間隔的內容。",
"apihelp-compare-param-fromcontentmodel-{slot}": "<var>fromtext-{slot}</var> 內容模組。若不提供,則會根據其它參數猜測。",
"apihelp-compare-param-fromcontentformat-{slot}": "<var>fromtext-{slot}</var> 的內容序列化格式。",
"apihelp-compare-param-fromtext": "指定 <kbd>fromslots=main</kbd> 並改用 <var>fromtext-main</var>。",
"apihelp-expandtemplates-param-title": "頁面標題。",
"apihelp-expandtemplates-param-text": "要轉換的 Wikitext。",
"apihelp-expandtemplates-param-revid": "修訂 ID,用於 <code><nowiki>{{REVISIONID}}</nowiki></code> 和相似變數。",
+ "apihelp-expandtemplates-param-prop": "所要取得的資訊部份。\n\n請注意若沒有選定值,結果會包含 wiki 文字,輸出內容會採用棄用格式。",
"apihelp-expandtemplates-paramvalue-prop-wikitext": "展開的 wiki 文字。",
+ "apihelp-expandtemplates-paramvalue-prop-categories": "任何呈現在輸入中,且未在 wiki 文字輸出裡表現出的分類。",
"apihelp-expandtemplates-paramvalue-prop-properties": "透過在 wiki 文字裡擴充魔術字所定義的頁面屬性。",
"apihelp-expandtemplates-paramvalue-prop-volatile": "輸出內容是否易變,且是否不應在頁面其它位置裡重複使用。",
+ "apihelp-expandtemplates-paramvalue-prop-ttl": "結果的快取應失效後的最長時間。",
"apihelp-expandtemplates-paramvalue-prop-modules": "已請求添加至輸出內容之解析器功能的任何 ResourceLoader 模組。要載入請使用 <code>mw.loader.using()</code>。<kbd>jsconfigvars</kbd> 或 <kbd>encodedjsconfigvars</kbd> 其一必須與 <kbd>modules</kbd> 一同被請求。",
"apihelp-expandtemplates-paramvalue-prop-jsconfigvars": "指定頁面的 JavaScript 設置變量。",
"apihelp-expandtemplates-paramvalue-prop-encodedjsconfigvars": "指定頁面的 JavaScript 設置變量為 JSON 字串。",
"apihelp-feedrecentchanges-param-hidecategorization": "隱藏分類成員更改。",
"apihelp-feedrecentchanges-param-tagfilter": "按標籤篩選。",
"apihelp-feedrecentchanges-param-target": "僅顯示從該頁面所連結頁面上的變更。",
+ "apihelp-feedrecentchanges-param-showlinkedto": "改成顯示出連結到所選頁面的那些頁面之變更。",
"apihelp-feedrecentchanges-example-simple": "顯示近期變更。",
"apihelp-feedrecentchanges-example-30days": "顯示近期30天內的變動",
"apihelp-feedwatchlist-summary": "返回監視清單摘要。",
"apihelp-filerevert-example-revert": "回退 <kbd>Wiki.png</kbd> 至 <kbd>2011-03-05T15:27:40Z</kbd> 的版本。",
"apihelp-help-summary": "顯示指定模組的說明。",
"apihelp-help-param-modules": "顯示說明的模組(<var>action</var> 與 <var>format</var> 參數的值、或 <kbd>main</kbd>)。可透過 <kbd>+</kbd> 來指定子模組。",
+ "apihelp-help-param-submodules": "包含用於命名模組之子模組的說明。",
+ "apihelp-help-param-recursivesubmodules": "包含遞迴子模組的說明。",
"apihelp-help-param-helpformat": "說明輸出的格式。",
"apihelp-help-param-wrap": "在標準 API 回應結構裡包裹輸出。",
"apihelp-help-param-toc": "在 HTML 輸出裡包含目錄。",
"apihelp-query+blocks-example-users": "列出使用者 <kbd>Alice</kbd> 與 <kbd>Bob</kbd> 的封鎖。",
"apihelp-query+categories-summary": "列出頁面隸屬的所有分類。",
"apihelp-query+categories-param-prop": "為各分類所要取得的額外屬性:",
+ "apihelp-query+categories-paramvalue-prop-sortkey": "添加用於分類的排序鍵值(十六進位字串)與排序鍵值字首(人類可讀的部份)。",
"apihelp-query+categories-paramvalue-prop-timestamp": "添加當添加分類時的時間戳記。",
"apihelp-query+categories-paramvalue-prop-hidden": "標記由 <code>__HIDDENCAT__</code> 隱藏的分類。",
"apihelp-query+categories-param-show": "要顯示出的分類種類。",
"apihelp-query+categorymembers-param-prop": "要包含的資訊部份:",
"apihelp-query+categorymembers-paramvalue-prop-ids": "添加頁面 ID。",
"apihelp-query+categorymembers-paramvalue-prop-title": "添加標題與頁面的命名空間 ID。",
- "apihelp-query+categorymembers-paramvalue-prop-sortkey": "添加使用來在分類裡排序的排序鍵(十六進位字串)。",
+ "apihelp-query+categorymembers-paramvalue-prop-sortkey": "添加使用來在分類裡排序的排序鍵值(十六進位字串)。",
+ "apihelp-query+categorymembers-paramvalue-prop-sortkeyprefix": "添加用於在分類裡排序的排序鍵值字首(排序鍵值中人類可讀的部份)。",
"apihelp-query+categorymembers-paramvalue-prop-type": "添加頁面已被分類的類型(<samp>page</samp>、<samp>subcat</samp> 或 <samp>file</samp>)。",
"apihelp-query+categorymembers-paramvalue-prop-timestamp": "添加在頁面有被包含時的時間戳記。",
"apihelp-query+categorymembers-param-namespace": "僅包含在這些命名空間的頁面。請注意可能會使用 <kbd>$1type=subcat</kbd> 或 <kbd>$1type=file</kbd>,而非 <kbd>$1namespace=14</kbd> 或 <kbd>6</kbd>。",
"apihelp-query+categorymembers-param-dir": "排序的方向。",
"apihelp-query+categorymembers-param-start": "起始列出的時間戳記。僅能與 <kbd>$1sort=timestamp</kbd> 一起使用。",
"apihelp-query+categorymembers-param-end": "結束列出的時間戳記。僅能與 <kbd>$1sort=timestamp</kbd> 一起使用。",
+ "apihelp-query+categorymembers-param-starthexsortkey": "開始列出的排序鍵值,由 <kbd>$1prop=sortkey</kbd> 所回傳。僅能與 <kbd>$1sort=sortkey</kbd> 一起使用。",
+ "apihelp-query+categorymembers-param-endhexsortkey": "終止列出的排序鍵值,由 <kbd>$1prop=sortkey</kbd> 所回傳。僅能與 <kbd>$1sort=sortkey</kbd> 一起使用。",
+ "apihelp-query+categorymembers-param-startsortkeyprefix": "開始列出的排序鍵值字首,僅能與 <kbd>$1sort=sortkey</kbd> 一起使用。覆蓋 <var>$1starthexsortkey</var>。",
+ "apihelp-query+categorymembers-param-endsortkeyprefix": "終止列出 <strong>before</strong> 的排序鍵值字首(並不是 <strong>at</strong>,若此值有出現將不會被包含到!),僅能與 $1sort=sortkey 一起使用。覆蓋 $1endhexsortkey。",
"apihelp-query+categorymembers-param-startsortkey": "請改用 $1starthexsortkey。",
"apihelp-query+categorymembers-param-endsortkey": "請改用 $1endhexsortkey。",
"apihelp-query+categorymembers-example-simple": "取得在 <kbd>Category:Physics</kbd> 裡前 10 項的頁面。",
"apihelp-query+contributors-param-limit": "要回傳的貢獻人員數量。",
"apihelp-query+contributors-example-simple": "顯示頁面 <kbd>Main Page</kbd> 的貢獻者。",
"apihelp-query+deletedrevisions-summary": "取得已刪除修訂的資訊。",
+ "apihelp-query+deletedrevisions-param-start": "要開始列舉的時間戳記。當處理修訂 ID 清單時會被忽略。",
+ "apihelp-query+deletedrevisions-param-end": "要停止列舉的時間戳記。當處理修訂 ID 清單時會被忽略。",
"apihelp-query+deletedrevisions-param-tag": "僅列出以此標籤所標記的修訂。",
"apihelp-query+deletedrevisions-param-user": "此列出由該使用者作出的修訂。",
"apihelp-query+deletedrevisions-param-excludeuser": "不要列出由該使用者作出的修訂。",
"apihelp-query+recentchanges-paramvalue-prop-autopatrolled": "標記可巡查編輯為自動巡查或否。",
"apihelp-query+recentchanges-paramvalue-prop-loginfo": "添加日誌資訊(日誌 ID、日誌類型、其它)至日誌項目。",
"apihelp-query+recentchanges-paramvalue-prop-tags": "列出項目的標籤。",
+ "apihelp-query+recentchanges-paramvalue-prop-sha1": "替與修訂關聯的項目添加內容核對和。",
"apihelp-query+recentchanges-param-token": "請改用 <kbd>[[Special:ApiHelp/query+tokens|action=query&meta=tokens]]</kbd>。",
"apihelp-query+recentchanges-param-show": "僅顯示符合這些標準的項目。例如,僅查看由登入使用者做出的小編輯,請設定 $1show=minor|!anon。",
"apihelp-query+recentchanges-param-limit": "要回傳變更總數。",
"apihelp-query+redirects-example-generator": "取得所有重新導向至 [[Main Page]] 的資訊。",
"apihelp-query+revisions-summary": "取得修訂的資訊。",
"apihelp-query+revisions-paraminfo-singlepageonly": "僅能在單一頁面使用(模式 #2)。",
+ "apihelp-query+revisions-param-startid": "從這個修訂時間戳記開始列舉。修訂必須要存在,但不需屬於此頁面。",
+ "apihelp-query+revisions-param-endid": "在這個修訂時間戳記停止列舉。修訂必須要存在,但不需屬於此頁面。",
+ "apihelp-query+revisions-param-start": "從哪個修訂時間戳記來開始列舉。",
"apihelp-query+revisions-param-end": "列舉至此的時間戳記。",
"apihelp-query+revisions-param-user": "僅包含由使用者做出的修訂。",
"apihelp-query+revisions-param-excludeuser": "不包含由使用者做出的修訂。",
"apihelp-query+search-param-what": "要執行的搜尋類型。",
"apihelp-query+search-param-info": "要回傳的詮釋資料。",
"apihelp-query+search-param-prop": "要回傳的屬性:",
+ "apihelp-query+search-param-qiprofile": "要使用的查詢獨立配置(會影響排序演算法)。",
"apihelp-query+search-paramvalue-prop-size": "添加以位元組為單位的頁面大小。",
"apihelp-query+search-paramvalue-prop-wordcount": "添加頁面的字數。",
"apihelp-query+search-paramvalue-prop-timestamp": "添加頁面自上一次編輯的時間戳記。",
$reset = $this->getPasswordResetData( $username, $data );
if ( !$reset && $this->config->get( 'InvalidPasswordReset' ) && !$status->isGood() ) {
+ $hard = $status->getValue()['forceChange'] ?? false;
$reset = (object)[
- 'msg' => $status->getMessage( 'resetpass-validity-soft' ),
- 'hard' => false,
+ 'msg' => $status->getMessage( $hard ? 'resetpass-validity' : 'resetpass-validity-soft' ),
+ 'hard' => $hard,
];
}
/**
* Retrieves the restrictions from the database by block id.
*
+ * @since 1.33
* @param int|array $blockId
* @param IDatabase|null $db
* @return Restriction[]
/**
* Inserts the restrictions into the database.
*
+ * @since 1.33
* @param Restriction[] $restrictions
* @return bool
*/
* Updates the list of restrictions. This method does not allow removing all
* of the restrictions. To do that, use ::deleteByBlockId().
*
+ * @since 1.33
* @param Restriction[] $restrictions
* @return bool
*/
/**
* Updates the list of restrictions by parent id.
*
+ * @since 1.33
* @param int $parentBlockId
* @param Restriction[] $restrictions
* @return bool
/**
* Delete the restrictions.
*
+ * @since 1.33
* @param Restriction[]|null $restrictions
* @throws MWException
* @return bool
/**
* Delete the restrictions by Block ID.
*
+ * @since 1.33
* @param int|array $blockId
* @throws MWException
* @return bool
/**
* Delete the restrictions by Parent Block ID.
*
+ * @since 1.33
* @param int|array $parentBlockId
* @throws MWException
* @return bool
* equality check as the restrictions do not have to contain the same block
* ids.
*
+ * @since 1.33
* @param Restriction[] $a
* @param Restriction[] $b
* @return bool
/**
* Set the blockId on a set of restrictions and return a new set.
*
+ * @since 1.33
* @param int $blockId
* @param Restriction[] $restrictions
* @return Restriction[]
/**
* Create Restriction.
*
+ * @since 1.33
* @param int $blockId
* @param int $value
*/
/**
* Set the title.
*
+ * @since 1.33
* @param \Title $title
* @return self
*/
/**
* Get Title.
*
+ * @since 1.33
* @return \Title|null
*/
public function getTitle() {
/**
* Gets the id of the block.
*
+ * @since 1.33
* @return int
*/
public function getBlockId();
/**
* Sets the id of the block.
*
+ * @since 1.33
* @param int $blockId
* @return self
*/
/**
* Gets the value of the restriction.
*
+ * @since 1.33
* @return int
*/
public function getValue();
/**
* Gets the type of restriction
*
+ * @since 1.33
* @return string
*/
public function getType();
/**
* Gets the id of the type of restriction. This id is used in the database.
*
+ * @since 1.33
* @return string
*/
public function getTypeId();
/**
* Creates a new Restriction from a database row.
*
+ * @since 1.33
* @param \stdClass $row
* @return self
*/
/**
* Convert a restriction object into a row array for insertion.
*
+ * @since 1.33
* @return array
*/
public function toRow();
/**
* Determine if a restriction matches a given title.
*
+ * @since 1.33
* @param \Title $title
* @return bool
*/
/**
* Determine if a restriction equals another restriction.
*
+ * @since 1.33
* @param Restriction $other
* @return bool
*/
/**
* Create a unique hash of the block restriction based on the type and value.
*
+ * @since 1.33
* @return string
*/
public function getHash();
protected $filterGroups;
/**
- * Changeslist constructor
- *
* @param Skin|IContextSource $obj
* @param array $filterGroups Array of ChangesListFilterGroup objects (currently optional)
*/
* Upload a file directly into archive. Generally for Special:Import.
*
* @param string $srcPath File system path of the source file
- * @param string $archiveName Full archive name of the file, in the form
- * $timestamp!$filename, where $filename must match $this->getName()
* @param string $timestamp
* @param string $comment
* @param User $user
* @return Status
*/
- function uploadOld( $srcPath, $archiveName, $timestamp, $comment, $user ) {
+ public function uploadOld( $srcPath, $timestamp, $comment, $user ) {
$this->lock();
+ $archiveName = $this->getArchiveName();
$dstRel = $this->getArchiveRel( $archiveName );
$status = $this->publishTo( $srcPath, $dstRel );
$languages[$languageCode] = $languageCode;
}
+ ksort( $languages );
+
foreach ( $languages as $code => $name ) {
$this->mParams['options'][$code . ' - ' . $name] = $code;
}
?: User::newFromName( $importableRevision->getUser(), false );
# Do the actual upload
- if ( $archiveName ) {
- $status = $file->uploadOld( $source, $archiveName,
- $importableRevision->getTimestamp(), $importableRevision->getComment(), $user );
+ if ( $file instanceof OldLocalFile ) {
+ $status = $file->uploadOld(
+ $source,
+ $importableRevision->getTimestamp(),
+ $importableRevision->getComment(),
+ $user
+ );
} else {
$flags = 0;
$status = $file->upload(
"config-sqlite-dir-help": "SQLite-k datu guztiak fitxategi bakarrean gordetzen ditu.\n\nHornitu duzun direktorioa web zerbitzariaren bidez idatzia izateko aukera eman beharko duu instalazioan zehar.\n\n<Strong>Ez</strong> da webgunearen bidez eskuragarri egon behar; horregatik zure PHP fitxategiak non dauden ez dugu erakutsi.\n\nInstalatzaileak <code>.htaccess</code> fitxategi bat idatziko du bertan, baina horrek huts egiten badu zure datu base gordinera norbait sar daiteke.\nErabiltzaileen datu gordinak (helbide elektronikoak, pasahitzak), ezabatutako berrikusketa eta gainontzeko datu mugatuak ere barnean hartuz.\n\nDatu-basea beste nonbait jartzearen inguruan hausnartu, adibidez, <code>/var/lib/mediawiki/yourwiki</code>-n.",
"config-oracle-def-ts": "Taula-toki lehenetsia:",
"config-oracle-temp-ts": "Aldi baterako taula:",
- "config-type-mysql": "MySQL (edo bateragarria)",
+ "config-type-mysql": "MariaDB, MySQL edo bateragarria",
"config-type-postgres": "PostgreSQL",
"config-type-sqlite": "SQLite",
"config-type-oracle": "Oracle",
{
"@metadata": {
"authors": [
- "Giromin Cangiaxo"
+ "Giromin Cangiaxo",
+ "Fitoschido"
]
},
"config-desc": "Programma de installaçion pe MediaWiki",
"config-sqlite-dir-help": "SQLite o memorizza tutti i dæti inte 'n unnico file.\n\nA directory che t'indichiæ a dev'ese scrivibile da-o serviou web durante l'instalaçion.\n\nA dev'ese <strong>non acescibbile via web</strong>, l'è pe questo che no a mettemmo donde gh'è i file PHP.\n\nL'instalou o ghe scriviâ insemme un file <code>.htaccess</code>, ma se o tentativo o falisse quarcun poriæ avei accesso a-o database sgroeuzzo.\nQuesto o l'includde di dæti utente sgroeuzzi (adressi, password çiffræ) coscì comme de vercsioin eliminæ e atri dæti a accesso limitou da wiki.\n\nConsciddera a-a dreitua l'oportunitæ d'alugâ o database da quarch'atra parte, prezempio in <code>/var/lib/mediawiki/tuowiki</code>.",
"config-oracle-def-ts": "Tablespace pe difetto:",
"config-oracle-temp-ts": "Tablespace tempoannio:",
- "config-type-mysql": "MySQL (ò compatibbile)",
+ "config-type-mysql": "MariaDB, MySQL ò compatibbile",
"config-type-mssql": "Microsoft SQL Server",
"config-support-info": "MediaWiki o supporta i seguenti scistemi de database:\n\n$1\n\nSe fra quelli elencæ chì de sotta no ti veddi o scistema de database che ti voriesci doeuviâ, segui e instruçioin inganciæ de d'ato pe abilitâ o supporto.",
"config-dbsupport-mysql": "* [{{int:version-db-mysql-url}} MySQL] a l'è a primma scelta pe MediaWiki e a l'è quella megio suportâ. MediaWiki a fonçion-a ascì con [{{int:version-db-mariadb-url}} MariaDB] e [{{int:version-db-percona-url}} Percona Server], che son compatibbili con MySQL.([https://secure.php.net/manual/en/mysqli.installation.php Comme compilâ PHP con suporto MySQL])",
"Cedric31",
"Jfblanc",
"Seb35",
- "Nicolas Eynaud"
+ "Nicolas Eynaud",
+ "Fitoschido"
]
},
"config-desc": "Lo programa d’installacion de MediaWiki",
"config-sqlite-dir": "Dorsièr de las donadas SQLite :",
"config-oracle-def-ts": "Espaci d'emmagazinatge (''tablespace'') per defaut :",
"config-oracle-temp-ts": "Espaci d'emmagazinatge (''tablespace'') temporari :",
- "config-type-mysql": "MySQL (o compatible)",
+ "config-type-mysql": "MariaDB, MySQL o compatible",
"config-type-mssql": "Microsoft SQL Server",
"config-header-mysql": "Paramètres de MySQL",
"config-header-postgres": "Paramètres de PostgreSQL",
"Stelistcristi",
"XXN",
"Tuxilina",
- "Strainu"
+ "Strainu",
+ "Fitoschido"
]
},
"config-desc": "Programul de instalare pentru MediaWiki",
"config-sqlite-dir": "Director de date SQLite:",
"config-oracle-def-ts": "Spațiu de stocare („tablespace”) implicit:",
"config-oracle-temp-ts": "Spațiu de stocare („tablespace”) temporar:",
- "config-type-mysql": "MySQL (sau compatibil)",
+ "config-type-mysql": "MariaDB, MySQL sau compatibil",
"config-type-mssql": "Microsoft SQL Server",
"config-header-mysql": "Setările MySQL",
"config-header-postgres": "Setări PostgreSQL",
"config-install-mainpage-failed": "Не вдається вставити головну сторінку: $1",
"config-install-done": "<strong>Вітаємо!</strong>\nВи успішно встановили MediaWiki.\n\nІнсталятор згенерував файл <code>LocalSettings.php</code>, який містить усі Ваші налаштування.\n\nВам необхідно завантажити його і помістити у кореневу папку Вашої вікі (туди ж, де index.php). Завантаження мало початись автоматично.\n\nЯкщо завантаження не почалось або Ви його скасували, можете заново його почати, натиснувши на посилання внизу:\n\n$3\n\n<strong>Примітка</strong>: Якщо Ви не зробите цього зараз, цей файл не буде доступним пізніше, коли Ви вийдете з встановлення, не скачавши його.\n\nПісля виконання дій, описаних вище, Ви зможете <strong>[$2 увійти у свою вікі]</strong>.",
"config-install-done-path": "<strong>Вітаємо!</strong>\nВи встановили Медіавікі.\n\nІнсталятор створив файл <code>LocalSettings.php</code>.\nУ ньому містяться всі Ваші налаштування.\n\nВам потрібно завантажити його й помістити в <code>$4</code>. Завантаження повинно було автоматично розпочатись.\n\nЯкщо завантаження не було запропоновано, або Ви його скасували, Ви можете перезапустити завантаження натиснувши на посилання нижче:\n\n$3\n\n<strong>Зверніть увагу:</strong> Якщо Ви не зробите це зараз, цей згенерований файл налаштувань не буде доступним для Вас пізніше якщо Ви вийдете зі встановлення не завантаживши його.\n\nКоли це було зроблено Ви можете <strong>[$2 зайти до своєї вікі]</strong>.",
- "config-install-success": "Mediawiki Ñ\83Ñ\81пÑ\96Ñ\88но вÑ\81Ñ\82ановлено. Ð\97аÑ\80аз ви можеÑ\82е пеÑ\80ейÑ\82и до <$1$2>, Ñ\89об пеÑ\80еглÑ\8fнÑ\83Ñ\82и Ñ\81воÑ\8e вÑ\96кÑ\96. ЯкÑ\89о Ñ\83 ваÑ\81 Ñ\94 пиÑ\82аннÑ\8f, ознайомÑ\82еÑ\81Ñ\8f з наÑ\88им FAQ: <https://www.mediawiki.org/wiki/Manual:FAQ> або використовуйте один з форумів підтримки, які вказано на цій сторінці.",
+ "config-install-success": "Mediawiki Ñ\83Ñ\81пÑ\96Ñ\88но вÑ\81Ñ\82ановлено. Ð\97аÑ\80аз Ð\92и можеÑ\82е пеÑ\80ейÑ\82и до <$1$2>, Ñ\89об пеÑ\80еглÑ\8fнÑ\83Ñ\82и Ñ\81воÑ\8e вÑ\96кÑ\96. ЯкÑ\89о Ñ\83 Ð\92аÑ\81 Ñ\94 пиÑ\82аннÑ\8f, ознайомÑ\82еÑ\81Ñ\8f з наÑ\88им FAQ: <https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ> або використовуйте один з форумів підтримки, які вказано на цій сторінці.",
"config-download-localsettings": "Завантажити <code>LocalSettings.php</code>",
"config-help": "допомога",
"config-help-tooltip": "натисніть, щоб розгорнути",
"config-nofile": "Файл \"$1\" не знайдено. Його видалено?",
- "config-extension-link": "Чи знаєте ви, що ваше вікі підтримує [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Extensions розширення]?\n\nВи можете переглядати [https://www.mediawiki.org/wiki/Special:MyLanguage/Category:Extensions_by_category розширення по категорії] або в [https://www.mediawiki.org/wiki/Extension_Matrix матрицю розширень] щоб побачити повний список розширень.",
+ "config-extension-link": "Чи знали Ви, що Ваша вікі підтримує [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Extensions розширення]?\n\nМожете переглянути [https://www.mediawiki.org/wiki/Special:MyLanguage/Category:Extensions_by_category розширення за категорією]",
"config-skins-screenshots": "$1 (скріншоти: $2)",
"config-extensions-requires": "$1 (вимагає $2)",
"config-screenshot": "скріншот",
+ "config-extension-not-found": "Не вдалося знайти файл реєстрації для розширення «$1»",
+ "config-extension-dependency": "При встановленні розширення «$1» виникла помилка залежності: $2",
"mainpagetext": "<strong>Програмне забезпечення «MediaWiki» встановлено.</strong>",
"mainpagedocfooter": "Інформацію про роботу з цією вікі можна знайти на сторінці [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Довідка:Вміст].\n\n== Деякі корисні ресурси ==\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Список налаштувань];\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Часті питання з приводу MediaWiki];\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Розсилка повідомлень про появу нових версій MediaWiki];\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Локалізувати MediaWiki своєю мовою]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Дізнатися, як боротися зі спамом у своїй вікі]"
}
*/
protected $typeTable = [];
- /** constructor */
function __construct() {
// Construct versioned type arrays from the base type array plus additions
$types = $this->baseTypeTable;
* );
* @endcode
*
+ * Example usage (key that is expensive with too many DB dependencies for "check keys"):
+ * @code
+ * $catToys = $cache->getWithSetCallback(
+ * // Key to store the cached value under
+ * $cache->makeKey( 'cat-toys', $catId ),
+ * // Time-to-live (seconds)
+ * $cache::TTL_HOUR,
+ * // Function that derives the new key value
+ * function ( $oldValue, &$ttl, array &$setOpts ) {
+ * // Determine new value from the DB
+ * $dbr = wfGetDB( DB_REPLICA );
+ * // Account for any snapshot/replica DB lag
+ * $setOpts += Database::getCacheSetOptions( $dbr );
+ *
+ * return CatToys::newFromResults( $dbr->select( ... ) );
+ * },
+ * [
+ * // Get the highest timestamp of any of the cat's toys
+ * 'touchedCallback' => function ( $value ) use ( $catId ) {
+ * $dbr = wfGetDB( DB_REPLICA );
+ * $ts = $dbr->selectField( 'cat_toys', 'MAX(ct_touched)', ... );
+ *
+ * return wfTimestampOrNull( TS_UNIX, $ts );
+ * },
+ * // Avoid DB queries for repeated access
+ * 'pcTTL' => $cache::TTL_PROC_SHORT
+ * ]
+ * );
+ * @endcode
+ *
* Example usage (hot key holding most recent 100 events):
* @code
* $lastCatActions = $cache->getWithSetCallback(
* expired for this specified time. This is useful if adaptiveTTL() is used on the old
* value's as-of time when it is verified as still being correct.
* Default: WANObjectCache::STALE_TTL_NONE
- * - touchedCallback: A callback that takes the current value and returns a timestamp that
- * indicates the last time a dynamic dependency changed. Null can be returned if there
+ * - touchedCallback: A callback that takes the current value and returns a UNIX timestamp
+ * indicating the last time a dynamic dependency changed. Null can be returned if there
* are no relevant dependency changes to check. This can be used to check against things
* like last-modified times of files or DB timestamp fields. This should generally not be
* used for small and easily queried values in a DB if the callback itself ends up doing
}
$this->loadPageData( 'fromdbmaster' );
+ $this->mTitle->loadRestrictions( null, Title::READ_LATEST );
$restrictionTypes = $this->mTitle->getRestrictionTypes();
$id = $this->getId();
/**
* @param array $policies
* @param array $checks mapping statement to its checking function. Checking functions are
- * called with the policy value for this user, the user object, and the password to check.
+ * called with the policy value for this user, the user object, and the password to check.
*/
public function __construct( array $policies, array $checks ) {
if ( !isset( $policies['default'] ) ) {
* @param User $user who's policy we are checking
* @param string $password the password to check
* @return Status error to indicate the password didn't meet the policy, or fatal to
- * indicate the user shouldn't be allowed to login.
+ * indicate the user shouldn't be allowed to login. The status value will be an array,
+ * potentially with the following keys:
+ * - forceChange: do not allow the user to login without changing the password if invalid.
*/
public function checkUserPassword( User $user, $password ) {
$effectivePolicy = $this->getPoliciesForUser( $user );
* @param string $password the password to check
* @param array $groups list of groups to which we assume the user belongs
* @return Status error to indicate the password didn't meet the policy, or fatal to
- * indicate the user shouldn't be allowed to login.
+ * indicate the user shouldn't be allowed to login. The status value will be an array,
+ * potentially with the following keys:
+ * - forceChange: do not allow the user to login without changing the password if invalid.
*/
public function checkUserPasswordForGroups( User $user, $password, array $groups ) {
$effectivePolicy = self::getPoliciesForGroups(
* @return Status
*/
private function checkPolicies( User $user, $password, $policies, $policyCheckFunctions ) {
- $status = Status::newGood();
- foreach ( $policies as $policy => $value ) {
+ $status = Status::newGood( [] );
+ $forceChange = false;
+ foreach ( $policies as $policy => $settings ) {
if ( !isset( $policyCheckFunctions[$policy] ) ) {
throw new DomainException( "Invalid password policy config. No check defined for '$policy'." );
}
- $status->merge(
- call_user_func(
- $policyCheckFunctions[$policy],
- $value,
- $user,
- $password
- )
+ if ( !is_array( $settings ) ) {
+ // legacy format
+ $settings = [ 'value' => $settings ];
+ }
+ if ( !array_key_exists( 'value', $settings ) ) {
+ throw new DomainException( "Invalid password policy config. No value defined for '$policy'." );
+ }
+ $value = $settings['value'];
+ /** @var StatusValue $policyStatus */
+ $policyStatus = call_user_func(
+ $policyCheckFunctions[$policy],
+ $value,
+ $user,
+ $password
);
+ if ( !$policyStatus->isGood() && !empty( $settings['forceChange'] ) ) {
+ $forceChange = true;
+ }
+ $status->merge( $policyStatus );
+ }
+ if ( $status->isOK() && $forceChange ) {
+ $status->value['forceChange'] = true;
}
return $status;
}
/**
* Utility function to get a policy that is the most restrictive of $p1 and $p2. For
* simplicity, we setup the policy values so the maximum value is always more restrictive.
+ * It is also used recursively to merge settings within the same policy.
* @param array $p1
* @param array $p2
* @return array containing the more restrictive values of $p1 and $p2
$ret[$key] = $p2[$key];
} elseif ( !isset( $p2[$key] ) ) {
$ret[$key] = $p1[$key];
- } else {
+ } elseif ( !is_array( $p1[$key] ) && !is_array( $p2[$key] ) ) {
$ret[$key] = max( $p1[$key], $p2[$key] );
+ } else {
+ if ( !is_array( $p1[$key] ) ) {
+ $p1[$key] = [ 'value' => $p1[$key] ];
+ } elseif ( !is_array( $p2[$key] ) ) {
+ $p2[$key] = [ 'value' => $p2[$key] ];
+ }
+ $ret[$key] = self::maxOfPolicies( $p1[$key], $p2[$key] );
}
}
return $ret;
'type' => 'user',
'ipallowed' => true,
'iprange' => true,
- 'label-message' => 'ipaddressorusername',
'id' => 'mw-bi-target',
'size' => '45',
'autofocus' => true,
'required' => true,
'validation-callback' => [ __CLASS__, 'validateTargetField' ],
+ 'section' => 'target',
+ ];
+
+ $a['Editing'] = [
+ 'type' => 'check',
+ 'label-message' => 'block-prevent-edit',
+ 'default' => true,
+ 'section' => 'actions',
+ 'disabled' => $enablePartialBlocks ? false : true,
];
if ( $enablePartialBlocks ) {
$a['EditingRestriction'] = [
'type' => 'radio',
- 'label' => $this->msg( 'ipb-type-label' )->text(),
+ 'cssclass' => 'mw-block-editing-restriction',
'options' => [
$this->msg( 'ipb-sitewide' )->text() => 'sitewide',
$this->msg( 'ipb-partial' )->text() => 'partial',
],
+ 'section' => 'actions',
];
$a['PageRestrictions'] = [
'type' => 'titlesmultiselect',
'input' => [
'autocomplete' => false
],
+ 'section' => 'actions',
];
}
- $a['Expiry'] = [
- 'type' => 'expiry',
- 'label-message' => 'ipbexpiry',
- 'required' => true,
- 'options' => $suggestedDurations,
- 'default' => $this->msg( 'ipb-default-expiry' )->inContentLanguage()->text(),
- ];
-
- $a['Reason'] = [
- 'type' => 'selectandother',
- // HTML maxlength uses "UTF-16 code units", which means that characters outside BMP
- // (e.g. emojis) count for two each. This limit is overridden in JS to instead count
- // Unicode codepoints (or 255 UTF-8 bytes for old schema).
- 'maxlength' => $oldCommentSchema ? 255 : CommentStore::COMMENT_CHARACTER_LIMIT,
- 'maxlength-unit' => 'codepoints',
- 'label-message' => 'ipbreason',
- 'options-message' => 'ipbreason-dropdown',
- ];
-
$a['CreateAccount'] = [
'type' => 'check',
'label-message' => 'ipbcreateaccount',
'default' => true,
+ 'section' => 'actions',
];
if ( self::canBlockEmail( $user ) ) {
$a['DisableEmail'] = [
'type' => 'check',
'label-message' => 'ipbemailban',
+ 'section' => 'actions',
];
}
'type' => 'check',
'label-message' => 'ipb-disableusertalk',
'default' => false,
+ 'section' => 'actions',
];
}
+ $a['Expiry'] = [
+ 'type' => 'expiry',
+ 'required' => true,
+ 'options' => $suggestedDurations,
+ 'default' => $this->msg( 'ipb-default-expiry' )->inContentLanguage()->text(),
+ 'section' => 'expiry',
+ ];
+
+ $a['Reason'] = [
+ 'type' => 'selectandother',
+ // HTML maxlength uses "UTF-16 code units", which means that characters outside BMP
+ // (e.g. emojis) count for two each. This limit is overridden in JS to instead count
+ // Unicode codepoints (or 255 UTF-8 bytes for old schema).
+ 'maxlength' => $oldCommentSchema ? 255 : CommentStore::COMMENT_CHARACTER_LIMIT,
+ 'maxlength-unit' => 'codepoints',
+ 'options-message' => 'ipbreason-dropdown',
+ 'section' => 'reason',
+ ];
+
$a['AutoBlock'] = [
'type' => 'check',
'label-message' => 'ipbenableautoblock',
'default' => true,
+ 'section' => 'options',
];
# Allow some users to hide name from block log, blocklist and listusers
'type' => 'check',
'label-message' => 'ipbhidename',
'cssclass' => 'mw-block-hideuser',
+ 'section' => 'options',
];
}
$a['Watch'] = [
'type' => 'check',
'label-message' => 'ipbwatchuser',
+ 'section' => 'options',
];
}
'type' => 'check',
'label-message' => 'ipb-hardblock',
'default' => false,
+ 'section' => 'options',
];
# This is basically a copy of the Target field, but the user can't change it, so we
$targetUser->clearInstanceCache(); // T40989
}
- $checkValue = explode( ',', $request->getVal( 'conflictcheck-originalgroups' ) );
+ $conflictCheck = $request->getVal( 'conflictcheck-originalgroups' );
+ $conflictCheck = ( $conflictCheck === '' ) ? [] : explode( ',', $conflictCheck );
$userGroups = $targetUser->getGroups();
- if ( $userGroups !== $checkValue ) {
- $out->addWikiMsg( 'userrights-conflict' );
+ if ( $userGroups !== $conflictCheck ) {
+ $out->wrapWikiMsg( '<span class="error">$1</span>', 'userrights-conflict' );
} else {
$status = $this->saveUserGroups(
$this->mTarget,
}
# image filters can pull in url, which could be svg that executes scripts
+ # Only allow url( "#foo" ). Do not allow url( http://example.com )
if ( $strippedElement == 'image'
&& $stripped == 'filter'
- && preg_match( '!url\s*\(!sim', $value )
+ && preg_match( '!url\s*\(\s*["\']?[^#]!sim', $value )
) {
wfDebug( __METHOD__ . ": Found image filter with url: "
. "\"<$strippedElement $stripped='$value'...\" in uploaded file.\n" );
/**
* Check if this is a valid password for this user
*
- * Create a Status object based on the password's validity.
- * The Status should be set to fatal if the user should not
- * be allowed to log in, and should have any errors that
- * would block changing the password.
- *
- * If the return value of this is not OK, the password
- * should not be checked. If the return value is not Good,
- * the password can be checked, but the user should not be
- * able to set their password to this.
+ * Returns a Status object with a set of messages describing
+ * problems with the password. If the return status is fatal,
+ * the action should be refused and the password should not be
+ * checked at all (this is mainly meant for DoS mitigation).
+ * If the return value is OK but not good, the password can be checked,
+ * but the user should not be able to set their password to this.
+ * The value of the returned Status object will be an array which
+ * can have the following fields:
+ * - forceChange (bool): if set to true, the user should not be
+ * allowed to log with this password unless they change it during
+ * the login process (see ResetPasswordSecondaryAuthenticationProvider).
*
* @param string $password Desired password
* @return Status
$wgPasswordPolicy['checks']
);
- $status = Status::newGood();
+ $status = Status::newGood( [] );
$result = false; // init $result to false for the internal checks
if ( !Hooks::run( 'isValidPassword', [ $password, &$result, $this ] ) ) {
}
if ( $result === false ) {
- $status->merge( $upp->checkUserPassword( $this, $password ) );
+ $status->merge( $upp->checkUserPassword( $this, $password ), true );
return $status;
} elseif ( $result === true ) {
return $status;
/**
* Get whether the user is blocked from using Special:Upload
*
+ * @since 1.33
* @return bool
*/
public function isBlockedFromUpload() {
const GENERAL_CD_ENCRYPTED = 13;
/**
- * Private constructor
* @param string $fileName
* @param callable $callback
* @param array $options
protected $forcedOff = [];
/**
- * CheckMatrixWidget constructor
- *
* Operates similarly to MultiSelectWidget, but instead of using an array of
* options, uses an array of rows and an array of columns to dynamically
* construct a matrix of options. The tags used to identify a particular cell
"statistics": "Статыстыка",
"statistics-header-pages": "Статыстыка старонак",
"statistics-header-edits": "Статыстыка рэдагаваньняў",
- "statistics-header-users": "СÑ\82аÑ\82Ñ\8bÑ\81Ñ\82Ñ\8bка Ñ\9eдзелÑ\83",
+ "statistics-header-users": "СÑ\82аÑ\82Ñ\8bÑ\81Ñ\82Ñ\8bка Ñ\9eдзелÑ\8cнÑ\96каÑ\9e",
"statistics-header-hooks": "Іншая статыстыка",
"statistics-articles": "Колькасьць старонак са зьместам",
"statistics-pages": "Колькасьць старонак",
"logentry-delete-delete": "$1 {{GENDER:$2|выдаліў|выдаліла}} старонку $3",
"logentry-delete-delete_redir": "$1 {{GENDER:$2|выдаліў|выдаліла}} перанакіраванне $3 шляхам перазапісу",
"logentry-delete-restore": "$1 {{GENDER:$2|аднавіў|аднавіла}} старонку $3 ($4)",
+ "logentry-delete-restore-nocount": "$1 {{GENDER:$2|аднавіў|аднавіла}} старонку $3",
"restore-count-revisions": "{{PLURAL:$1|1 версія|$1 версіі|$1 версій}}",
"logentry-delete-event": "$1 {{GENDER:$2|змяніў|змяніла}} бачнасць {{PLURAL:$5|запісу журнала|$5 запісаў журнала}} $3: $4",
"logentry-delete-revision": "$1 {{GENDER:$2|змяніў|змяніла}} бачнасць {{PLURAL:$5|версіі|$5 версій|$5 версій}} старонкі $3: $4",
},
"tog-underline": "هومپیڤٱندا زیر خٱتدار",
"tog-hideminor": "دٱم تی نٱبیڌن آلشتا کۊچیر",
+ "tog-hidepatrolled": "بؽدیار نڤیڌن آلشڌٱل کوچیر",
+ "tog-newpageshidepatrolled": "بٱلٛگیٱل لرهٱرڌاْ زاْ فاٛئرست بٱلٛگیٱل نۊ بؽدیار ڤۊهاْ",
+ "tog-hidecategorization": "بؽدیارنیڌن رٱئڌاٛڤٱنی بٱلٛگیٱل",
"tog-extendwatchlist": "گپ کردن نوم گه آ مو سی دیئن همه آلشتا نه فقط هونو که بیشتر ز همه انجوم ابون.",
"tog-usenewrc": "جٱرغاٛ کاری آلشتا ڤا آلشتکاری بٱلگاٛیلسۊن و سئیل بٱرگسۊن",
"tog-numberheadings": "شوماراٛ ڤٱندن خودٱنجوم سی سربٱلگاٛیل",
"tog-watchdefault": "اٛزاف کردن بٱلگاٛیٱل و جانیایٱلی کاٛ مو مئن سئیل برگوم ڤیرایشدسۊن کردوماٛ",
"tog-watchmoves": "اْزاف کردن بٱلگاٛیٱلی کاٛ خوم جا ب جاسۊن کردوماٛ سی سئل بٱرگوم",
"tog-watchdeletion": "اٛزاف کردن بٱلگاٛیٱل و جانیایٱلی کاٛ خوم ز مئن سئیل بٱرگوم پاکسا کردوماٛ",
- "tog-minordefault": "علامت نهادن به اصلاحات ناقص",
- "tog-previewontop": "نشودادن پیش نمایش قبل از یوکه جعبه یا کادر اصلاح بوه",
- "tog-previewonfirst": "نشو دادن پیش نمایش دراصلاح اول",
- "tog-enotifwatchlistpages": "امیل به مو وقتی که صفحه ای که منه فهرست نمایش مونه تغییر کرد",
+ "tog-watchuploads": "پٱرڤٱنداٛیٱل نۊئی کاْ باراْنم ڤاْ فاٛئرسڌ دیناگریٱل مو بالاڤٱن ڤۊ",
+ "tog-watchrollback": "بالاڤٱن کرڌن بٱلٛگیٱلؽ کاْ اْؤورگٱرنیم ڤاْ فاٛئرسڌ دیناگریٱل مو",
+ "tog-minordefault": "دیاری کردن جۊر ڤیرایشتا ناقس",
+ "tog-previewontop": "دیاری کردن پیش سئیل پیش ز ڤیرایشد جٱڤٱ",
+ "tog-previewonfirst": "دیاری کردن پیش سئیل مئن ٱڤلین ڤیرایشد",
+ "tog-enotifwatchlistpages": "هر گاتی کاٛ یٱ بٱلگٱ یا یٱ جانیا مئن سئیل بٱرگ مۊ آلشد ابۊ بوم خٱڤٱر بڌین",
"tog-enotifusertalkpages": "امیل به مو وقتی که صفحه گفتگوی مو تغییر کرد",
"tog-enotifminoredits": "او بٱلگاٛیٱل و جانیایٱلی کاٛ ڤیرایشد کۊچیر و ناقس دارن بفرشن سی ٱنجوماناموم",
"tog-enotifrevealaddr": "دیاری کردن تیرنشۊن ٱنجوماناماٛ مو مئن دیارکاری ایمیلی",
"tog-shownumberswatching": "نشودادن شماره کاربران درحال کار یاتماشا",
"tog-oldsig": "اٛمزا ایسئنی",
- "tog-fancysig": "اÙ\85ضاÛ\8cÙ\84 Ù\86اتموم",
+ "tog-fancysig": "اÙ\9bÙ\85زاÛ\8cÙ±Ù\84 Ù\86اتٱموم",
"tog-uselivepreview": "پیش سئیل زندٱ نٱ ڤٱن ڤا کار",
"tog-forceeditsummary": "یادآوری سریع به مو هنگام اصلاح عقیم وخلاصه",
"tog-watchlisthideown": "قایم کردن اصلاحات مو زه لیست پیگیریها",
"tog-watchlisthidebots": "قایم کردن اصلاحات بوت زه لیست پیگیریها",
"tog-watchlisthideminor": "قایم کردن اصلاحات ریز زه لیست پیگیریها",
"tog-watchlisthideliu": "قایم کردن اصلاحات انجام وابیده بوسیله کاربران داخل سیستم وابیده زه لیست پیگیریها",
+ "tog-watchlistreloadautomatically": "راتؽ کاْ یٱ پاْلایاٛ آلشڌآڤیڌ فاٛئرسڌ دیناگری بؽنگوڌ(خوڌکار)ڤ رۊز ڤۊهاْ(هوجاْ ڤاْ جاڤا اسکریپت)",
+ "tog-watchlistunwatchlinks": "فرٱنیڌن دیاری کونٱنڌیٱل نڤیڌ دیناگری/دیناگری ({{int:Watchlist-unwatch}}/{{int:Watchlist-unwatch-undo}})ڤاْ بٱلٛگیٱل دیناگری آلشڌدار (سی عملیات کلؽز ۉ بلؽز کرڌن، جاڤاسکریپت هوجاْ هؽڌآ)",
"tog-watchlisthideanons": "قایم کردن اصلاحات انجام شده بوسیله کاربران داخل سیستم نشده زه لیست پیگیری",
+ "tog-watchlisthidepatrolled": "تٱپنیڌن پٱخڌارٱل ساوا زاْ لیسڌ دیناگریٱل\nتٱپنیڌن پٱخڌارٱل بۊت زاْ لیسڌ دیناگریٱل\nتٱپنیڌن پٱخڌارٱل مو زاْ لیسڌ دیناگریٱل",
+ "tog-watchlisthidecategorization": "تٱپنیڌن رٱئڌڤٱنی بٱلٛگیٱل",
"tog-ccmeonemails": "ارسال کپی امیلهایی که مو به کاربران دیه ارسال کردم به مو",
- "tog-diffonly": "نشو نده صفحه ای که دارای محتوای متفاوت زیره",
- "tog-showhiddencats": "نشودادن دسته بندیهای قایم شده",
- "underline-always": "Ù\87Ù\85Û\8cØ´Ù\87",
- "underline-never": "هرگز",
- "underline-default": "Ù¾Û\88سدإ Ø¢ دÙ\88ڤارتإ Ù\86Ù\8aأر Ù¾Ù\8aØ´ Ù\81رز",
+ "tog-diffonly": "بٱلگاٛیی نٱ کاٛ مئنۊناٛ فٱرخداراٛ نشۊن مٱڌاٛ",
+ "tog-showhiddencats": "دیاری کردن جٱرغاٛ بٱندیٱل نادیار",
+ "underline-always": "Ù\87Ù\85Û\8cشٱ",
+ "underline-never": "هیژگات",
+ "underline-default": "Ù¾Û\8aسداÙ\9b Û\8cا دڤÙ\88ارتاÙ\9b Ù\86Û\8cٱر Ù\85Û\8cزÛ\8aÙ\86کارÛ\8c ڤابÛ\8cÚ\8cاÙ\9b",
"editfont-sansserif": "فونت سان سئریف",
"editfont-serif": "فونت سريف",
- "sunday": "یکشنبه",
- "monday": "دوشنبه",
- "tuesday": "سهشنبه",
- "wednesday": "چهارشنبه",
- "thursday": "Ù¾Ù\86جشÙ\86بÙ\87",
- "friday": "جÙ\85عÙ\87",
- "saturday": "Ø´Ù\86بÙ\87",
- "sun": "یکشنبه",
- "mon": "دÙ\88Ø´Ù\86بÙ\87",
- "tue": "سهشنبه",
- "wed": "چهارشنبه",
- "thu": "Ù¾Ù\86جشÙ\86بÙ\87",
- "fri": "جÙ\85عÙ\87",
- "sat": "Ø´Ù\86بÙ\87",
- "january": "جانڤیە",
+ "sunday": "یٱشٱمبڌ",
+ "monday": "دۊشٱمبڌ",
+ "tuesday": "ساٛشٱمبڌ",
+ "wednesday": "چارشٱمبڌ",
+ "thursday": "پٱÙ\86شٱÙ\85بÚ\8c",
+ "friday": "جÙ\88Ù\85Ù±",
+ "saturday": "شٱÙ\85بÚ\8c",
+ "sun": "یٱشٱمبڌ",
+ "mon": "دÙ\88شٱÙ\85بÚ\8c",
+ "tue": "ساٛشٱمبڌ",
+ "wed": "چارشٱمبڌ",
+ "thu": "پٱÙ\86شٱÙ\85بÚ\8c",
+ "fri": "جÙ\88Ù\85Ù±",
+ "sat": "شٱÙ\85بÚ\8c",
+ "january": "جانڤیٱ",
"february": "فڤریٱ",
"march": "مارس",
"april": "آڤریل",
- "may_long": "مە",
- "june": "جوٙأن",
- "july": "جوٙئیە",
+ "may_long": "مئی",
+ "june": "جۊٱن",
+ "july": "جۊئیٱ",
"august": "آگوست",
- "september": "سئپتامر",
- "october": "ئوکتوبر",
+ "september": "سپتامر",
+ "october": "اوکتوبر",
"november": "نوڤامر",
- "december": "دئساÙ\85ر",
- "january-gen": "ژانویه",
- "february-gen": "فوریه",
+ "december": "دسامر",
+ "january-gen": "جانڤیٱ",
+ "february-gen": "فڤریٱ",
"march-gen": "مارس",
- "april-gen": "آوریل",
- "may-gen": "مه",
- "june-gen": "ژوئن",
- "july-gen": "ژوئیه",
- "august-gen": "اÙ\88ت",
+ "april-gen": "آڤریل",
+ "may-gen": "مئی",
+ "june-gen": "جۊٱن",
+ "july-gen": "جۊئیٱ",
+ "august-gen": "Ø¢Ú¯Ù\88ست",
"september-gen": "سپتامبر",
- "october-gen": "اکتبر",
- "november-gen": "نوامبر",
- "december-gen": "دساÙ\85بر",
- "jan": "جانڤیە",
- "feb": "فئڤریە",
+ "october-gen": "اوکتوبر",
+ "november-gen": "نوڤامر",
+ "december-gen": "دسامر",
+ "jan": "جانڤیٱ",
+ "feb": "فڤریٱ",
"mar": "مارس",
"apr": "آڤریل",
- "may": "مە",
- "jun": "جوٙأن",
- "jul": "جوٙئیە",
+ "may": "مئی",
+ "jun": "جۊٱن",
+ "jul": "جۊلای",
"aug": "آگوست",
- "sep": "سئپتامر",
- "oct": "ئوکتوبر",
+ "sep": "سپتامر",
+ "oct": "اوکتوبر",
"nov": "نوڤامر",
- "dec": "دئساÙ\85ر",
- "january-date": "جانڤيأ $1",
- "february-date": "فإڤريأ $1",
+ "dec": "دسامر",
+ "january-date": "جانڤيٱ $1",
+ "february-date": "فڤریٱ $1",
"march-date": "مارس $1",
"april-date": "آڤريل $1",
- "may-date": "Ù\85Ø¥ی $1",
- "june-date": "جÛ\88Ø£ن $1",
+ "may-date": "Ù\85ئی $1",
+ "june-date": "جÛ\8aÙ±ن $1",
"july-date": "جۈلای $1",
"august-date": "آگوست $1",
"september-date": "سپتامر $1",
"category-file-count": "{{PLURAL:$2|ای دسته فقط فایلهای زیر راداره.|ذیل الذکر{{PLURAL:$1|فایل است|$1 فایلهاهستند}} درای دسته, بیشترزه$2 کل.}}",
"category-file-count-limited": "ذیل الذکر {{PLURAL:$1|فایل است|$1 فایلها هستند}} درآن دسته جریانی.",
"listingcontinuesabbrev": "دنباله",
+ "noindex-category": "بٱلٛگاْیٱل نمایاْ ناڤیڌاْ",
"about": "درباره",
"article": "بلگه آ مینونه دار",
"newwindow": "(پنجره تازه واز کن)",
"yourtext": "متن ايسا",
"copyrightwarning": "لطفاً دقت کنین که درنظر گریده ابوه که همه شراکتهای ایسا {{SITENAME}} تحت «$2» منتشر ابون ).\n\n\n(سی دیدن جزئیات بیشتر به $1 برین\n\nایر نه خوین نوشتههاتو بیرحمانه اصلاح بوه و به دلخواه ارسال بوه، ایچو نفرستن.<br />\nدرضمن ایسادارین به ایما قول ادین که خودتو یونه نوشتین یا هونه زه یک منبع آزاد با مالکیت عمومی یا مثل هو ورداشتین. '''کارهای دارای کارهای دارای حق کپی رایت را بیاجازه نفرستین!'''',",
"templatesused": "{{PLURAL:$1|چوٙأ|چوٙأیل}} ب کار گرهڌأ ڤابيڌإ مإن اي بألگأ:",
- "templatesusedpreview": "قالبها یا الگوهای استفاده وابیده در ای پیش نمایش:",
+ "templatesusedpreview": "قالڤٱل یا اولگۊیٱل ڤاْ کار رٱئڌاْ مؽن ای نهانماو",
"template-protected": "(تحت حمایت)",
"template-semiprotected": "(نیمه حمایت وابیده)",
"hiddencategories": "اي بلگأ یکي ز أندوما {{PLURAL:$1|1 hidden category|$1 hidden categories}} إ:",
"nocreatetext": "{{SITENAME}}قابلیت درست کردن صفحات تازه را محدود کرده. ترین برگردین و صفحه موجود را اصلاح کنین یا اینکه [[Special:UserLogin|به سیستم داخل بوین یا حساب کاربری درست کنین]].",
+ "permissionserrors": "پٱلاْ:اْجازاْ ڤگرا",
"permissionserrorstext-withaction": "ايسا سی نياگري $2 سإلا\nنارين {{PLURAL:$1|دلیل|دليلا}}:",
- "recreate-moveddeleted-warn": "'''هشدار: ایسا در حال درست کردن دوباره صفحهای هدین که قبلاً حذف وابیده '''در نظر داشته بوین که ادامه اصلاح ای صفحه کار درستی هده یا نه. نمایه حذف مربوط به ای صفحه سی راحتی کار در ادامه اویده",
+ "recreate-moveddeleted-warn": "'''هوشڌار: ایسا هنؽ سٱرزاْنۊ بٱلٛگی ناْ ؤرکل اْکونین کاْ نهاتٱرپاکسا آڤیڌاْ '''مؽن فرگ داشڌ ڤۊهین کاْ آلشڌ ای بٱلٛگاْ کارؽ دوروساْ هؽڌا آ نٱ. نمایاٛ پاکسا مؽنڌار ڤا ای بٱلٛگاْ سی راهٱتی کار ڤا دینا آؤوڌاْ",
"moveddeleted-notice": "ای بٱلٛیاْ پاکسا آڤیڌاْ،ؤرداوناْ سیاهؽ پاکسا،هناڌاری ۉ کلٛ کرڌن ای بٱلٛیاْ ؤرتی نهاڌ آڤیڌاْ",
"content-model-wikitext": "ڤيکي تکست",
"content-model-javascript": "جاڤا إسکريپت",
+ "undo-failure": "سی نڤیڌن سلۊکی ڤا آلشڌکاریٱل مؽنجخائی ای آلشڌکاریناْ نؽڤۊ بؽ هرنڳ کرڌ",
"viewpagelogs": "نشودادن نمایه ها سی ای صفحه",
"currentrev": "نسخه جاری",
"currentrev-asof": "آخرين ڤانيأري جۈر $1",
"revdelete-log": "دلیل:",
"mergehistory-from": "بألگإ سرچشمأ:",
"mergehistory-reason": "دلیل:",
+ "mergelog": "سیائاْ ؤریٱک",
"history-title": "دڤارتإ دیئن ڤيرگار $1",
"difference-title": "فرخ مإنجقا ڤانإیريا \"$1\"",
"lineno": "سطر $1:",
"timezoneregion-asia": "آسيا",
"yourrealname": "نام واقعی:",
"prefs-help-realname": "ذکر نام واقعی اختیاریه ایر تصمیم به گدن بگیرین هنگام ارجاع به آثارتو و انتساب هونو به ایسا زه نام واقعیتو استفاده ابوه",
+ "group-bot": "روڤاتٱل",
"grouppage-sysop": "{{ns:project}}:مدیران",
"right-writeapi": "سي نڤشدن اي پ آی ڤأنين ڤاکار",
"newuserlogpage": "راسد ڤابیه وا کاریار",
"rightslog": "نمایه حقوق کاربر",
"action-edit": "ای بلگٱ نٱ ۋیرایشد کو",
+ "action-createaccount": "ڤاکل ای هساْڤ مؽنتوری",
"nchanges": "$1 {{PLURAL:$1|تغییر|تغییرات}}",
"enhancedrc-history": "ڤیرگار",
"recentchanges": "تغییرات اخیر",
"recentchanges-label-unpatrolled": "ای ويرايشت هنی تيه واداشت نوابيه",
"recentchanges-label-plusminus": "أندازإ بألگأ ب شومار اي بایتا آلشد کردإ.",
"recentchanges-legend-heading": "<strong>میراث:</strong>",
- "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (Ù\87Ù\85Ú\86Û\8cÙ\86Ù\88 ساÙ\9bÛ\8cÙ\84 [[Special:بÙ\84Ú¯Ù±Û\8cÙ±Ù\84 تازٱ|Ù\86Ù\88Ù\85Ú¯Ù± بÙ\84Ú¯Ù±Û\8cÙ±Ù\84 تازاÙ\9b]]) Ú©Ù\88Ù\86Û\8cÙ\86",
+ "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (Ù\87Ù±Ú\86Ù\88Ù\86Ù\88Ý© ڤاÙ\92[[Special:NewPages|Ù\81اÙ\9bئرست بٱÙ\84Ù\9bÚ¯Û\8cÙ±Ù\84 Ù\86Û\8a]] ساÙ\9bÙ\84 ؤÙ\88Ù±Ù\86Û\8cÙ\86)",
"rcnotefrom": "ۋازیر {{PLURAL:$5|ۋیرایشد|ۋیرایشدا}}ز ۋیرگار strong>$3, $4</strong> تا ۋیرگار <strong>$1</strong> دیاری اٛکونن .",
"rclistfrom": "نشودادن تغییرات تازه با شروع زه $3 $2",
"rcshowhideminor": "اصلاحات کوچیک $1",
"recentchangeslinked-feed": "تغییرات مرتبط",
"recentchangeslinked-toolbox": "تغییرات مرتبط",
"recentchangeslinked-title": "تغییرهای مرتبط با $1",
- "recentchangeslinked-summary": "Ù\86Ù\88Ù\85 Û\8cÙ± بÙ\84گاÙ\9b Ù\86Ù± ڤاÙ\85ؽ Ú©Ù\88Ù\86Û\8cÙ\86 تا Ø¢Ù\84Ø´Ú\8cکارÛ\8c بٱÙ\84Ù\9bگاÙ\9bÛ\8cÙ±Ù\84 کاÙ\92 ڤاÙ\92 Ù\87Ù\88Ù\85ؽÙ\86اÙ\9bÚ\8cار کرÚ\8cÛ\8cÙ\86 Û\8cا زاÙ\92 Ù\87Ù\88 Ù\85ؽÙ\86اÙ\9bÚ\8cارÛ\8c گرÛ\8cÚ\8cÛ\8cÙ\86 Ù\86اÙ\92 Ú¤Ú¤Û\8cÙ\86Û\8cÙ\86(سÛ\8c Ù\86Û\8cٱشÚ\8cÙ\86 Ù\87Ù\88Ù\85 Ú¤Ù±Ù\86Ù\88Ù\86اÙ\92 Û\8cÙ± بÙ\86Ú©Û\8a Ú\86Ù\88Ù\86Ù\88Ý© Ú\86Û\8c ائÛ\8c Ù\86اÙ\92 ڤزٱÙ\86Û\8cÙ\86:Ù\86Ù\88Ù\85 بÙ\88Ù\86Ú©Û\8a). \nØ¢Ù\84Ø´Ú\8cکارÛ\8cÙ±Ù\84 بÙ\84گاÙ\9bÛ\8cÙ±Ù\84ؽ کاÙ\92 Ù\85ؽÙ\86 سÛ\8cائاÙ\9b ساÙ\9bÛ\8cÙ\84 برگ اÛ\8cسا بÛ\8aÙ\86 Ú\86Ù\88Ù\86Ù\88 <strong>Ù\85اÙ\9bÙ\86 Ù¾Ù\88ر</strong> دÛ\8cارÛ\8c اÙ\9bÚ©Ù\88Ù\86Ù\86.",
+ "recentchangeslinked-summary": "Ù\86Ù\88Ù\85 Û\8cÙ± بٱÙ\84Ù\9bگاÙ\92 Ù\86اÙ\92 ڤاÙ\85ؽ Ú©Ù\88Ù\86Û\8cÙ\86 تا Ø¢Ù\84Ø´Ú\8cکارÛ\8c بٱÙ\84Ù\9bÚ¯Û\8cÙ±Ù\84 کاÙ\92 ڤاÙ\92 Ù\87Ù\88Ù\85ؽÙ\86Ú\8cارکرÚ\8cÛ\8cÙ\86اÙ\92 Û\8cا زاÙ\92 Ù\87Ù\88 Ù\85ؽÙ\86Ú\8cارÛ\8c گرÛ\8cÚ\8cÛ\8cÙ\86 Ù\86اÙ\92 Ú¤Ú¤Û\8cÙ\86Û\8cÙ\86(سÛ\8c Ù\86Û\8cٱشÚ\8cÙ\86 Ù\87Ù\88Ù\85 Ú¤Ù±Ù\86Ù\88Ù\86اÙ\92 Û\8cÙ± بÙ\86Ú©Û\8a Ú\86Ù\88Ù\86Ù\88Ý© Ú\86Û\8c ائÛ\8c Ù\86اÙ\92 ڤزٱÙ\86Û\8cÙ\86:Ù\86Ù\88Ù\85 بÙ\88Ù\86Ú©Û\8a). \nØ¢Ù\84Ø´Ú\8cکارÛ\8cÙ±Ù\84 بÙ\84Ù\9bÚ¯Û\8cÙ±Ù\84ؽ کاÙ\92 Ù\85ؽÙ\86[[Special:Watchlist|Ù\81اÙ\9bئرست دÛ\8cÙ\86اگرÛ\8cÙ±Ù\84 اؽسا]] Ù\87ؽÚ\8cÙ\86 <strong>ؤٱرÚ\8cار</strong> Ù\86Ù\85اÛ\8c اÙ\92Ú¤Û\8aÙ\87اÙ\92",
"recentchangeslinked-page": "نوم بألگە:",
"recentchangeslinked-to": "آلشتیایی که د بلگه یا هوم پیوند بینه وه جا بلگه دئیه بیه نشو بیه",
"upload": "آپلود فایل",
"filehist-datetime": "تاریخ/زمان",
"filehist-thumb": "عسگ کۈچير وابيه",
"filehist-thumbtext": "كۈچير کردن سی نوسقإ $1",
+ "filehist-nothumb": "نڤیڌن بٱن کلکی",
"filehist-user": "کاربر",
"filehist-dimensions": "ابعاد",
"filehist-filesize": "اندازه فایل",
"randomredirect": "تغییر مسیر اتفاقی",
"statistics": "آمار",
"doubleredirects": "تغییر مسیر دوبله",
+ "double-redirect-fixer": "ساموݩکار آلشڌتورٱل",
"brokenredirects": "تغییرمسیرهای اشکسته وخراو",
"withoutinterwiki": "صفحات بدون لینک های زبانی میان ویکی",
"fewestrevisions": "صفحات با کمترین تعداداصلاحات وتجدیدنظرها",
"booksources": "منابع کتاب",
"booksources-search-legend": "پی جۈري سي سرچشمإیل کتاڤ",
"booksources-search": "پی جۈري",
- "specialloguserlabel": "کاربر:",
- "speciallogtitlelabel": "عÙ\86Ù\88ان:",
+ "specialloguserlabel": "مؽنتور:",
+ "speciallogtitlelabel": "اÙ\88Ú\8cÚ¤ان:",
"log": "نمایه ها",
"all-logs-page": "گشڌنمائیٱل",
"allpages": "همه صفحات",
"allarticles": "همه صفحات",
"allpagessubmit": "برو",
"allpagesprefix": "نشو دادن صفحات همراه با پیشوند:",
+ "allpages-hide-redirects": "بؽ دیارنیڌن آلشڌتورٱل",
"categories": "دسته آ",
"emailuser": "امیل ای کاربر",
"watchlist": "لیسڌ دیناگریٱل مو",
"mywatchlist": "سإیل برگ",
+ "watchlistfor2": "سی $1 $2",
"addedwatchtext": "صفحه «<nowiki>$1</nowiki>» به [[Special:Watchlist|لیست پیگیریهای ]] ایسا\nاضاف وابید.\nتغییرات این صفحه و صفحه صحبت مر بوطه اش در آینده ایچو لیست ابوه. بهعلاوه، ای صفحه، سی واضحتر دیده وابیدن در [[Special:RecentChanges|فهرست تغییرات اخیر]] به شکل <b>سیاه</b> ایا.\n\nایر بعدا خواستین ای صفحه زه لیست پیگیریهاتو ورداشته بوه، رو «'''عدم پیگیری'''» در بالای صفحه کلیک کنین.",
"removedwatchtext": "آن صفحه\"[[:$1]]\" جابجا وابیده زه[[Special:لیست پیگیری|لیست پیگیری ایسا]].",
"watch": "پیگیری",
"watchthispage": "پیگیری ای صفحه",
"unwatch": "پيگري نبيڎه",
- "watchlist-details": "{{PLURAL:$1|$1 صفحه|$1 صفحات}} درلیست پیگیری ایسا, صفحات صحبت حساب نیبوه.",
- "wlshowlast": "نمایش آخرین $1 ساعت $2 روز",
+ "watchlist-details": "{{PLURAL:$1|$1 بٱلٛگاْ|$1 بٱلٛگیٱل}} مؽن لیست دیناگری ایسا,هؽڌا",
+ "wlheader-showupdated": "بٱلٛگیٱلؽ کاْ دیناتٱر زاْ آخرین ساٛل اؽسا آلشڌ آڤیڌ ناْ<strong>پورٱنڳ</strong> نماونیڌاْ آڤیڌناْ",
+ "wlnote": "ڤاْ لٱم {{PLURAL:$1|آلشڌؽ|<strong>$1</strong> آلشڌؽ}} کاْ ڤاْ {{PLURAL:$2|سات|<strong>$2</strong> سات}} رٱئڌاْ انجوم آڤؽڌ مۉجۊڌ هؽڌا،ؤرگار دوکرٱت ڤینی دینائی: $3، $4",
+ "wlshowlast": "نماونیڌن ٱخیری $1 سات $2 رۊز",
+ "watchlist-options": "دزاٛیٱل دیناگری",
"watching": "مئن حالت پي جوري",
"unwatching": "درحالت عدم پیگیری...",
+ "enotif_reset": "دزاٛکرڌن گشڌ بٱلٛگیٱل ڤاْ اوڌڤان نیٱشڌ آڤیڌاْ",
"deletepage": "حذف صفحه",
"historywarning": "توجه: آن صفحه ای که ایسا اخوین حذف کنین گزارش تاریخی داره:",
"confirmdeletetext": "ایسا اخوین یه صفحه بلند با همه گزارش تاریخی هونه حذف کنین.\nلطفا کانفیرم یا تائید کنین که تمایل وقصد ای کار را دارین, وایسا دوین یا می دانید نتایج وآثار ای کار را, و ایسا انجام ادین ای کار را مطابق با [[{{MediaWiki:Policy-url}}|سیاستها]].",
"rollbacklinkcount": "چڤاسإ کردن $1 {{PLURAL:$1|ویرایشت|ویرایشتیا}}",
"protectlogpage": "نمایه حفاظت وحمایت",
"protectedarticle": "پٱر و پیم ڤابیڌاٛ \"[[$1]]\"",
+ "modifiedarticleprotection": "بارت هناگری«[[$1]]» ناْ آلشڌنیڌ",
"prot_1movedto2": "[[$1]] جابجا وابید به[[$2]]",
"protectcomment": "دلیل:",
"protectexpiry": "سپری وابیڎه ا:",
"mycontris": "هومياریا",
"anoncontribs": "هومياریا",
"contribsub2": "سی {{GENDER:$3|$1}} ($2)",
+ "nocontribs": "هیچ آلشڌؽ ڤا ای دیاریٱل جوساْ نڤیڌ",
"uctop": "تازاٛ باۋ",
"month": "در این ماه (و قبل زه آن):",
"year": "در ای سال (وقبل زه آن):",
"blockip": "بستن کاربر",
"ipboptions": "۲ ساعت:2 hours,۱ روز:1 day,۳ روز:3 days,۱ هفته:1 week,۲ هفته:2 weeks,۱ ماه:1 month,۳ ماه:3 months,۶ ماه:6 months,۱ سال:1 year,بینهایت:infinite",
"ipblocklist": "آدرسهای آی پی وکاربران بسته وابیدند",
+ "infiniteblock": "بؽ تٱ",
"blocklink": "بسته بوه !",
"unblocklink": "باز بوه",
"contribslink": "ھأیاری",
"blocklogpage": "نمایه _ بستهوابیدهها",
"blocklogentry": "بسته وابید [[$1]] با سپری وابیدن وقت زه $2 $3",
+ "reblock-logentry": "ساموکارٱل بورسناْ دٱسرٱسی [[$1]]آلشڌ ڤاْ تٱهاْ بورسناْ دٱسرٱسی مؽن $2 $3",
+ "block-log-flags-nocreate": "هرنڳ ؤرکلنیڌن هساڤ بؽ هرنڳ آڤیڌ",
+ "proxyblocker": "ؤٱرڤٱناْ پروکسی",
"movepagetext": "با استفاده زه فرم زیر نام صفحه تغییر اکنه و همه گزارش تاریخی هو به نام تازه جابجا ابوه.\nعنوان کهنه تبدیل به یک صفحه تغییر مسیر به عنوان جدید ابوه.\nایسا ترین بطور اتوماتیک تغییر مسیر های مربوط به عنوان اصلی رو به روز رسانی کنین. ایر ایسا مطمئن نهدین با دیدن یونو مطمئن بوین:\n[[Special:تغییر مسیر دوبل|دوبل ]] یا[[Special:تغییرمسیر خروا یا اشکسته|تغییرمسیرهای خراو یا اشکسته]].\n\nلینکهایی که به عنوان صفحه قدیمی هدن تغییر نه کنن حتماً تغییر مسیرهای دوبل یا اشکسته و خراو را بررسی کنین.\n'''ایسا''' مسئول اطمینان زه یو هدین که لینکها هنی به همانجایی که قرار هده برن.\n\nتوجه کنین که ایر زه قبل صفحهای در عنوان تازه وجود داشته بوه صفحه منتقل '''نه بوه'''،\nمیر یو که آن صفحه خالی یا تغییر مسیر بوه و گزارش تاریخی اصلاح نداشته بوه.\n یعنی ایر اشتباه کردین ترین صفحه را به همان جایی که زه هو جابجا وابیدین برگردانین، و نترین رو صفحات موجود بنویسین\n\n'''هشدار!'''\nجابجایی صفحات به نام تازه ممکنه تغییر کلی و غیرمنتظرهای سی\n صفحات دوست داشتنی داشته بوه ؛\nلطفاً مطمئن بوین که قبل زه جابجا کردن صفحه، عواقب ای کار را درک اکنین.",
"movepagetalktext": "صفحه صحبت مربوط، ایر وجود داشته بوه، بطور اتوماتیک همراه با صفحه اصلی\n جابجا ابوه '''میر یو که''' :\n* در حال جابجایی صفحه زه ای فضای نام به فضای نام دیگری بوین،\n* یه صفحه صحبت غیرخالی تحت ای نام تازه وجود داشته بوه، یا\n* کادر زیر را تیک نزده بوین.\n\nدر ای موارد، وا صفحه را بطور دستی جابجا کرده و یا محتویات دو صفحه را با اصلاح ادغام کنین.",
"newtitle": "به عنوان تازه:",
"pageinfo-title": "ڌونائی زاْ «$1»",
"pageinfo-header-basic": "ڌونائیٱل بٱلٛگاْ",
"pageinfo-header-restrictions": "هناڌاری زاْ بٱلٛگاْ",
+ "pageinfo-header-properties": "ؤیژگیٱل بٱلٛگاْ",
"pageinfo-display-title": "نماونیڌن داسوݩ",
"pageinfo-default-sort": "کاٛلیت یٱتورنیڌن نهافٱرز",
"pageinfo-length": "هنا بٱلگاْ (ڤایڌ)",
"pageinfo-language": "زڤون هؽلنیڌیٱل بٱلٛگاْ",
"pageinfo-content-model": "بارت هؽلنیڌاْ بٱلٛگاْ",
"pageinfo-robot-policy": "فاٛئرست کرڌن ڤا روڤات",
+ "pageinfo-robot-index": "موجاز",
+ "pageinfo-robot-noindex": "بؽ اعتڤار",
"pageinfo-watchers": "شوماراٛ دیناگروناٛ بٱلٛگاْ",
+ "pageinfo-few-watchers": " ساوا تٱر زاْ $1 {{PLURAL:$1| دیناگری|دیناگری}}",
"pageinfo-redirects-name": "بور آلشڌ لۊرٱل ڤاْ ای بٱلٛگاْ",
+ "pageinfo-subpages-name": "زؽر بٱلٛگیٱل ای بٱلٛگاْ",
+ "pageinfo-subpages-value": "1 ($2 {{PLURAL:$2|آلشڌتور|آلشڌ تور}}; $3 {{PLURAL:$3|خاٛراز آلشڌ تور|خاٛراز آلشڌ تور}})",
+ "pageinfo-magic-words": "{{PLURAL:$1|قساْ|قساْ}} جادۊیی ($1)",
+ "pageinfo-hidden-categories": "{{PLURAL:$1| بنکۊ|بنکۊ}} بؽ دیار ( $1 )",
+ "pageinfo-templates": "{{PLURAL:$1|اولگۊیٱل|اولگۊیٱل}} اْسفاڌاْ آڤیڌاْ ($1)",
"pageinfo-toolboxlink": "دونسمندیا بلگه",
+ "pageinfo-contentpage": "باٛئنٱت آڤیڌاْ ڤاْ اوڌڤان بٱلٛگاْ موهتٱڤائی",
+ "pageinfo-contentpage-yes": "هٱراٛ",
+ "patrol-log-page": "سیاهاْ لاٛر",
"previousdiff": "← اصلاح قدیمی",
"nextdiff": "تفاوت بعدی→",
"file-info-size": "$1 × $2 پیکسل, اندازه فایل: $3, MIME نوع: $4",
+ "file-info-size-pages": "<span style=\"direction:ltr\">$1 × $2</span> نوخڌاْ، مٱشلٱقی پٱرڤٱناْ: $3، نوع MIME پٱرڤٱناْ: $4، $5 بٱلٛگاْ",
"file-nohires": "قابلیت تفکیک بالاتری در دسترس نه.",
"svg-long-desc": "SVG فایل, تقریبا$1 × $2 پیکسل, اندازه فایل: $3",
"show-big-image": "جانیا اصلی",
"namespacesall": "همه",
"monthsall": "همه ماهها",
"semicolon-separator": "؛ ",
+ "imgmultipagenext": "بٱلٛگاْ دینائی",
+ "imgmultigo": "رۉڤا",
+ "imgmultigoto": "رٱئڌن ڤاٛ بٱلٛگاْ $1",
+ "watchlisttools-clear": "پاکسانیڌن فاٛئرسڌ دیناگری",
"watchlisttools-view": "نشودادن تغییرات مربوطه",
"watchlisttools-edit": "نشودادن واصلاح کردن لیست پیگیریها",
"watchlisttools-raw": "اصلاح لیست خام پیگیریها",
"signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|چک چنه]])",
"version": "ترجمه یا تفسیر",
+ "redirect": "آلشد لۊر ڤا پٱرڤٱناْ،مؽنتور،بٱلٛگاْ دبارنوماْ نوسخاْ",
"specialpages": "بلگاٛ آ ۋیجٱ",
- "tag-filter": "[[Special:سٱرڌیسا|سٱرديس]] فيلتر:",
+ "tag-filter": "[[Special:سٱرڌیسا|سٱرديس]]فیلتر:",
"tag-list-wrapper": "[[Special:Tags|{{PLURAL:$1|سرديس|سرديسا}}]]: $2",
"logentry-delete-delete": "$1 بألگأ {{GENDER:$2|پاکسا ڤابيأ}} $3",
+ "logentry-delete-restore": "$1{{GENDER:$2|آلشڌ کرڌن}} بٱلٛگاْ ناْ$3سی$4",
+ "logentry-delete-revision": "$1 دیاری {{PLURAL:$5|یٱ نوسخاْ|$5 نوسخاْ}} بٱلٛگاْ $3 ناْ{{GENDER:$2|آلشڌکرڌ}}: $4",
+ "revdelete-content-hid": "هؽلنیڌناْ بؽڌیارکرڌ",
"logentry-move-move": "$1 {{GENDER:$2|جا ب جا کردإ}} بألگأ $3 نأ سي $4",
"logentry-move-move_redir": "$1 بٱلٛگاْ $3 ناْ ڤاْ $4 کاْ آلشڌ تور ڤیڌاْ {{GENDER:$2|کل کرڌ}}",
"logentry-patrol-patrol-auto": "$1 نوسخهٔ $4 بٱلٛگاْ $3 ناْ خودکار ڤاْ عنڤان لرخوؤٱرداٛ {{GENDER:$2|دزاْ کونین}}",
"logentry-newusers-create": "هساڤ کارياري $1 {{GENDER:$2|راسد ڤابي}}",
+ "logentry-newusers-autocreate": "حساڤ $1 ڤاْ بارت خوڌکار {{GENDER:$2|ؤرکل آڤیڌاْ}}",
"logentry-upload-upload": "$1 {{GENDER:$2|سوڤار کرده}} $3",
"searchsuggest-search": "جستن {{SITENAME}}",
- "duration-days": "$1 رۊز"
+ "duration-days": "$1 رۊز",
+ "randomrootpage": "بٱلٛگاْ بونی تٱساڌفی"
}
"tooltip-preferences-save": "Uložit nastavení",
"tooltip-summary": "Zadejte stručné shrnutí",
"interlanguage-link-title": "$1 – $2",
- "common.css": "/* Zde uvedené CSS bude ovlivňovat všechny styly */",
+ "common.css": "/* Zde uvedené CSS bude ovlivňovat všechny vzhledy */",
"print.css": "/* Zde uvedené CSS bude ovlivňovat tiskový výstup */",
"noscript.css": "/* Zde uvedené CSS bude ovlivňovat uživatele s vypnutým JavaScriptem */",
"group-autoconfirmed.css": "/* Zde uvedené CSS bude ovlivňovat pouze automaticky schválené uživatele */",
"group-sysop.css": "/* Zde uvedené CSS bude ovlivňovat pouze správce */",
"group-bureaucrat.css": "/* Zde uvedené CSS bude ovlivňovat pouze byrokraty */",
"common.json": "/* Zde uvedený JSON se načte pro všechny uživatele při načtení každé stránky. */",
- "common.js": "/* Zde uvedený JavaScript bude použit pro všechny uživatele při načtení každé stránky. */",
+ "common.js": "/* Zde uvedený JavaScript bude použit pro všechny uživatele při načtení každé stránky */",
"group-autoconfirmed.js": "/* Zde uvedený JavaScript bude použit pouze pro automaticky schválené uživatele */",
"group-user.js": "/* Zde uvedený JavaScript bude použit pouze pro registrované uživatele */",
"group-bot.js": "/* Zde uvedený JavaScript bude použit pouze pro roboty */",
"resetpass-abort-generic": "Die Passwortänderung wurde durch eine Erweiterung abgebrochen.",
"resetpass-expired": "Dein Passwort ist abgelaufen. Bitte lege ein neues Passwort zur Anmeldung fest.",
"resetpass-expired-soft": "Dein Passwort ist abgelaufen und muss geändert werden. Bitte wähle jetzt ein neues Passwort aus oder klicke auf „{{int:authprovider-resetpass-skip-label}}“, um es später zu ändern.",
+ "resetpass-validity": "Dein Passwort ist nicht gültig: $1\n\nBitte lege zur Anmeldung ein neues Passwort fest.",
"resetpass-validity-soft": "Dein Passwort ist ungültig: $1\n\nBitte wähle jetzt ein neues Passwort aus oder klicke auf „{{int:authprovider-resetpass-skip-label}}“, um es später zu ändern.",
"passwordreset": "Passwort zurücksetzen",
"passwordreset-text-one": "Fülle dieses Formular aus, um ein temporäres Passwort per E-Mail zu erhalten.",
"about": "Heqa",
"article": "Wesiqe",
"newwindow": "(pençerey newey de beno a)",
- "cancel": "Annuler",
+ "cancel": "Bıtexelne",
"moredotdotdot": "Vêşi...",
"morenotlisted": "Na lista qay kemi ya.",
"mypage": "Per",
"resetpass-abort-generic": "Password change has been aborted by an extension.",
"resetpass-expired": "Your password has expired. Please set a new password to log in.",
"resetpass-expired-soft": "Your password has expired and needs to be changed. Please choose a new password now, or click \"{{int:authprovider-resetpass-skip-label}}\" to change it later.",
+ "resetpass-validity": "Your password is not valid: $1\n\nPlease set a new password to log in.",
"resetpass-validity-soft": "Your password is not valid: $1\n\nPlease choose a new password now, or click \"{{int:authprovider-resetpass-skip-label}}\" to change it later.",
"passwordreset": "Reset password",
"passwordreset-text-one": "Complete this form to receive a temporary password via email.",
"blockedtext": "<strong>Your username or IP address has been blocked.</strong>\n\nThe block was made by $1.\nThe reason given is <em>$2</em>.\n\n* Start of block: $8\n* Expiration of block: $6\n* Intended blockee: $7\n\nYou can contact $1 or another [[{{MediaWiki:Grouppage-sysop}}|administrator]] to discuss the block.\nYou cannot use the \"{{int:emailuser}}\" feature unless a valid email address is specified in your [[Special:Preferences|account preferences]] and you have not been blocked from using it.\nYour current IP address is $3, and the block ID is #$5.\nPlease include all above details in any queries you make.",
"autoblockedtext": "Your IP address has been automatically blocked because it was used by another user, who was blocked by $1.\nThe reason given is:\n\n:<em>$2</em>\n\n* Start of block: $8\n* Expiration of block: $6\n* Intended blockee: $7\n\nYou may contact $1 or one of the other [[{{MediaWiki:Grouppage-sysop}}|administrators]] to discuss the block.\n\nNote that you may not use the \"{{int:emailuser}}\" feature unless you have a valid email address registered in your [[Special:Preferences|user preferences]] and you have not been blocked from using it.\n\nYour current IP address is $3, and the block ID is #$5.\nPlease include all above details in any queries you make.",
"systemblockedtext": "Your username or IP address has been automatically blocked by MediaWiki.\nThe reason given is:\n\n:<em>$2</em>\n\n* Start of block: $8\n* Expiration of block: $6\n* Intended blockee: $7\n\nYour current IP address is $3.\nPlease include all above details in any queries you make.",
+ "actionblockedtext": "You have been blocked from performing this action.",
"blockednoreason": "no reason given",
"whitelistedittext": "Please $1 to edit pages.",
"confirmedittext": "You must confirm your email address before editing pages.\nPlease set and validate your email address through your [[Special:Preferences|user preferences]].",
"blockip": "Block {{GENDER:$1|user}}",
"blockiptext": "Use the form below to block write access from a specific IP address or username.\nThis should be done only to prevent vandalism, and in accordance with [[{{MediaWiki:Policy-url}}|policy]].\nFill in a specific reason below (for example, citing particular pages that were vandalized).\nYou can block IP address ranges using the [https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing CIDR] syntax; the largest allowed range is /$1 for IPv4 and /$2 for IPv6.",
"ipaddressorusername": "IP address or username:",
- "ipbexpiry": "Expiration:",
"ipbreason": "Reason:",
"ipbreason-dropdown": "*Common block reasons\n** Inserting false information\n** Removing content from pages\n** Spamming links to external sites\n** Inserting nonsense/gibberish into pages\n** Intimidating behavior/harassment\n** Abusing multiple accounts\n** Unacceptable username",
"ipb-hardblock": "Prevent logged-in users from editing from this IP address",
- "ipbcreateaccount": "Prevent account creation",
- "ipbemailban": "Prevent user from sending email",
+ "ipbcreateaccount": "Account creation",
+ "ipbemailban": "Sending email",
"ipbenableautoblock": "Automatically block the last IP address used by this user, and any subsequent IP addresses they try to edit from",
"ipbsubmit": "Block this user",
"ipbother": "Other time:",
"ipboptions": "2 hours:2 hours,1 day:1 day,3 days:3 days,1 week:1 week,2 weeks:2 weeks,1 month:1 month,3 months:3 months,6 months:6 months,1 year:1 year,indefinite:infinite",
"ipbhidename": "Hide username from edits and lists",
"ipbwatchuser": "Watch this user's user and talk pages",
- "ipb-disableusertalk": "Prevent this user from editing their own talk page while blocked",
+ "ipb-disableusertalk": "Editing their own talk page",
"ipb-change-block": "Re-block the user with these settings",
"ipb-confirm": "Confirm block",
"ipb-sitewide": "Sitewide",
"ipb-partial": "Partial",
- "ipb-type-label": "Type",
"ipb-pages-label": "Pages",
"badipaddress": "Invalid IP address",
"blockipsuccesssub": "Block succeeded",
"ipb-blocklist": "View existing blocks",
"ipb-blocklist-contribs": "Contributions for {{GENDER:$1|$1}}",
"ipb-blocklist-duration-left": "$1 left",
+ "block-actions": "Actions to block:",
+ "block-expiry": "Expiration:",
+ "block-options": "Additional options:",
+ "block-prevent-edit": "Editing",
+ "block-reason": "Reason:",
+ "block-target": "Username or IP address:",
"unblockip": "Unblock user",
"unblockiptext": "Use the form below to restore write access to a previously blocked IP address or username.",
"ipusubmit": "Remove this block",
"returnto": "Volver a $1.",
"tagline": "De {{SITENAME}}",
"help": "Ayuda",
+ "help-mediawiki": "Ayuda sobre MediaWiki",
"search": "Buscar",
"search-ignored-headings": " #<!-- dejar esta línea exactamente como está --> <pre>\n# Títulos que serán ignorados por la búsqueda.\n# Los cambios estarán en vigor tan pronto como la página con el título esté indexada.\n# Puedes forzar la reindexación de una página haciendo una edición nula.\n# La sintaxis es la siguiente:\n# * Todo lo que sigue a un carácter \"#\" hasta el final de la línea, es un comentario.\n# * Todas las líneas que no están en blanco son los títulos exactos que se ignorarán (diferenciando mayúsculas de minúsculas).\nReferencias\nEnlaces externos\nVéase también\n #</pre> <!-- dejar esta línea exactamente como está -->",
"searchbutton": "Buscar",
"editpage-invalidcontentmodel-text": "El modelo de contenido «$1» no se admite.",
"editpage-notsupportedcontentformat-title": "Formato de contenido no compatible",
"editpage-notsupportedcontentformat-text": "El formato de contenido $1 no es compatible con el modelo de contenido $2.",
+ "slot-name-main": "Principal",
"content-model-wikitext": "texto wiki",
"content-model-text": "texto sin formato",
"content-model-javascript": "JavaScript",
"grant-delete": "Borrar páginas, revisiones y entradas del registro",
"grant-editinterface": "Editar el espacio de nombres MediaWiki y las páginas CSS/JSON/JavaScript del sitio y de los usuarios",
"grant-editmycssjs": "Editar tu CSS/JSON/JavaScript",
- "grant-editmyoptions": "Editar tus preferencias de usuario",
+ "grant-editmyoptions": "Editar tus preferencias de usuario y configuración en formato JSON",
"grant-editmywatchlist": "Editar tu lista de seguimiento",
"grant-editsiteconfig": "Editar páginas de configuración CSS/JS del sitio",
"grant-editpage": "Editar páginas existentes",
"rcfilters-watchlist-edit-watchlist-button": "Edita tu lista de seguimiento",
"rcfilters-watchlist-showupdated": "Los cambios hechos en páginas que no has visitado desde que se efectuaron aparecen en <strong>negrita</strong>, acompañados de marcadores sólidos.",
"rcfilters-preference-label": "Usar la interfaz sin JavaScript",
- "rcfilters-preference-help": "Cargar cambios recientes sin filtros ni la funcionalidad de resaltado.",
+ "rcfilters-preference-help": "Carga los cambios recientes sin filtros de búsqueda ni funcionalidad de resaltado.",
"rcfilters-watchlist-preference-label": "Usar interfaz sin JavaScript",
- "rcfilters-watchlist-preference-help": "Cargar la lista de seguimiento sin filtros ni la funcionalidad de resaltado.",
+ "rcfilters-watchlist-preference-help": "Carga la lista de seguimiento sin filtros de búsqueda ni la funcionalidad de resaltado.",
"rcfilters-filter-showlinkedfrom-label": "Mostrar cambios en páginas enlazadas desde",
"rcfilters-filter-showlinkedfrom-option-label": "<strong>Páginas enlazadas desde</strong> la página seleccionada",
"rcfilters-filter-showlinkedto-label": "Mostrar cambios en páginas que enlazan a",
"exif-photometricinterpretation-1": "Чорны і белы (чорны — 0)",
"exif-photometricinterpretation-3": "Палітра",
"exif-photometricinterpretation-4": "Маска празрыстасьці",
+ "exif-photometricinterpretation-5": "Падзелены (імаверна, CMYK)",
+ "exif-photometricinterpretation-8": "CIE L*a*b*",
"exif-unknowndate": "Невядомая дата",
"exif-orientation-1": "Звычайная",
"exif-orientation-2": "Адлюстраваная па гарызанталі",
"exif-compression-4": "CCITT Group 4 codification fax",
"exif-copyrighted-true": "Sub copyright",
"exif-copyrighted-false": "Stato de copyright non definite",
+ "exif-photometricinterpretation-0": "Nigro e blanco (blanco es 0)",
"exif-photometricinterpretation-1": "Nigre e blanc (0 pro nigre)",
+ "exif-photometricinterpretation-3": "Paletta",
+ "exif-photometricinterpretation-4": "Masca de transparentia",
+ "exif-photometricinterpretation-5": "Separate (probabilemente CMYK)",
+ "exif-photometricinterpretation-8": "CIE L*a*b*",
+ "exif-photometricinterpretation-9": "CIE L*a*b* (codification ICC)",
+ "exif-photometricinterpretation-10": "CIE L*a*b* (codification ITU)",
"exif-unknowndate": "Data incognite",
"exif-orientation-1": "Normal",
"exif-orientation-2": "Invertite horizontalmente",
"Ua2004",
"Ата",
"Максим Підліснюк",
- "Тест"
+ "Тест",
+ "Piramidion"
]
},
"exif-imagewidth": "Ширина",
"exif-compression-34712": "JPEG2000",
"exif-copyrighted-true": "Охороняється законом про авторське право",
"exif-copyrighted-false": "Авторські права не встановлено",
+ "exif-photometricinterpretation-0": "Чорний і білий (Білий — це 0)",
"exif-photometricinterpretation-1": "Чорний і білий (білий — 0)",
"exif-photometricinterpretation-2": "RGB",
+ "exif-photometricinterpretation-3": "Палітра",
+ "exif-photometricinterpretation-4": "Маска прозорості",
+ "exif-photometricinterpretation-5": "Відокремлено (ймовірно CMYK)",
"exif-photometricinterpretation-6": "YCbCr",
+ "exif-photometricinterpretation-8": "CIE L*a*b*",
+ "exif-photometricinterpretation-9": "CIE L*a*b* (ICC-кодування)",
+ "exif-photometricinterpretation-10": "CIE L*a*b* (ITU-кодування)",
"exif-unknowndate": "Невідома дата",
"exif-orientation-1": "Нормальна",
"exif-orientation-2": "Відображено по горизонталі",
"accmailtext": "Un mot de passe généré aléatoirement pour [[User talk:$1|$1]] a été envoyé à $2.\nIl peut être modifié sur la page ''[[Special:ChangePassword|Changement de mot de passe]]'' après connexion.",
"newarticle": "(Nouveau)",
"newarticletext": "Vous avez suivi un lien vers une page qui n’existe pas encore. \nAfin de créer cette page, entrez votre texte dans la boîte ci-après (vous pouvez consulter [$1 la page d’aide] pour plus d’informations). \nSi vous êtes arrivé{{GENDER:||e}} ici par erreur, cliquez sur le bouton <strong>Retour</strong> de votre navigateur.",
- "anontalkpagetext": "----\n<em>Vous êtes sur la page de discussion d’un utilisateur anonyme qui n’a pas encore créé de compte ou qui n’en utilise pas</em>.\nPour cette raison, nous devons utiliser son adresse IP pour les identifier.\nUne adresse IP peut être partagée par plusieurs utilisateurs.\nSi vous êtes un{{GENDER:||e|}} utilisat{{GENDER:|eur|rice|eur}} anonyme et si vous constatez que des commentaires qui ne vous concernent pas vous ont été adressés, vous pouvez [[Special:CreateAccount|créer un compte]] ou [[Special:UserLogin|vous connecter]] afin d’éviter toute confusion future avec d’autres contributeurs anonymes.",
+ "anontalkpagetext": "----\n<em>Vous êtes sur la page de discussion d’un utilisateur anonyme qui n’a pas encore créé de compte ou qui n’en utilise pas</em>.\nPour cette raison, nous devons utiliser son adresse IP pour l'identifier.\nUne telle adresse IP peut être partagée par plusieurs utilisateurs.\nSi vous êtes un{{GENDER:||e|}} utilisat{{GENDER:|eur|rice|eur}} anonyme et si vous constatez que des commentaires qui ne vous concernent pas vous ont été adressés, vous pouvez [[Special:CreateAccount|créer un compte]] ou [[Special:UserLogin|vous connecter]] afin d’éviter toute confusion future avec d’autres contributeurs anonymes.",
"noarticletext": "Il n’y a pour l’instant aucun texte sur cette page.\nVous pouvez [[Special:Search/{{PAGENAME}}|lancer une recherche sur ce titre]] dans les autres pages,\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} rechercher dans les opérations liées]\nou [{{fullurl:{{FULLPAGENAME}}|action=edit}} créer cette page]</span>.",
"noarticletext-nopermission": "Il n'y a pour l'instant aucun texte sur cette page.\nVous pouvez [[Special:Search/{{PAGENAME}}|faire une recherche sur ce titre]] dans les autres pages,\nou <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} rechercher dans les journaux associés]</span>, mais vous n'avez pas la permission de créer cette page.",
"missing-revision": "La révision nº $1 de la page intitulée « {{FULLPAGENAME}} » n’existe pas.\n\nCela survient en général en suivant un lien historique désuet vers une page qui a été supprimée.\nVous pouvez trouver plus de détails dans le [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} journal des suppressions].",
"special-characters-title-emdash": "tiret cadratin",
"special-characters-title-minus": "signe moins",
"mw-widgets-abandonedit": "Êtes-vous sûr de vouloir quitter le mode d’édition sans enregistrer d’abord ?",
- "mw-widgets-abandonedit-discard": "Ignorer les modifications",
+ "mw-widgets-abandonedit-discard": "Annuler les modifications",
"mw-widgets-abandonedit-keep": "Continuer à modifier",
"mw-widgets-abandonedit-title": "Êtes-vous sûr ?",
"mw-widgets-dateinput-no-date": "Aucune date sélectionnée",
"thu": "Ham",
"fri": "Jum",
"sat": "Sah",
- "january": "Januwari",
- "february": "Peburuari",
+ "january": "Januari",
+ "february": "Pebruari",
"march": "Maret",
"april": "April",
"may_long": "Mei",
"october": "Oktober",
"november": "Nopember",
"december": "Desember",
- "january-gen": "Januwari",
+ "january-gen": "Januari",
"february-gen": "Pebruari",
"march-gen": "Maret",
"april-gen": "April",
"newmessagesdifflinkplural": "{{PLURAL:$1|biloli'o|999=u biloli'o}} pulitiyo",
"youhavenewmessagesmulti": "Yio lo'otapu tahuli bohu to $1",
"editsection": "boli'a",
- "editold": "boli'",
+ "editold": "boli'a",
"viewsourceold": "Bilohi bungoliyo",
"editlink": "boli'a",
"viewsourcelink": "Bilohi bungoliyo",
"resetpass-abort-generic": "שינוי הסיסמה בוטל על־ידי הרחבה.",
"resetpass-expired": "סיסמתך פקעה. נא להגדיר סיסמה חדשה כדי להיכנס.",
"resetpass-expired-soft": "הסיסמה שלך פקעה, וצריך לשנות אותה. יש לבחור סיסמה חדשה כעת, או ללחוץ על \"{{int:authprovider-resetpass-skip-label}}\" כדי לשנות אותה מאוחר יותר.",
+ "resetpass-validity": "סיסמתך אינה חוקית: $1\n\nנא להגדיר סיסמה חדשה כדי להיכנס.",
"resetpass-validity-soft": "הסיסמה שלך אינה תקינה: $1\n\nיש לבחור סיסמה חדשה כעת או ללחוץ על \"{{int:authprovider-resetpass-skip-label}}\" כדי לשנות את הסיסמה מאוחר יותר.",
"passwordreset": "איפוס סיסמה",
"passwordreset-text-one": "יש למלא טופס זה כדי לקבל סיסמה זמנית בדוא\"ל.",
"anoneditwarning": "<strong>Figyelem:</strong> Nem vagy bejelentkezve. Ha szerkesztesz, az IP-címed nyilvánosan látható lesz a laptörténetben. Ha <strong>[$1 bejelentkezel]</strong> vagy <strong>[$2 regisztrálsz]</strong>, a szerkesztéseid a felhasználónevedhez lesznek társítva, egyéb hasznos dolgok mellett.",
"anonpreviewwarning": "''Nem vagy bejelentkezve. A mentéskor az IP-címed rögzítve lesz a laptörténetben.''",
"missingsummary": "'''Emlékeztető:''' Nem adtál meg szerkesztési összefoglalót. Ha összefoglaló nélkül akarod elküldeni a szöveget, kattints újra a mentésre.",
- "selfredirect": "<strong>Figyelem:</strong> Az lapot önmagára készült átirányítani. Lehet, hogy rossz célt adtál meg, vagy rossz oldalt szerkesztesz. Ha ismét a $1 gombra kattintasz, akkor az átirányítás mégis létrejön.",
+ "selfredirect": "<strong>Figyelem:</strong> A lapot önmagára készülöd átirányítani. Lehet, hogy rossz célt adtál meg, vagy rossz oldalt szerkesztesz. Ha ismét a „$1” gombra kattintasz, az átirányítás mégis létrejön.",
"missingcommenttext": "Kérjük, írj összefoglalót a szerkesztésedhez.",
"missingcommentheader": "<strong>Emlékeztető:</strong> Nem adtad meg a megjegyzés tárgyát.\nHa ismét a „$1” gombra kattintasz, akkor a szerkesztésed nélküle lesz elmentve.",
"summary-preview": "A szerkesztési összefoglaló előnézete:",
"seconds": "{{PLURAL:$1|egy|$1}} másodperccel",
"minutes": "{{PLURAL:$1|egy|$1}} perccel",
"hours": "{{PLURAL:$1|egy|$1}} órával",
- "days": "$1 nappal",
+ "days": "$1 nap",
"weeks": "{{PLURAL:$1|$1 hét|$1 hét}}",
"months": "{{PLURAL:$1|$1 hónap|$1 hónap}}",
"years": "{{PLURAL:$1|$1 év|$1 év}}",
"Macofe",
"Fitoschido",
"Baloch Khan",
- "Muhraz"
+ "Muhraz",
+ "Ardzun"
]
},
"tog-underline": "Garih bawahi tautan:",
"permalink": "Pautan parmanen",
"print": "Cetak",
"view": "Baco",
+ "view-foreign": "Caliak di $1",
"edit": "Suntiang",
"create": "Buek",
+ "create-local": "Tambah sumber deskripsi lokal",
"delete": "Hapuih",
"undelete_short": "Batal hapuih $1 {{PLURAL:$1|suntiangan}}",
"viewdeleted_short": "Lihek {{PLURAL:$1|$1 suntiangan}} nan dihapuih",
"otherlanguages": "Dalam bahaso lain",
"redirectedfrom": "(Dialiahkan dari $1)",
"redirectpagesub": "Laman pangaliahan",
+ "redirectto": "Dialiahkan ka",
"lastmodifiedat": "Laman ko tarakhia diubah pado $2, tanggal $1.",
"viewcount": "Laman ko lah dicaliak {{PLURAL:$1|$1 kali}}.",
"protectedpage": "Laman nan dilinduangi",
"actionthrottled": "Tindakan tabateh",
"actionthrottledtext": "Sanak tabateh untuak malakuan tindakan ko banyak-banyak dalam wakatu singkek. Cubo lah laik satalah bara minit.",
"protectedpagetext": "Laman ko alah dikunci untuak manghindari panyuntiangan.",
- "viewsourcetext": "Sanak dapek malihek atau manyalin sumber laman iko:",
+ "viewsourcetext": "Sanak dapek mancaliak atau manyalin sumber laman iko:",
"viewyourtext": "Sanak dapek mancaliak jo mangkopi sumber untuak \"suntiangan sanak\" ka laman ko",
"protectedinterface": "Laman ko baisi teks antarmuko untuak digunoan dek parangkaik lunak di wiki ko sajo, dan alah dikunci untuak maindaan kasalahan. \nUntuak manambah atau maubah tajamahan di kasado wiki, harap gunoan [https://translatewiki.net/ translatewiki.net], yaitu proyek palokalan MediaWiki.",
"editinginterface": "'''Paringatan:''' Sanak manyuntiang laman nan digunoan untuak manyadiokan teks antarmuko untuak parangkaik lunak.\nParubahan teks ko akan mampangaruhi tampilan pado antarmuko pangguno untuak pangguno lain.\nUntuak tajamahan, harap gunoan [https://translatewiki.net/wiki/Main_Page?setlang=min translatewiki.net], proyek palokalan MediaWiki.",
"userlogin-noaccount": "Alun ado akun?",
"userlogin-joinproject": "Join {{SITENAME}}",
"createaccount": "Buek akun",
- "userlogin-resetpassword-link": "Buek ulang kato sandi",
+ "userlogin-resetpassword-link": "Lupo kato sandi Sanak?",
+ "userlogin-helplink2": "Bantuan masuak log",
"createacct-emailrequired": "Alamaik surel",
"createacct-emailoptional": "Alamaik surel (opsional)",
"createacct-email-ph": "Masuakan alamaik surel Sanak",
"loginlanguagelabel": "Baso: $1",
"suspicious-userlogout": "Pamintaan Sanak untuak kalua log ditulak karano tampaknyo dikirim oleh paramban nan rusak atau proksi panyinggah.",
"pt-login": "Masuak log",
+ "pt-login-button": "Masuak log",
"pt-createaccount": "Buek akun",
"pt-userlogout": "Kalua log",
"php-mail-error-unknown": "Kasalahan nan indak jaleh dalam fungsi mail() PHP",
"preview": "Caliak",
"showpreview": "Pratonton",
"showdiff": "Parubahan",
- "anoneditwarning": "'''Ingek:''' Sanak alun masuak log.\nAlamat IP sanak tacatat pado riwayaik suntiangan laman ko.",
+ "anoneditwarning": "'''Ingek:''' Sanak alun masuak log.\nAlamat IP sanak tacatat pado riwayaik suntiangan laman ko. Kok Sanak <strong>[$1 log in]</strong> atau <strong>[$2 mambuek akun]</strong>, suantiang Sanak ka didistribusian kapado namo pangguno Sanak, sarato baragam kauntuangan lainnyo.",
"anonpreviewwarning": "''Sanak alun masuak log. Manyimpan laman akan manyababkan alamaik IP Sanak tacatat pado riwayat suntiangan laman iko.''",
"missingsummary": "'''Paringatan:''' Sanak indak mamasuakan ringkasan panyuntiangan. Jikok Sanak baliak manakan tombol Simpan, suntiangan Sanak akan disimpan tanpa ringkasan panyuntiangan.",
"missingcommenttext": "Masuakan komentar Sanak di bawah ko.",
"permissionserrorstext": "Sanak indak ado hak untuak malakuannyo dek {{PLURAL:$1|alasan}} barikuik:",
"permissionserrorstext-withaction": "Sanak indak punyo hak akses untuak $2, dek {{PLURAL:$1|alasan}} barikuik:",
"recreate-moveddeleted-warn": "'''Ingek: Sanak mambuek ulang suatu laman nan alah dihapuih.'''\n\nHarap ditimbang apo rancak malanjuikan suntiangan Sanak.\nBarikuik ko log pangapuihan jo pamindahan dari laman ko:",
- "moveddeleted-notice": "Laman ko alah dihapuih.\nSabagai reperensi, barikuik adolah log pangapuihan dan pamindahannyo.",
+ "moveddeleted-notice": "Laman ko alah dihapuih.\nSabagai referensi, barikuik adolah log pangapuihan jo pamindahannyo.",
"log-fulllog": "Liek saluruah log",
"edit-hook-aborted": "Suntiangan dibatalan samo kait parser\ntanpa ado katarangan.",
"edit-gone-missing": "Indak dapek mampabarui laman.\nMungkin alah dihapuih.",
"histlegend": "Bandiangan piliahan: Tandoi revisi untuak mambandiangan dan takan enter atau tombol di bawah.<br />\nContoh: '''({{int:cur}})''' = bedo jo versi tarakhia, '''({{int:last}})''' = bedo jo versi sabalunnyo, '''{{int:minoreditletter}}''' = suntiangan ketek.",
"history-fieldset-title": "Talusuri riwayaik",
"history-show-deleted": "Hanyo nan dihapuih",
- "histfirst": "Nan lamo",
- "histlast": "Nan baru",
+ "histfirst": "Nan paliang lamo",
+ "histlast": "Nan paliang baru",
"historysize": "({{PLURAL:$1|$1 bita}})",
"historyempty": "(kosong)",
"history-feed-title": "Riwayat revisi",
"shown-title": "Tampilkan $1 {{PLURAL:$1|hasil}} per laman",
"viewprevnext": "Caliak ($1 {{int:pipe-separator}} $2) ($3)",
"searchmenu-exists": "'''Ado laman nan banamo \"[[:$1]]\" pado wiki ko.'''",
- "searchmenu-new": "'''Buek laman \"[[:$1]]\" di wiki ko!'''",
+ "searchmenu-new": "<strong>Buek laman \"[[:$1]]\" di wiki ko!</strong> {{PLURAL:$2|0=|Caliak pulo laman nan ditamukan dari pancarian Sanal.|Caliak pulo hasia pancarian nan ditamukan.}}",
"searchprofile-articles": "Laman isi",
"searchprofile-images": "Multimedia",
"searchprofile-everything": "Sadonyo",
"searchrelated": "bakaitan",
"searchall": "sado",
"showingresults": "Di bawah ko dikaluaan sampai {{PLURAL:$1|'''$1''' hasil}}, dimulai dari #'''$2'''.",
+ "search-showingresults": "{{PLURAL:$4|Hasia <strong>$1</strong> dari <strong>$3</strong>|Hasia <strong>$1 - $2</strong> dari <strong>$3</strong>}}",
"search-nonefound": "Indak ado hasil nan cocok sasuai jo parmintaan",
"powersearch-legend": "Pencarian lanjut",
"powersearch-ns": "Mancari di ruangnamo:",
"action-writeapi": "manggunoan panulisan API",
"action-import": "impor laman ko dari wiki lain",
"nchanges": "$1 {{PLURAL:$1|parubahan}}",
+ "enhancedrc-history": "riwayaik",
"recentchanges": "Parubahan baru",
"recentchanges-legend": "Piliahan parubahan baru",
"recentchanges-summary": "Caliak parubahan tabaru pado wiki di laman ko.<br />\n;Patunjuak:(<span style=\"color:blue;\">bedo</span>) parubahan, (<span style=\"color:blue;\">sijarah</span>) riwayaik parubahan, '''B''' laman baru, '''b''' suntiangan bot, '''k''' suntiangan ketek, <span class=\"unpatrolled\">!</span> parubahan alun dipatroli,<br /><span style=\"color:green;\">'''(+ ''bita'')'''</span> isi laman batambah, <span style=\"color:red;\">(- ''bita'')</span> isi laman bakurang, (← Ikhtisar otomatih), (→ <span style=\"color:grey;\">Suntiangan bagian</span>)",
+ "recentchanges-noresult": "Indak ado parubahan dalam rantang wakatu ko nan sasuai jo kriteria.",
"recentchanges-feed-description": "Tamuan parubahan baru dalam umpan wiki ko",
"recentchanges-label-newpage": "Suntiangan ko mambuek laman baru",
"recentchanges-label-minor": "Iko suntiangan ketek",
"rcnotefrom": "Di bawah ko ado parubahan mulai dari '''$2''' (sampai '''$1''' parubahan).",
"rclistfrom": "Tunjuakan parubahan baru mulai dari tanggal $3 $2",
"rcshowhideminor": "$1 suntiangan ketek",
+ "rcshowhideminor-show": "Tunjuakan",
+ "rcshowhideminor-hide": "Suruakan",
"rcshowhidebots": "$1 bot",
+ "rcshowhidebots-show": "Tunjuakan",
+ "rcshowhidebots-hide": "Suruakan",
"rcshowhideliu": "$1 pangguno tadaftar",
+ "rcshowhideliu-show": "Tunjuakan",
+ "rcshowhideliu-hide": "Suruakan",
"rcshowhideanons": "$1 pangguno anon",
+ "rcshowhideanons-show": "Tunjuakan",
+ "rcshowhideanons-hide": "Suruakan",
"rcshowhidepatr": "$1 suntiangan tapatroli",
"rcshowhidemine": "$1 suntiangan denai",
+ "rcshowhidemine-show": "Tunjuakan",
+ "rcshowhidemine-hide": "Suruakan",
"rclinks": "Tunjuakkan $1 parubahan tabaru dalam $2 hari nan tarakhia",
"diff": "bedo",
"hist": "sijarah",
"imagelinks": "Panggunoan berkas",
"linkstoimage": "{{PLURAL:$1|Halaman|$1 halaman}} nan iko manggunoan berkas nan iko:",
"linkstoimage-more": "Labiah dari $1 {{PLURAL:$1|laman}} ado pautan ka berkas ko.\nDaftar barikuik manunjuakan {{PLURAL:$1|$1 laman jo pautan langsuang}} ka berkas ko.\nAdo juo tasadio [[Special:WhatLinksHere/$2|daftar langkoknyo]].",
- "nolinkstoimage": "Indak ado laman nan bapauik ka berkas ko.",
+ "nolinkstoimage": "Indak ado laman nan manggunokan berkas ko.",
"morelinkstoimage": "Caliak [[Special:WhatLinksHere/$1|pautan baliak]] ka berkas ko.",
"linkstoimage-redirect": "$1 (pangaliahan berkas) $2",
"duplicatesoffile": "Sabanyak {{PLURAL:$1|$1 berkas barikuik}} marupoan duplikat dari berkas ko ([[Special:FileDuplicateSearch/$2|rincian labiah lanjuik]]):",
"apisandbox": "Bak kasiak API",
"booksources": "Sumber buku",
"booksources-search-legend": "Cari di sumber buku",
+ "booksources-search": "Cari",
"specialloguserlabel": "Pangguno:",
"speciallogtitlelabel": "Target (judul atau pangguno):",
"log": "Log",
"contributions": "Jariah {{GENDER:$1|pangguno}}",
"contributions-title": "Jariah pangguno untuak $1",
"mycontris": "Jariah",
- "contribsub2": "Untuak $1 ($2)",
+ "anoncontribs": "Jariah",
+ "contribsub2": "Untuak {{GENDER:$3|$1}} ($2)",
"uctop": "kini",
"month": "Dari bulan (dan sabalunnyo):",
"year": "Dari taun (dan sabalunnyo):",
"sp-contributions-search": "Cari jariah",
"sp-contributions-username": "Alamaik IP atau namo pangguno:",
"sp-contributions-toponly": "Hanyo manampilan suntiangan nan tarakhia",
+ "sp-contributions-newonly": "Hanyo manampilan suntiangan nan tarakhia",
"sp-contributions-submit": "Cari",
"whatlinkshere": "Pautan baliak",
"whatlinkshere-title": "Laman nan takaik ka \"$1\"",
"importstart": "Mangimpor laman...",
"importnosources": "Indak ado sumber impor transwiki nan lah dibuek dan pamuatan riwayaik sacaro langsuang alah dinon-aktipan.",
"importlogpagetext": "Administrasi laman impor jo riwayaik panyuntiangannyo dari wiki lain.",
- "tooltip-pt-userpage": "Laman pangguno Sanak",
+ "tooltip-pt-userpage": "Laman {{GENDER:|pangguno Sanak}}",
"tooltip-pt-anonuserpage": "Laman pangguno IP Sanak",
- "tooltip-pt-mytalk": "Laman rundiang Sanak",
+ "tooltip-pt-mytalk": "Laman rundiang {{GENDER:|Sanak}}",
"tooltip-pt-anontalk": "Parundiangan tantang suntiangan dari IP ko",
- "tooltip-pt-preferences": "Pangaturan denai",
+ "tooltip-pt-preferences": "Piliahan {{GENDER:|Sanak}}",
"tooltip-pt-watchlist": "Daftar laman nan dipantau.",
- "tooltip-pt-mycontris": "Daftar jariah Sanak",
+ "tooltip-pt-mycontris": "Daftar jariah {{GENDER:|Sanak}}",
"tooltip-pt-login": "Sanak disaranan untuak masuak log; walaupun indak wajib",
"tooltip-pt-logout": "Kalua log",
"tooltip-pt-createaccount": "Sanak dianjuaan mambuek akun dan masuak log; walaupun hal iko indak aruih",
"tooltip-t-recentchangeslinked": "Parubahan baru laman nan bakaik jo laman ko",
"tooltip-feed-rss": "Umpan RSS untuak laman ko",
"tooltip-feed-atom": "Umpan Atom untuak laman ko",
- "tooltip-t-contributions": "Caliak daftar jariah pangguno ko",
+ "tooltip-t-contributions": "Daftar kontribusi {{GENDER:$1|pangguno iko}}",
"tooltip-t-emailuser": "Kirimkan surel pado pangguno ko",
"tooltip-t-upload": "Muek berkas",
"tooltip-t-specialpages": "Daftar kasado laman istimewa",
"logentry-newusers-autocreate": "Akun pangguno $1 alah {{GENDER:$2|dibuek}} sacaro otomatih",
"logentry-rights-rights": "$1 {{GENDER:$2|maubah}} kaanggotaan kalompok $3 dari $4 manjadi $5",
"logentry-rights-rights-legacy": "$1 {{GENDER:$2|maubah}} kaanggotaan kalompok $3",
+ "logentry-upload-upload": "$1 {{GENDER:$2|mangunggah}} $3",
"rightsnone": "(indak ado)",
"searchsuggest-search": "Cari {{SITENAME}}",
"searchsuggest-containing": "Barisi...",
"resetpass-abort-generic": "Generic error message shown on [[Special:ChangePassword]] when an extension aborts a password change from a hook.",
"resetpass-expired": "Generic error message shown on [[Special:ChangePassword]] when a user's password is expired",
"resetpass-expired-soft": "Generic warning message shown on [[Special:ChangePassword]] when a user needs to reset their password, but they are not prevented from logging in at this time",
+ "resetpass-validity": "Warning message shown on [[Special:ChangePassword]] when a user needs to reset their password, because their password is not valid.\n\nParameters:\n* $1 - error message",
"resetpass-validity-soft": "Warning message shown on [[Special:ChangePassword]] when a user needs to reset their password, because their password is not valid.\n\nRefers to {{msg-mw|authprovider-resetpass-skip-label}}.\n\nParameters:\n* $1 - error message",
"passwordreset": "Title of [[Special:PasswordReset]].\n{{Identical|Reset password}}",
"passwordreset-text-one": "Text on [[Special:PasswordReset]] that appears when there is only one way of resetting the password.\n\n{{msg-mw|Passwordreset-text-many}} will be used, when there are multiple ways of resetting the password.",
"blockedtext": "Text displayed to blocked users.\n\n\"email this user\" should be consistent with {{msg-mw|Emailuser}}.\n\nParameters:\n* $1 - the blocking sysop (with a link to his/her userpage)\n* $2 - the reason for the block\n* $3 - the current IP address of the blocked user\n* $4 - (Unused) the blocking sysop's username (plain text, without the link)\n* $5 - the unique numeric identifier of the applied autoblock\n* $6 - the expiry of the block\n* $7 - the intended target of the block (what the blocking user specified in the blocking form)\n* $8 - the timestamp when the block started\nSee also:\n* {{msg-mw|Grouppage-sysop}}\n* {{msg-mw|Autoblockedtext|notext=1}}\n* {{msg-mw|Systemblockedtext|notext=1}}",
"autoblockedtext": "Text displayed to automatically blocked users.\n\n\"email this user\" should be consistent with {{msg-mw|Emailuser}}.\n\nParameters:\n* $1 - the blocking sysop (with a link to his/her userpage)\n* $2 - the reason for the block (in case of autoblocks: {{msg-mw|autoblocker}})\n* $3 - the current IP address of the blocked user\n* $4 - (Unused) the blocking sysop's username (plain text, without the link). Use it for GENDER.\n* $5 - the unique numeric identifier of the applied autoblock\n* $6 - the expiry of the block\n* $7 - the intended target of the block (what the blocking user specified in the blocking form)\n* $8 - the timestamp when the block started\nSee also:\n* {{msg-mw|Grouppage-sysop}}\n* {{msg-mw|Blockedtext|notext=1}}\n* {{msg-mw|Systemblockedtext|notext=1}}",
"systemblockedtext": "Text displayed to requests blocked by MediaWiki configuration.\n\n\"email this user\" should be consistent with {{msg-mw|Emailuser}}.\n\nParameters:\n* $1 - (Unused) A dummy user attributed as the blocker, possibly as a link to a user page.\n* $2 - the reason for the block\n* $3 - the current IP address of the blocked user\n* $4 - (Unused) the dummy blocking user's username (plain text, without the link).\n* $5 - A short string indicating the type of system block.\n* $6 - the expiry of the block\n* $7 - the intended target of the block\n* $8 - the timestamp when the block started\nSee also:\n* {{msg-mw|Grouppage-sysop}}\n* {{msg-mw|Blockedtext|notext=1}}\n* {{msg-mw|Autoblockedtext|notext=1}}",
+ "actionblockedtext": "Text displayed when a user is blocked from perofmring an action, but no matching block for the user exists. This can happen if an extension forces a user to be blocked.",
"blockednoreason": "Substituted with <code>$2</code> in the following message if the reason is not given:\n* {{msg-mw|cantcreateaccount-text}}.\n{{Identical|No reason given}}",
"whitelistedittext": "Used as error message. Parameters:\n* $1 - a link to [[Special:UserLogin]] with {{msg-mw|loginreqlink}} as link description\n* $2 - an URL to the same\n\nSee also:\n* {{msg-mw|Nocreatetext}}\n* {{msg-mw|Uploadnologintext}}\n* {{msg-mw|Loginreqpagetext}}",
"confirmedittext": "Used as error message.",
"blockip": "Used as the text of a link in the sidebar toolbox. Clicking this link takes you to [[Special:Block]], with a relevant username or IP address (e.g. \"Username\" on [[User talk:Username]], [[Special:Contributions/Username]], etc.) already filled in.\n\nParameters:\n* $1 - username, for GENDER support\n{{Identical|Block user}}",
"blockiptext": "Used in the {{msg-mw|Blockip}} form in [[Special:Block]].\n\nRefers to {{msg-mw|Policy-url}}.\n\nThis message may follow the message {{msg-mw|Ipb-otherblocks-header}} and other block messages.\n\nParameters:\n* $1 - CIDR suffix of the largest allowed IPv4 block (as an integer)\n* $2 - CIDR suffix of the largest allowed IPv6 block (as an integer)\n\nSee also:\n* {{msg-mw|Unblockiptext}}",
"ipaddressorusername": "{{Identical|IP address or username}}",
- "ipbexpiry": "{{Identical|Expiry}}",
"ipbreason": "Label of the block reason dropdown in [[Special:BlockIP]] and the unblock reason textfield in [{{fullurl:Special:IPBlockList|action=unblock}} Special:IPBlockList?action=unblock].\n\n{{Identical|Reason}}",
"ipbreason-dropdown": "Used as item list for dropdown on [[Special:Block]].\n\nThe label for this dropdown is {{msg-mw|Ipbreason}}.",
"ipb-hardblock": "This is the label for a checkbox in the user block form on [[Special:Block]].\n\nSee also:\n* {{msg-mw|ipbemailban}}\n* {{msg-mw|ipb-disableusertalk}}\n* {{msg-mw|ipbenableautoblock}}\n* {{msg-mw|ipbhidename}}\n* {{msg-mw|ipbwatchuser}}",
"ipb-confirm": "Used as hidden field in the form on [[Special:Block]].",
"ipb-sitewide": "A type of block the user can select from on [[Special:Block]].",
"ipb-partial": "A type of block the user can select from on [[Special:Block]].",
- "ipb-type-label": "The label of the type of editing restriction the admin would like to impose on [[Special:Block]].",
"ipb-pages-label": "The label for an autocomplete text field to specify pages to block a user from editing on [[Special:Block]].",
"badipaddress": "An error message shown when one entered an invalid IP address in blocking page.",
"blockipsuccesssub": "Used as page title in [[Special:Block]].\n\nThis message is the subject for the following message:\n* {{msg-mw|Blockipsuccesstext}}",
"ipb-blocklist": "Used as link text in [[Special:Block]].\n\nThe link points to Specil:BlockList.",
"ipb-blocklist-contribs": "Used in [[Special:Block]].\n* $1 - target username",
"ipb-blocklist-duration-left": "Used on [[Special:BlockList]] to show the remaining time (years, months, days, hours, minutes) until the block expires.\n$1 - The duration left",
+ "block-actions": "Label for the checkboxes for specifying the actions that a block affects on [[Special:Block]]",
+ "block-expiry": "Label for the input for specifying the expiry time of a block on [[Special:Block]]",
+ "block-options": "Label for the checkboxes for specifying additional options for a block on [[Special:Block]]",
+ "block-prevent-edit": "Label for the checkbox for specifying an editing block in [[Special:Block]].",
+ "block-reason": "Label for the input for specifying the reason for a block on [[Special:Block]]",
+ "block-target": "Label for the input for specifying the target of a block on [[Special:Block]]",
"unblockip": "Used as title and legend for the form in [[Special:Unblock]].",
"unblockiptext": "Used in the {{msg-mw|Unblockip}} form on [[Special:Unblock]].",
"ipusubmit": "Used as button text on [{{canonicalurl:Special:BlockList|action=unblock}} Special:BlockList?action=unblock]. To see the message:\n* Go to [[Special:BlockList]]\n* Click \"unblock\" for any block (but you can only see \"unblock\" if you have administrator rights)\n* It is now the button below the form",
"help": "Faaba",
"help-mediawiki": "Faaba MediaWiki ga",
"search": "Ceeci",
+ "search-ignored-headings": "#<!-- ma ši žeero woo barmay kul --> <pre>\n# Tammaasawey kaŋ ceeciyanoo g'i muray.\n# Ne barmawey ga huru goy ra za moɲoo nda tammaasaa šilbandi.\n# War ga hin ka moɲoo gaabi k'a šilbay taaga nda war na barmayyan koonu tee.\n# Nahawoo ti sanda:\n# * Haya kul kaŋ hun \"#\" harfu ga hala žeeri benantaa ga ti daara.\n# * Žeeri kaŋ ši koonu kul ti maa alhakiika kaŋ ga murandi, šigira azzaati da goo a ra.\nFellawey\nTarayhere dobey\nGuna da\n #</pre> <!-- ma ši žeero woo barmay kul -->",
"searchbutton": "Ceeci",
"go": "Koy",
"searcharticle": "Koy",
"toolbox": "Goyjinawey",
"tool-link-userrights": "{{GENDER:$1|goykaw}} batey barmay",
"tool-link-userrights-readonly": "{{GENDER:$1|goykaw}} batey guna",
+ "tool-link-emailuser": "Bataga sanba {{GENDER:$1|goykaa woo}} se",
"imagepage": "Tuku moo guna",
"mediawikipage": "Bataga moo guna",
"templatepage": "Leeti moo guna",
"redirectedfrom": "(Kaŋ $1 n'a bisandi)",
"redirectpagesub": "Bisandi moo",
"redirectto": "Bisandi ne:",
- "lastmodifiedat": "Moɲoo barmay cee koraa $1 hane, $2 waate.",
+ "lastmodifiedat": "Moɲoo woo barmay koraa tee $1 hane, $2 waate.",
"viewcount": "Moɲoo woo duwandi {{PLURAL:$1|cee foo|$1 cee booboyaŋ}}.",
"protectedpage": "Moo jejebante",
"jumpto": "Sar ne:",
"protectedinterface": "Moɲoo woo ka hantumoo cebe wikiyoo woo porogaramoo se nd'a ga jejebu hasaraw teekey ga. Goy nda [https://translatewiki.net/ translatewiki.net], MediaWiki berandiyan porožewoo ka berandiyaŋ tonton wala k'i barmay.",
"editinginterface": "<strong>Yaamar:</strong> War goo ma moo fasal kaŋ nd'i ga goy ka goyjinaa porogaram hantumoo cebe. \nBarmawey kaŋ ga tee moɲoo ka hantumey kaŋ goykaw ga dii y'ey bere wikiyoo woo ga.",
"translateinterface": "Ka berandiyan tonton wala k'i barmay, wa goy nda [https://translatewiki.net/ translatewiki.net], MediaWiki berandiyan porožewoo.",
- "cascadeprotected": "Moɲoo woo ga jejebu barmayyan ga zam'a goo ne {PLURAL:$1|moo kaŋ ti|moɲey kaŋ ti}} jejebante nda \"kaŋandiyan\" suubari kaŋ ga dira: \n$2",
+ "cascadeprotected": "Moɲoo woo ga jejebu barmayyan ga zam'a goo ne {PLURAL:$1|moo kaŋ ti|moɲey kaŋ ti}} jejebante nda \"kaŋandiyan\" suubaroo kaŋ ga dira: \n$2",
"namespaceprotected": "War šii nda fondo ka moɲey barmay <strong>$1</strong> maadogoo ra.",
"customcssprotected": "War šii nda fondo ka CSS moɲoo woo barmay zama goykaw tana foo boŋkayandiyaney g'a ra.",
"customjsprotected": "War šii nda fondo ka JavaScript barmay zama a goo nda goykaw tana foo boŋkayandiyaney.",
"noemail": "Bataga aderesu kul mana jisandi \"$1\" goykaa se.",
"noemailcreate": "War ga hima ka bataga aderesu henna noo.",
"passwordsent": "Šennikufal taaga n' ka sanbandi bataga aderesu jisantaa ga \"$1\" se. Ceeci ka huru koyne nda n' duu w'a.",
- "blocked-mailpassword": "I na war IP aderesoo ganji a ma hantum, adiši a ši duu fondo ka goy nda šennikufal yeetiyan dabaroo ka zanbayan gagay.",
+ "blocked-mailpassword": "I na war IP aderesoo ganji a ma hantum. Ka zanbayan ganji, šennikufal yeetiyan ši duɲe IP aderesoo woo ga.",
"eauthentsent": "Cimandiyan bataga n' ka sanbandi bataga aderesu tabatantaa ga. \nWar ga hima ka šilbawey kaŋ goo bataga laasaabu jina, hala war ga hin ka war ga hin ka bataga tana kul sanba ka cimandi kaŋ alhakiika ra kontoo ti war wane.",
"throttled-mailpassword": "Šennikufal yeetiyan bataga n' ka sanbandi war se a ga too {{PLURAL:$1|guuru $1}}.\nKa zanbayan ganji, šennikufal yeetiyan bataga foo de ma sanbandi {{PLURAL:$1|guuru $1}} ra.",
"mailerror": "Bataga sanbayan firka: $1",
- "acct_creation_throttle_hit": "Boro kaŋ ga ciya nda war IP aderesoo na {{PLURAL:$1|kontu $1}} jirbi koraa ra, woo ti alkadaroo beero kaŋ ga hin ka tee waatoo woo ra. \nWoo sabboo se, borey kaŋ ga goy nda IP aderesoo woo ši hin ka kontu kul tee koyne sohõda.",
+ "acct_creation_throttle_hit": "Boro kaŋ ga ciya nda war IP aderesoo na {{PLURAL:$1|kontu foo|kontu $1}} jirbi $2 korawey ra, kaŋ ti alkadar kul ibeero kaŋ duɲe waatoo woo ra. \nAdiši, borey kaŋ ga goy nda IP aderesoo woo ši hin ka kontu kul tee koyne sohõda.",
"emailauthenticated": "War bataga aderesoo n' ka tabatandi $2 boŋ $3 ga.",
"emailnotauthenticated": "War bataga aderesoo mana tabatandi.\nBataga kul ši sanbandi alhaaley wey affoo ku še.",
"noemailprefs": "Bataga aderesu tabatandi war ibaayey ra alhaaley wey ma hin ka goy.",
"createaccount-title": "Kontu tee {{SITENAME}} se",
"createaccount-text": "Boro foo na kontu tee war bataga aderesoo se {{SITENAME}} ($4) kaŋ maa ti \"$2\" ga, nda \"$3\" šennikufaloo.\nWa maa hantum ka hima ka huru nda šennikufal barmay sohõ.\n\nWar ga hin ka batagaa woo muray nda kontoo woo n' ka dere ka tee.",
"login-throttled": "War ceeci cee booboyaŋ ka huru.\nTaare batu $1 jina ka ciya koyne.",
- "login-abort-generic": "War mana hin ka huru - Laybu",
+ "login-abort-generic": "War mana hin ka huru",
"login-migrated-generic": "War kontoo n' ka ganandi nda war goykaw maaɲoo ši ye ka bara wikiyoo woo ga.",
"loginlanguagelabel": "Šenni:$1",
"suspicious-userlogout": "War ceeciroo ka fatta mongu zam'a ga hima kaŋ ceecikaw kayraa wala tugudoo tokore k'a sanba.",
"createacct-another-realname-tip": "Maa cimi noo nda n' ga baa.\nNda war soobay k'a noondi, boro ga goy nd'a ka goykaw alhaali noo ngi goyoo se.",
"pt-login": "Huru",
"pt-login-button": "Huru",
+ "pt-login-continue-button": "Gaabandi ka huru",
"pt-createaccount": "Kontu tee",
"pt-userlogout": "Fatta",
"php-mail-error-unknown": "Firka šibayante PHP mail() goymee se",
"newpassword": "Šennikufal taaga:",
"retypenew": "Šennikufal taagaa hantum koyne:",
"resetpass_submit": "Šennikufal barmay ka huru",
- "changepassword-success": "War šennikufaloo barmay ka boori!",
+ "changepassword-success": "War šennikufaloo n' k barmay!",
"changepassword-throttled": "War ceeci cee booboyaŋ ka huru.\nTaare batu $1 jina ka ceeci koyne.",
"resetpass_forbidden": "Šennikufaley ši hin ka barmay",
"resetpass-no-info": "War ga hima ka huru jina ka duu moɲoo woo.",
"limitreport-templateargumentsize-value": "$1/$2 ไบต์",
"limitreport-expansiondepth": "ความลึกการขยายสูงสุด",
"limitreport-unstrip-depth": "ความลึกการเรียกซ้ำ Unstrip",
+ "limitreport-unstrip-size-value": "$1/$2 ไบต์",
"expandtemplates": "ขยายแม่แบบ",
"expand_templates_output": "ผลลัพธ์",
"expand_templates_ok": "ตกลง",
"pagelang-select-lang": "เลือกภาษา",
"pagelang-reason": "เหตุผล",
"pagelang-submit": "ส่ง",
+ "pagelang-nonexistent-page": "ไม่มีหน้า $1",
+ "pagelang-unchanged-language": "ตั้งหน้า $1 เป็นภาษา $2 แล้ว",
+ "pagelang-unchanged-language-default": "ตั้งหน้า $1 เป็นภาษาเนื้อหาโดยปริยายของวิกิแล้ว",
+ "pagelang-db-failed": "ฐานข้อมูลไม่สามารถเปลี่ยนภาษาของหน้า",
"right-pagelang": "เปลี่ยนภาษาหน้า",
"action-pagelang": "เปลี่ยนภาษาหน้า",
"log-name-pagelang": "ปูมการเปลี่ยนภาษา",
"special-characters-group-khmer": "เขมร",
"special-characters-group-canadianaboriginal": "แคนาดาพื้นเมืองดั้งเดิม",
"special-characters-title-minus": "เครื่องหมายลบ",
- "mw-widgets-abandonedit": "à¸\84ุà¸\93à¹\81à¸\99à¹\88à¹\83à¸\88วà¹\88าà¸\95à¹\89à¸à¸\87à¸\81ารà¸\81ลัà¸\9aà¹\84à¸\9b view mode à¹\82à¸\94ยà¹\84มà¹\88à¸\9aัà¸\99à¸\97ึà¸\81à¸\82à¹\89à¸à¸¡à¸¹à¸¥à¹\80à¸à¸²à¹\84วà¹\89à¸\81à¹\88à¸à¸\99 ?",
+ "mw-widgets-abandonedit": "à¸\84ุà¸\93à¹\81à¸\99à¹\88à¹\83à¸\88วà¹\88าà¸\95à¹\89à¸à¸\87à¸\81ารà¸à¸à¸\81à¸\88าà¸\81ภาวะà¹\81à¸\81à¹\89à¹\84à¸\82à¹\82à¸\94ยà¹\84มà¹\88à¸\9aัà¸\99à¸\97ึà¸\81à¸\81à¹\88à¸à¸\99หรืà¸à¹\84มà¹\88",
"mw-widgets-dateinput-no-date": "ไม่เลือกวันที่",
"mw-widgets-usersmultiselect-placeholder": "เพิ่ม...",
"mw-widgets-titlesmultiselect-placeholder": "เพิ่ม...",
"tog-watchdefault": "Men tahrirlagan sahifa va fayllar kuzatuv roʻyxatimga qoʻshilsin",
"tog-watchmoves": "Men nomini koʻchirgan sahifa va fayllar kuzatuv roʻyxatimga qoʻshilsin",
"tog-watchdeletion": "Men oʻchirgan sahifa va fayllarni kuzatuv roʻyxatimga qoʻsh",
- "tog-minordefault": "Sukut boʻyicha barcha tahrirlarimni «kichik tahrir» etib belgilash",
+ "tog-watchuploads": "Yuklagan fayllarim kuzatuv roʻyxatimga qoʻshilsin",
+ "tog-watchrollback": "Tezda qaytarish harakatini amalga oshirganimdan keyin oʻsha sahifa kuzatuv roʻyxatimga qoʻshilsin",
+ "tog-minordefault": "Sukut boʻyicha barcha tahrirlarimni «kichik tahrir» deb belgilansin",
"tog-previewontop": "Koʻrib chiqish imkoniyati tahrir oynasi tepasida boʻlsin",
- "tog-previewonfirst": "Tahrirlashga oʻtgandayoq koʻrib chiqishni boshlash",
+ "tog-previewonfirst": "Tahrirlashga oʻtgandayoq koʻrib chiqish boshlansin",
"tog-enotifwatchlistpages": "Kuzatuv roʻyxatimdagi sahifa yoki fayllar oʻzgartirilsa, menga bu haqda xat yuborilsin",
"tog-enotifusertalkpages": "Munozara sahifam oʻzgartirilsa, menga bu haqda xat yuborilsin",
- "tog-enotifminoredits": "Kichik tahrir qilinsa ham e-pochtamga bu haqda xat yuborilsin",
- "tog-enotifrevealaddr": "Xabar beruvchi xatlarda e-pochta manzilim koʻrsatilsin",
- "tog-shownumberswatching": "Sahifani kuzatuv roʻyxatiga olgan foydalanuvchilar sonini koʻrsatish",
- "tog-oldsig": "Joriy imzo:",
+ "tog-enotifminoredits": "Sahifa va fayllarga kichik oʻzgarish kiritilsa ham elektron pochtamga bu haqda xat yuborilsin",
+ "tog-enotifrevealaddr": "Xabar beruvchi xatlarda elektron pochta manzilim koʻrsatilsin",
+ "tog-shownumberswatching": "Sahifani kuzatuv roʻyxatiga qoʻshgan foydalanuvchilar soni koʻrsatilsin",
+ "tog-oldsig": "Joriy imzoingiz:",
"tog-fancysig": "Imzoni viki-belgi qilib koʻrsatish (avtomatik ishoratsiz)",
- "tog-uselivepreview": "Tez koʻrib chiqish",
- "tog-forceeditsummary": "Qisqa tavsif oynasi toʻldirilmagani haqida ogohlantirish koʻrsatish",
+ "tog-uselivepreview": "Oʻzgarishlarni sahifani yangilamasdan koʻrib chiqish",
+ "tog-forceeditsummary": "Qisqa tavsif oynasi toʻldirilmagani haqida ogohlantirish berilsin",
"tog-watchlisthideown": "Oʻz tahrirlarim kuzatuv roʻyxatimda koʻrsatilmasin",
"tog-watchlisthidebots": "Botlar qilgan tahrirlar kuzatuv roʻyxatimda koʻrsatilmasin",
"tog-watchlisthideminor": "Kichik tahrirlar kuzatuv roʻyxatimda koʻrsatilmasin",
"tog-watchlisthideliu": "Tizimga kirgan foydalanuvchilar tahrirlari kuzatuv roʻyxatimda koʻrsatilmasin",
- "tog-watchlisthideanons": "Anonim foydalanuvchilar tahrirlari kuzatuv roʻyxatimda koʻrsatilmasin",
+ "tog-watchlisthideanons": "Anonimlarning tahrirlari kuzatuv roʻyxatimda koʻrsatilmasin",
"tog-watchlisthidepatrolled": "Tekshirilgan tahrirlar kuzatuv roʻyxatimda koʻrsatilmasin",
+ "tog-watchlisthidecategorization": "Sahifalarning turkumlari yashirilsin",
"tog-ccmeonemails": "Boshqa ishtirokchilarga yozgan xatimning nusxasi oʻzimga yuborilsin",
"tog-diffonly": "Versiyalar taqqoslanayotganda, pastda sahifa matni koʻrsatilmasin",
- "tog-showhiddencats": "Yashirin turkumlarni koʻrsatish",
- "tog-norollbackdiff": "Tahrir qaytarilganda, versiyalar taqqosi koʻrsatilmasin",
- "tog-useeditwarning": "Oʻzgarishlarni saqlamay sahifadan chiqib ketayotganim haqida ogohlantir",
- "tog-prefershttps": "Doim himoyalangan holda kirish",
+ "tog-showhiddencats": "Yashirin turkumlar koʻrsatilsin",
+ "tog-norollbackdiff": "Biron tahrir tezda qaytarilsa, versiyalar taqqosini koʻrsatishning hojati yoʻq",
+ "tog-useeditwarning": "Oʻzgarishlarni saqlamay sahifadan chiqib ketayotganim haqida ogohlantirish berilsin",
+ "tog-prefershttps": "Tizimga kirganimdan keyin doim himoyalangan aloqadan foydalanilsin",
"underline-always": "Har doim",
"underline-never": "Hech qachon",
- "underline-default": "Bezak mavzusi yoki brauzer andozasi boʻyicha",
+ "underline-default": "Brauzer sozlamalaridan foydalanilsin",
"editfont-style": "Tahrirlash maydonidagi shrift turi:",
"editfont-monospace": "Teng enli shrift (Monospaced)",
"editfont-sansserif": "Kertiksiz shrift (Sans-serif)",
"category_header": "„$1“ turkumidagi sahifalar",
"subcategories": "Ostturkumlar",
"category-media-header": "„$1“ turkumidagi fayllar",
- "category-empty": "''Ushbu turkumda hozircha sahifa yoki fayllar yoʻq.''",
+ "category-empty": "<em>Ushbu turkumda hozircha sahifa yoki fayllar yoʻq.</em>",
"hidden-categories": "{{PLURAL:$1|Yashirin turkum}}",
"hidden-category-category": "Yashirin turkumlar",
"category-subcat-count": "{{PLURAL:$2|Ushbu turkumda faqat bitta ostturkum mavjud.|Quyida ushbu turkumga kiruvchi $2 ta ostturkumdan $1 tasi koʻrsatilgan.}}",
"mytalk": "Munozara",
"anontalk": "Ushbu IP-manzil munozarasi",
"navigation": "Saytda harakatlanish",
- "and": " va",
+ "and": " va",
"faq": "TSS",
"actions": "Amallar",
"namespaces": "Nomfazolar",
"viewsourcelink": "manbasini koʻrish",
"editsectionhint": "Boʻlimni tahrirlash: $1",
"toc": "Mundarija",
- "showtoc": "koʻrsatish",
+ "showtoc": "koʻrsat",
"hidetoc": "yashirish",
"collapsible-collapse": "Yigʻish",
"collapsible-expand": "Yoyish",
"viewsource-title": "$1 sahifasining manbasini koʻrish",
"actionthrottled": "Tezlik cheklovi",
"protectedpagetext": "Bu sahifa tahrirlash va boshqa oʻzgarishlar kiritishdan himoyalangan.",
- "viewsourcetext": "Siz bu sahifaning manbasini koʻrishingiz va uni nusxasini olishingiz mumkin:",
+ "viewsourcetext": "Siz bu sahifaning ichki kodini koʻrishingiz va undan nusxa olishingiz mumkin:",
"protectedinterface": "Ushbu sahifada dasturiy taʼminot interfeysi xabari mavjud. Bezoriliklardan saqlash uchun uni oʻzgartirish taʼqiqlangan.\nUshbu xabar tarjimasini qoʻshish yoki oʻzgartirish uchun, iltimos, MediaWikining [https://translatewiki.net/ translatewiki.net] mahalliylashtirish saytidan foydalaning.",
"editinginterface": "<strong>Eʼtibor bering:</strong> Siz interfeys matnini aks ettiruvchi sahifani tahrirlamoqdasiz.\nUning oʻzgartirilishi boshqa foydalanuvchilar uchun ham interfeys oʻzgarishiga olib keladi.",
"translateinterface": "Ushbu xabar tarjimasini qoʻshish yoki oʻzgartirish uchun, iltimos, MediaWikining [https://translatewiki.net/ translatewiki.net] mahalliylashtirish saytidan foydalaning.",
"subject": "Mavzu/sarlavha",
"minoredit": "Bu kichik tahrir",
"watchthis": "Sahifani kuzatish",
- "savearticle": "Saqla",
- "publishpage": "Sahifani chop et",
- "publishchanges": "Oʻzgarishlarni chop et",
+ "savearticle": "Chop etish",
+ "savechanges": "Oʻzgarishlarni saqlash",
+ "publishpage": "Sahifani chop etish",
+ "publishchanges": "Oʻzgarishlarni chop etish",
"preview": "Ko‘rib chiqish",
"showpreview": "Koʻrib chiqish",
"showdiff": "Kiritilgan o‘zgarishlar",
"rev-deleted-user": "(muallif nomi oʻchirilgan)",
"rev-deleted-event": "(jurnal tafsilotlari o‘chirildi)",
"rev-delundel": "koʻrsatish/yashirish",
- "rev-showdeleted": "koʻrsatish",
+ "rev-showdeleted": "koʻrsat",
"revdelete-show-file-submit": "Ha",
"revdelete-confirm": "Iltimos, haqiqatdan ham shu harakatni amalga oshirmoqchiligingizni, uning oqibatlarini tushunib turganingizni va harakatingiz [[{{MediaWiki:Policy-url}}|qoidalarga]] asosanlanganini tasdiqlang.",
"revdelete-hide-text": "Sahifaning ushbu versiyasi matnini yashirish",
"prefs-signature": "Imzo",
"prefs-dateformat": "Sana formati",
"prefs-timeoffset": "Vaqt farqi",
- "prefs-advancedediting": "Qoʻshimcha moslamalar",
+ "prefs-advancedediting": "Asosiy bandlar",
+ "prefs-editor": "Tahrirlagich",
"prefs-advancedrc": "Qoʻshimcha moslamalar",
"prefs-advancedrendering": "Qoʻshimcha moslamalar",
"prefs-advancedsearchoptions": "Qoʻshimcha moslamalar",
"userrights": "Huquqlarini oʻzgartirish",
"userrights-lookup-user": "Foydalanuvchini tanlash",
"userrights-user-editname": "Foydalanuvchi nomi:",
- "editusergroup": "Shu foydalanuvchi huquqlarini oʻzgartirish",
+ "editusergroup": "Qaysi guruhlarga aʼzo ekanligini koʻrish",
"editinguser": "{{GENDER:$1|Foydalanuvchi}} <strong>[[User:$1|$1]]</strong> $2 huquqlarini oʻzgartirish",
"userrights-editusergroup": "Guruhlardagi aʼzoligini oʻzgartirish",
"saveusergroups": "Oʻzgarishlarni saqlash",
"newuserlogpage": "Foydalanuvchilarni roʻyxatga olish qaydlari",
"newuserlogpagetext": "Yaqinda roʻyxatdan oʻtgan foydalanuvchilar roʻyxati",
"rightslog": "Foydalanuvchi huquqlari koʻrsatilgan qaydlar",
- "rightslogtext": "Foydalanuvchi huquqlarini oʻzgartirish qaydlari.",
+ "rightslogtext": "Bu sahifada foydalanuvchilarning huquqlarini oʻzgartirish qaydlari koʻrsatilgan.",
"action-edit": "ushbu sahifani tahrirlash",
"action-move": "bu sahifani koʻchirish",
"action-move-subpages": "Bu sahifani va uning ostsahifalarini koʻchirish",
"recentchanges-label-plusminus": "Sahifa vazni qanchaga oʻzgargani (bayt)",
"recentchanges-legend-heading": "<strong>Izoh:</strong>",
"recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} ([[Special:NewPages|alohida roʻyxat]])",
+ "recentchanges-submit": "Koʻrsat",
+ "rcfilters-activefilters-show": "Koʻrsat",
"rcnotefrom": "Quyida <strong>$3, $4</strong> dan keyin sodir boʻlgan oʻzgarishlar koʻrsatilgan (oxirgi <strong>$1</strong> tasi).",
"rclistfrom": "$3, $2 dan keyin sodir boʻlgan oʻzgarishlarni koʻrsat",
"rcshowhideminor": "Kichik tahrirlarni $1",
- "rcshowhideminor-show": "koʻrsat",
+ "rcshowhideminor-show": "Koʻrsat",
"rcshowhideminor-hide": "yashir",
"rcshowhidebots": "Botlarni $1",
- "rcshowhidebots-show": "koʻrsat",
+ "rcshowhidebots-show": "Koʻrsat",
"rcshowhidebots-hide": "yashir",
"rcshowhideliu": "Roʻyxatdan oʻtganlarni $1",
"rcshowhideliu-show": "koʻrsat",
"rcshowhideliu-hide": "yashir",
"rcshowhideanons": "Anonimlarni $1",
- "rcshowhideanons-show": "koʻrsat",
+ "rcshowhideanons-show": "Koʻrsat",
"rcshowhideanons-hide": "yashir",
"rcshowhidepatr": "Tekshirilgan tahrirlarni $1",
+ "rcshowhidepatr-show": "Koʻrsat",
"rcshowhidepatr-hide": "yashir",
"rcshowhidemine": "Oʻz tahrirlarimni $1",
- "rcshowhidemine-show": "koʻrsat",
+ "rcshowhidemine-show": "Koʻrsat",
"rcshowhidemine-hide": "yashir",
"rclinks": "Oxirgi $2 kun ichida sodir boʻlgan $1 ta oʻzgarish koʻrsatildi",
"diff": "farq",
"hist": "tarix",
- "hide": "yashir",
- "show": "koʻrsat",
+ "hide": "Yashir",
+ "show": "Koʻrsat",
"minoreditletter": "k",
"newpageletter": "Y",
"boteditletter": "b",
"statistics-header-users": "Foydalanuvchilar statistikasi",
"statistics-articles": "Maqolalar",
"statistics-pages": "Sahifalar",
- "statistics-pages-desc": "Ushbu vikidagi barcha sahifalar, jumladan munozara, yoʻnaltirish va hk.",
+ "statistics-pages-desc": "Ushbu vikidagi barcha sahifalar, shu jumladan munozara sahifalari, qayta yoʻnaltiruvchi va boshqa sahifalar",
"statistics-files": "Yuklangan fayllar",
"statistics-edits": "{{SITENAME}}dagi tahrirlarning umumiy soni",
"statistics-edits-average": "Oʻrtacha tahrirlar soni (sahifa boshiga)",
"pageswithprop-prop": "Xossa nomi:",
"pageswithprop-submit": "Oʻtish",
"brokenredirects-edit": "tahrirlash",
+ "withoutinterwiki-submit": "Koʻrsat",
"nbytes": "$1 {{PLURAL:$1|bayt}}",
"ncategories": "$1 {{PLURAL:$1|turkum|turkumlar}}",
"nmembers": "$1 {{PLURAL:$1|ta sahifa}}",
"wantedcategories": "Talab qilinayotgan turkumlar",
"mostcategories": "Eng koʻp turkumli sahifalar",
"prefixindex": "Prefiksli barcha sahifalar",
+ "prefixindex-submit": "Koʻrsat",
"prefixindex-strip": "Natijalar roʻyxatida prefiks koʻrsatilmasin",
"protectedpages": "Himoyalangan sahifalar",
"listusers": "Foydalanuvchilar roʻyxati",
"usercreated": "$1, $2 da {{GENDER:$3|roʻyxatdan oʻtgan}}",
"newpages": "Yangi sahifalar",
+ "newpages-submit": "Koʻrsat",
"move": "Ko‘chirish",
"movethispage": "Bu sahifani koʻchirish",
"pager-newer-n": "{{PLURAL:$1|yangiroq 1|yangiroq $1}}",
"specialloguserlabel": "Ijrochi:",
"speciallogtitlelabel": "Moʻljal:",
"log": "Qaydlar",
+ "logeventslist-submit": "Koʻrsat",
"all-logs-page": "Barcha ochiq qaydlar",
"alllogstext": "{{SITENAME}}dagi barcha jurnallar roʻyxati.\nNatijalarni jurnal nomi, foydalanuvchi nomi (harflar katta-kichikligi inobatga olinadi) yoki sahifa nomi boʻyicha saralashingiz mumkin.\n* Biror foydalanuvchi amalga oshirgan qaydni topish uchun uning foydalanuvchi nomini „Ijrochi“ oynasiga kiriting.\n* Biror foydalanuvchi yoki sahifaga nisbatan amalga oshirilgan qaydni topish uchun ulardan birining nomini „Moʻljal“ oynasiga kiriting.",
"logempty": "Talabga mos yozuvlar mavjud emas.",
"allpages-hide-redirects": "Yoʻnaltirishlarni yashirish",
"cachedspecial-refresh-now": "Oxirgi versiyasini koʻrish",
"categories": "Turkumlar",
+ "categories-submit": "Koʻrsat",
"categoriespagetext": "Quyidagi {{PLURAL:$1|turkumda|turkumlarda}} sahifa yoki media-fayllar mavjud.\n[[Special:UnusedCategories|Ishlatilmayotgan turkumlar]] bu yerda koʻrsatilmaydi.\nShuningdek qarang: [[Special:WantedCategories|talab qilinayotgan turkumlar]].",
"categoriesfrom": "Quyidagidan boshlanuvchi turkumlarni koʻrsatish:",
"deletedcontributions": "Foydalanuvchining o‘chirilgan hissasi",
"linksearch-ok": "Qidirish",
"linksearch-line": "$2 ichidan $1 ga havola",
"listusersfrom": "Quyidagidan boshlanuvchi foydalanuvchilarni koʻrsatish:",
- "listusers-submit": "Koʻrsatish",
+ "listusers-submit": "Koʻrsat",
"listusers-noresult": "Foydalanuvchilar topilmadi.",
"listusers-blocked": "(chetlashtirilgan)",
"activeusers": "Faol foydalanuvchilar roʻyxati",
"wlheader-showupdated": "Siz oxirgi marta kirganingizdan keyin oʻzgartirilgan sahifalar '''qalin''' yozuv bilan ajratib koʻrsatilgan.",
"wlnote": "Quyida oxirgi $2 soat ichida sodir boʻlgan {{PLURAL:$1|oxirgi oʻzgarish|<strong>$1</strong> ta oʻzgarishlar}} koʻrsatilgan. $3, $4.",
"wlshowlast": "Oxirgi $1 soatdagi $2 kundagi tahrirlarni koʻrsatish",
+ "watchlist-submit": "Koʻrsat",
"watchlist-options": "Kuzatuv roʻyxati moslamalari",
"watching": "Kuzatish...",
"unwatching": "Kuzatuv roʻyxatidan oʻchirilmoqda...",
"delete-confirm": "$1 — oʻchirish",
"delete-legend": "Sahifani o‘chirish",
"historywarning": "<strong>Diqqat:</strong> Siz oʻchirmoqchi boʻlayotgan sahifaning tarixida $1 ta {{PLURAL:$1|versiyasi}} bor:",
+ "historyaction-submit": "Koʻrsat",
"confirmdeletetext": "Siz ushbu sahifani va uning tarixini butunlay oʻchirib tashlamoqchi boʻlyapsiz. Iltimos, [[Special:Whatlinkshere/{{FULLPAGENAMEE}}|bogʻlangan sahifalar]] bilan tanishib chiqishni unutmang.",
"actioncomplete": "Bajarildi",
"actionfailed": "Jarayon amalga oshmadi",
"contributions-title": "{{GENDER:$1|Foydalanuvchi}} $1 hissasi",
"mycontris": "Hissam",
"anoncontribs": "Qoʻshilgan hissa",
- "contribsub2": "$1 uchun ($2)",
+ "contribsub2": "$1ning tahrirlari ($2)",
"nocontribs": "Belgilangan shartlarga muvofiq oʻzgarishlar topilmadi.",
"uctop": "joriy",
"month": "Oydan (va avvalroq)",
"year": "Yildan (va avvalroq)",
+ "date": "Shu sanadan avvalroq:",
"sp-contributions-newbies": "Faqatgina yangi foydalanuvchilarning hissalarini koʻrsat",
"sp-contributions-newbies-sub": "Yangi hisob yozuvlaridan",
"sp-contributions-newbies-title": "Yangi hisob yozuvlarining hissalari",
"tooltip-ca-nstab-category": "Turkum sahifasini koʻrish",
"tooltip-minoredit": "Kichik o‘zgartirish sifatida belgilash",
"tooltip-save": "Oʻzgarishlarni saqlash",
+ "tooltip-publish": "Siz kiritgan oʻzgarishlarni chop etish",
"tooltip-preview": "Oʻzgarishlarni koʻrib chiqish; Iltimos, saqlashdan oldin undan foydalaning!",
"tooltip-diff": "Matnga qanday oʻzgarishlar kiritganligingizni koʻrish.",
"tooltip-compareselectedversions": "Bu sahifaning ikki tanlangan versiyalari orasidagi farqni koʻrish.",
"watchlisttools-raw": "Kuzatuv roʻyxatimni tahrirlash",
"signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|munozara]])",
"duplicate-defaultsort": "'''Diqqat:''' \"$2\" boshlang'ich saralash kaliti oldingi \"$1\" boshlang'ich saralash kalitini qayta aniqlayapti.",
+ "version": "Versiyasi",
"version-specialpages": "Maxsus sahifalar",
+ "version-ext-colheader-version": "Versiyasi",
+ "version-software-version": "Versiyasi",
+ "version-libraries-version": "Versiyasi",
"specialpages": "Maxsus sahifalar",
"tag-filter": "[[Special:Tags|Nishonlar]] filtri:",
"tag-list-wrapper": "[[Special:Tags|{{PLURAL:$1|Teg|Teglar}}]]: $2",
"special-characters-group-khmer": "Kxmer",
"special-characters-title-emdash": "uzun tire",
"special-characters-title-minus": "minus belgisi",
- "mw-widgets-abandonedit": "Siz haqiqatdan ham oʻzgarishlarni saqlamasdan koʻrish tartibiga oʻtishni xohlaysizmi?"
+ "mw-widgets-abandonedit": "Siz haqiqatdan ham oʻzgarishlarni saqlamasdan koʻrish tartibiga oʻtishni xohlaysizmi?",
+ "log-action-filter-block": "Chetlatish turi:",
+ "log-action-filter-rights": "Huquqlarni oʻzgartirish turi:",
+ "log-action-filter-all": "Barchasi",
+ "log-action-filter-block-block": "Chetlatish",
+ "log-action-filter-block-reblock": "Chetlatish turini oʻzgartirish",
+ "log-action-filter-block-unblock": "Chetlatishni bekor qilish",
+ "log-action-filter-rights-rights": "Qoʻlda kiritilgan oʻzgarish",
+ "authprovider-resetpass-skip-label": "Qoldirib ketish"
}
"booksources-search": "זוכן",
"booksources-text": "אונטן איז א ליסטע פון סייטס וואס פֿארקויפֿן נייע און גענוצטע ביכער און האבן אויך נאך אינפֿארמאציע וועגן די ביכער וואס איר זוכט:",
"booksources-invalid-isbn": "דאָס געגעבענע ISBN זעט נישט אויס צו זיין גילטיק; קאנטראלירט פֿאַר גרײַזן בײַם קאפּירן פון דעם ערשטיקן מקור.",
+ "magiclink-tracking-rfc": "בלעטער וואס ניצן מאגישע RFC-לינקען",
+ "magiclink-tracking-pmid": "בלעטער וואס ניצן מאגישע PMID-לינקען",
+ "magiclink-tracking-isbn": "בלעטער וואס ניצן מאגישע ISBN-לינקען",
"specialloguserlabel": "אויספֿירער:",
"speciallogtitlelabel": "ציל (טיטל אדער {{ns:user}}:באניצער־נאמען פאר א באניצער):",
"log": "לאגביכער",
"edit-gone-missing": "不能更新页面。\n它可能刚刚被删除。",
"edit-conflict": "编辑冲突。",
"edit-no-change": "因为没有文字更改,您的编辑已被忽略。",
+ "edit-slots-cannot-add": "下列{{PLURAL:$1|栏位|栏位}}在此不受支持:$2。",
+ "edit-slots-cannot-remove": "下列{{PLURAL:$1|栏位|栏位}}是必需的且无法被移除:$2。",
"postedit-confirmation-created": "页面已创建。",
"postedit-confirmation-restored": "页面已恢复。",
"postedit-confirmation-saved": "您的编辑已保存。",
# Integrity from link modals at https://code.jquery.com/qunit/
files:
qunit.js:
- src: https://code.jquery.com/qunit/qunit-2.6.2.js
- integrity: sha256-72OhbBvECs6Z5vG0GfPqiyYvTf8vhdEVHKQcacIcIeM=
+ src: http://code.jquery.com/qunit/qunit-2.9.1.js
+ integrity: sha256-eNccBdxd8zReziWcVjEsPeyJDi3LKMYnzMXyDv8bzsU=
qunit.css:
- src: https://code.jquery.com/qunit/qunit-2.6.2.css
- integrity: sha256-qpkurjTvVTJJCSpMABcvF4IlYUJkd8saxiHgUQpEjX8=
+ src: https://code.jquery.com/qunit/qunit-2.9.1.css
+ integrity: sha256-SSS7o92V7wzcIFg3qnJL9mc4msePaT4klbxtuSGvVVo=
sinonjs:
type: file
$( '.config-help-field-data' ).hide()
.closest( '.config-help-field-container' ).find( '.config-help-field-hint' )
.show()
- .click( function () {
+ .on( 'click', function () {
+ // FIXME: Use CSS transition
+ // eslint-disable-next-line jquery/no-slide
$( this ).closest( '.config-help-field-container' ).find( '.config-help-field-data' )
.slideToggle( 'fast' );
} );
$( document.getElementById( $( this ).attr( 'rel' ) ) ).hide();
} );
$( document.getElementById( $( '.dbRadio:checked' ).attr( 'rel' ) ) ).show();
- $( '.dbRadio' ).click( function () {
+ $( '.dbRadio' ).on( 'click', function () {
var $checked = $( '.dbRadio:checked' ),
$wrapper = $( document.getElementById( $checked.attr( 'rel' ) ) );
if ( $wrapper.is( ':hidden' ) ) {
+ // FIXME: Use CSS transition
+ // eslint-disable-next-line jquery/no-animate-toggle
$( '.dbWrapper' ).hide( 'slow' );
+ // eslint-disable-next-line jquery/no-animate-toggle
$wrapper.show( 'slow' );
}
} );
} );
// Show/hide Creative Commons thingy
- $( '.licenseRadio' ).click( function () {
+ $( '.licenseRadio' ).on( 'click', function () {
var $wrapper = $( '#config-cc-wrapper' );
if ( $( '#config__LicenseCode_cc-choose' ).is( ':checked' ) ) {
+ // FIXME: Use CSS transition
+ // eslint-disable-next-line jquery/no-animate-toggle
$wrapper.show( 'slow' );
} else {
+ // eslint-disable-next-line jquery/no-animate-toggle
$wrapper.hide( 'slow' );
}
} );
// Show/hide random stuff (email, upload)
- $( '.showHideRadio' ).click( function () {
+ $( '.showHideRadio' ).on( 'click', function () {
var $wrapper = $( '#' + $( this ).attr( 'rel' ) );
if ( $( this ).is( ':checked' ) ) {
+ // FIXME: Use CSS transition
+ // eslint-disable-next-line jquery/no-animate-toggle
$wrapper.show( 'slow' );
} else {
+ // eslint-disable-next-line jquery/no-animate-toggle
$wrapper.hide( 'slow' );
}
} );
- $( '.hideShowRadio' ).click( function () {
+ $( '.hideShowRadio' ).on( 'click', function () {
var $wrapper = $( '#' + $( this ).attr( 'rel' ) );
if ( $( this ).is( ':checked' ) ) {
+ // FIXME: Use CSS transition
+ // eslint-disable-next-line jquery/no-animate-toggle
$wrapper.hide( 'slow' );
} else {
+ // eslint-disable-next-line jquery/no-animate-toggle
$wrapper.show( 'slow' );
}
} );
}
// Enable/disable "other" textboxes
- $( '.enableForOther' ).click( function () {
+ $( '.enableForOther' ).on( 'click', function () {
var $textbox = $( document.getElementById( $( this ).attr( 'rel' ) ) );
// FIXME: Ugh, this is ugly
if ( $( this ).val() === 'other' ) {
+ // FIXME: Use CSS transition
+ // eslint-disable-next-line jquery/no-slide
$textbox.prop( 'readonly', false ).closest( '.config-block' ).slideDown( 'fast' );
} else {
+ // eslint-disable-next-line jquery/no-slide
$textbox.prop( 'readonly', true ).closest( '.config-block' ).slideUp( 'fast' );
}
} );
$( '#config_wgSitename' ).on( 'keyup change', syncText ).each( syncText );
// Show/Hide memcached servers when needed
- $( 'input[name$="config__MainCacheType"]' ).change( function () {
+ $( 'input[name$="config__MainCacheType"]' ).on( 'change', function () {
var $memc = $( '#config-memcachewrapper' );
if ( $( 'input[name$="config__MainCacheType"]:checked' ).val() === 'memcached' ) {
+ // FIXME: Use CSS transition
+ // eslint-disable-next-line jquery/no-animate-toggle
$memc.show( 'slow' );
} else {
+ // eslint-disable-next-line jquery/no-animate-toggle
$memc.hide( 'slow' );
}
} );
},
"devDependencies": {
"deepmerge": "1.3.2",
- "eslint-config-wikimedia": "0.9.0",
+ "eslint-config-wikimedia": "0.10.0",
"grunt": "1.0.3",
"grunt-banana-checker": "0.6.0",
"grunt-contrib-copy": "1.0.0",
"karma-mocha-reporter": "2.2.5",
"karma-qunit": "2.1.0",
"postcss-less": "2.0.0",
- "qunit": "2.6.2",
+ "qunit": "2.9.1",
"stylelint": "9.6.0",
"stylelint-config-wikimedia": "0.5.0",
"wdio-junit-reporter": "0.2.0",
'resources/src/mediawiki.special/upload.css',
'resources/src/mediawiki.special/userrights.css',
'resources/src/mediawiki.special/watchlist.css',
- 'resources/src/mediawiki.special/block.less'
+ 'resources/src/mediawiki.special/block.less',
],
'targets' => [ 'desktop', 'mobile' ],
],
/*!
- * QUnit 2.6.2
+ * QUnit 2.9.1
* https://qunitjs.com/
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license
* https://jquery.org/license
*
- * Date: 2018-08-19T19:37Z
+ * Date: 2019-01-07T16:37Z
*/
/** Font Family and Sizes */
/*!
- * QUnit 2.6.2
+ * QUnit 2.9.1
* https://qunitjs.com/
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license
* https://jquery.org/license
*
- * Date: 2018-08-19T19:37Z
+ * Date: 2019-01-07T16:37Z
*/
(function (global$1) {
'use strict';
global$1 = global$1 && global$1.hasOwnProperty('default') ? global$1['default'] : global$1;
- var window = global$1.window;
+ var window$1 = global$1.window;
var self$1 = global$1.self;
var console = global$1.console;
- var setTimeout = global$1.setTimeout;
+ var setTimeout$1 = global$1.setTimeout;
var clearTimeout = global$1.clearTimeout;
- var document = window && window.document;
- var navigator = window && window.navigator;
+ var document$1 = window$1 && window$1.document;
+ var navigator = window$1 && window$1.navigator;
var localSessionStorage = function () {
var x = "qunit-test-string";
}
}();
+ /**
+ * Returns a function that proxies to the given method name on the globals
+ * console object. The proxy will also detect if the console doesn't exist and
+ * will appropriately no-op. This allows support for IE9, which doesn't have a
+ * console if the developer tools are not open.
+ */
+ function consoleProxy(method) {
+ return function () {
+ if (console) {
+ console[method].apply(console, arguments);
+ }
+ };
+ }
+
+ var Logger = {
+ warn: consoleProxy("warn")
+ };
+
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
return typeof obj;
} : function (obj) {
return new Date().getTime();
};
+ var hasPerformanceApi = detectPerformanceApi();
+ var performance = hasPerformanceApi ? window$1.performance : undefined;
+ var performanceNow = hasPerformanceApi ? performance.now.bind(performance) : now;
+
+ function detectPerformanceApi() {
+ return window$1 && typeof window$1.performance !== "undefined" && typeof window$1.performance.mark === "function" && typeof window$1.performance.measure === "function";
+ }
+
+ function measure(comment, startMark, endMark) {
+
+ // `performance.measure` may fail if the mark could not be found.
+ // reasons a specific mark could not be found include: outside code invoking `performance.clearMarks()`
+ try {
+ performance.measure(comment, startMark, endMark);
+ } catch (ex) {
+ Logger.warn("performance.measure could not be executed because of ", ex.message);
+ }
+ }
+
var defined = {
- document: window && window.document !== undefined,
- setTimeout: setTimeout !== undefined
+ document: window$1 && window$1.document !== undefined,
+ setTimeout: setTimeout$1 !== undefined
};
// Returns a new Array with the elements that are in a but not in b
};
// take a predefined QUnit.config and extend the defaults
- var globalConfig = window && window.QUnit && window.QUnit.config;
+ var globalConfig = window$1 && window$1.QUnit && window$1.QUnit.config;
// only extend the global config if there is no QUnit overload
- if (window && window.QUnit && !window.QUnit.version) {
+ if (window$1 && window$1.QUnit && !window$1.QUnit.version) {
extend(config, globalConfig);
}
key: "start",
value: function start(recordTime) {
if (recordTime) {
- this._startTime = Date.now();
+ this._startTime = performanceNow();
+
+ if (performance) {
+ var suiteLevel = this.fullName.length;
+ performance.mark("qunit_suite_" + suiteLevel + "_start");
+ }
}
return {
key: "end",
value: function end(recordTime) {
if (recordTime) {
- this._endTime = Date.now();
+ this._endTime = performanceNow();
+
+ if (performance) {
+ var suiteLevel = this.fullName.length;
+ performance.mark("qunit_suite_" + suiteLevel + "_end");
+
+ var suiteName = this.fullName.join(" – ");
+
+ measure(suiteLevel === 0 ? "QUnit Test Run" : "QUnit Test Suite: " + suiteName, "qunit_suite_" + suiteLevel + "_start", "qunit_suite_" + suiteLevel + "_end");
+ }
}
return {
}
}
+ function objectOrFunction(x) {
+ var type = typeof x === 'undefined' ? 'undefined' : _typeof(x);
+ return x !== null && (type === 'object' || type === 'function');
+ }
+
+ function isFunction(x) {
+ return typeof x === 'function';
+ }
+
+
+
+ var _isArray = void 0;
+ if (Array.isArray) {
+ _isArray = Array.isArray;
+ } else {
+ _isArray = function _isArray(x) {
+ return Object.prototype.toString.call(x) === '[object Array]';
+ };
+ }
+
+ var isArray = _isArray;
+
+ var len = 0;
+ var vertxNext = void 0;
+ var customSchedulerFn = void 0;
+
+ var asap = function asap(callback, arg) {
+ queue[len] = callback;
+ queue[len + 1] = arg;
+ len += 2;
+ if (len === 2) {
+ // If len is 2, that means that we need to schedule an async flush.
+ // If additional callbacks are queued before the queue is flushed, they
+ // will be processed by this flush that we are scheduling.
+ if (customSchedulerFn) {
+ customSchedulerFn(flush);
+ } else {
+ scheduleFlush();
+ }
+ }
+ };
+
+ function setScheduler(scheduleFn) {
+ customSchedulerFn = scheduleFn;
+ }
+
+ function setAsap(asapFn) {
+ asap = asapFn;
+ }
+
+ var browserWindow = typeof window !== 'undefined' ? window : undefined;
+ var browserGlobal = browserWindow || {};
+ var BrowserMutationObserver = browserGlobal.MutationObserver || browserGlobal.WebKitMutationObserver;
+ var isNode = typeof self === 'undefined' && typeof process !== 'undefined' && {}.toString.call(process) === '[object process]';
+
+ // test for web worker but not in IE10
+ var isWorker = typeof Uint8ClampedArray !== 'undefined' && typeof importScripts !== 'undefined' && typeof MessageChannel !== 'undefined';
+
+ // node
+ function useNextTick() {
+ // node version 0.10.x displays a deprecation warning when nextTick is used recursively
+ // see https://github.com/cujojs/when/issues/410 for details
+ return function () {
+ return process.nextTick(flush);
+ };
+ }
+
+ // vertx
+ function useVertxTimer() {
+ if (typeof vertxNext !== 'undefined') {
+ return function () {
+ vertxNext(flush);
+ };
+ }
+
+ return useSetTimeout();
+ }
+
+ function useMutationObserver() {
+ var iterations = 0;
+ var observer = new BrowserMutationObserver(flush);
+ var node = document.createTextNode('');
+ observer.observe(node, { characterData: true });
+
+ return function () {
+ node.data = iterations = ++iterations % 2;
+ };
+ }
+
+ // web worker
+ function useMessageChannel() {
+ var channel = new MessageChannel();
+ channel.port1.onmessage = flush;
+ return function () {
+ return channel.port2.postMessage(0);
+ };
+ }
+
+ function useSetTimeout() {
+ // Store setTimeout reference so es6-promise will be unaffected by
+ // other code modifying setTimeout (like sinon.useFakeTimers())
+ var globalSetTimeout = setTimeout;
+ return function () {
+ return globalSetTimeout(flush, 1);
+ };
+ }
+
+ var queue = new Array(1000);
+ function flush() {
+ for (var i = 0; i < len; i += 2) {
+ var callback = queue[i];
+ var arg = queue[i + 1];
+
+ callback(arg);
+
+ queue[i] = undefined;
+ queue[i + 1] = undefined;
+ }
+
+ len = 0;
+ }
+
+ function attemptVertx() {
+ try {
+ var vertx = Function('return this')().require('vertx');
+ vertxNext = vertx.runOnLoop || vertx.runOnContext;
+ return useVertxTimer();
+ } catch (e) {
+ return useSetTimeout();
+ }
+ }
+
+ var scheduleFlush = void 0;
+ // Decide what async method to use to triggering processing of queued callbacks:
+ if (isNode) {
+ scheduleFlush = useNextTick();
+ } else if (BrowserMutationObserver) {
+ scheduleFlush = useMutationObserver();
+ } else if (isWorker) {
+ scheduleFlush = useMessageChannel();
+ } else if (browserWindow === undefined && typeof require === 'function') {
+ scheduleFlush = attemptVertx();
+ } else {
+ scheduleFlush = useSetTimeout();
+ }
+
+ function then(onFulfillment, onRejection) {
+ var parent = this;
+
+ var child = new this.constructor(noop);
+
+ if (child[PROMISE_ID] === undefined) {
+ makePromise(child);
+ }
+
+ var _state = parent._state;
+
+
+ if (_state) {
+ var callback = arguments[_state - 1];
+ asap(function () {
+ return invokeCallback(_state, child, callback, parent._result);
+ });
+ } else {
+ subscribe(parent, child, onFulfillment, onRejection);
+ }
+
+ return child;
+ }
+
+ /**
+ `Promise.resolve` returns a promise that will become resolved with the
+ passed `value`. It is shorthand for the following:
+
+ ```javascript
+ let promise = new Promise(function(resolve, reject){
+ resolve(1);
+ });
+
+ promise.then(function(value){
+ // value === 1
+ });
+ ```
+
+ Instead of writing the above, your code now simply becomes the following:
+
+ ```javascript
+ let promise = Promise.resolve(1);
+
+ promise.then(function(value){
+ // value === 1
+ });
+ ```
+
+ @method resolve
+ @static
+ @param {Any} value value that the returned promise will be resolved with
+ Useful for tooling.
+ @return {Promise} a promise that will become fulfilled with the given
+ `value`
+ */
+ function resolve$1(object) {
+ /*jshint validthis:true */
+ var Constructor = this;
+
+ if (object && (typeof object === 'undefined' ? 'undefined' : _typeof(object)) === 'object' && object.constructor === Constructor) {
+ return object;
+ }
+
+ var promise = new Constructor(noop);
+ resolve(promise, object);
+ return promise;
+ }
+
+ var PROMISE_ID = Math.random().toString(36).substring(2);
+
+ function noop() {}
+
+ var PENDING = void 0;
+ var FULFILLED = 1;
+ var REJECTED = 2;
+
+ var TRY_CATCH_ERROR = { error: null };
+
+ function selfFulfillment() {
+ return new TypeError("You cannot resolve a promise with itself");
+ }
+
+ function cannotReturnOwn() {
+ return new TypeError('A promises callback cannot return that same promise.');
+ }
+
+ function getThen(promise) {
+ try {
+ return promise.then;
+ } catch (error) {
+ TRY_CATCH_ERROR.error = error;
+ return TRY_CATCH_ERROR;
+ }
+ }
+
+ function tryThen(then$$1, value, fulfillmentHandler, rejectionHandler) {
+ try {
+ then$$1.call(value, fulfillmentHandler, rejectionHandler);
+ } catch (e) {
+ return e;
+ }
+ }
+
+ function handleForeignThenable(promise, thenable, then$$1) {
+ asap(function (promise) {
+ var sealed = false;
+ var error = tryThen(then$$1, thenable, function (value) {
+ if (sealed) {
+ return;
+ }
+ sealed = true;
+ if (thenable !== value) {
+ resolve(promise, value);
+ } else {
+ fulfill(promise, value);
+ }
+ }, function (reason) {
+ if (sealed) {
+ return;
+ }
+ sealed = true;
+
+ reject(promise, reason);
+ }, 'Settle: ' + (promise._label || ' unknown promise'));
+
+ if (!sealed && error) {
+ sealed = true;
+ reject(promise, error);
+ }
+ }, promise);
+ }
+
+ function handleOwnThenable(promise, thenable) {
+ if (thenable._state === FULFILLED) {
+ fulfill(promise, thenable._result);
+ } else if (thenable._state === REJECTED) {
+ reject(promise, thenable._result);
+ } else {
+ subscribe(thenable, undefined, function (value) {
+ return resolve(promise, value);
+ }, function (reason) {
+ return reject(promise, reason);
+ });
+ }
+ }
+
+ function handleMaybeThenable(promise, maybeThenable, then$$1) {
+ if (maybeThenable.constructor === promise.constructor && then$$1 === then && maybeThenable.constructor.resolve === resolve$1) {
+ handleOwnThenable(promise, maybeThenable);
+ } else {
+ if (then$$1 === TRY_CATCH_ERROR) {
+ reject(promise, TRY_CATCH_ERROR.error);
+ TRY_CATCH_ERROR.error = null;
+ } else if (then$$1 === undefined) {
+ fulfill(promise, maybeThenable);
+ } else if (isFunction(then$$1)) {
+ handleForeignThenable(promise, maybeThenable, then$$1);
+ } else {
+ fulfill(promise, maybeThenable);
+ }
+ }
+ }
+
+ function resolve(promise, value) {
+ if (promise === value) {
+ reject(promise, selfFulfillment());
+ } else if (objectOrFunction(value)) {
+ handleMaybeThenable(promise, value, getThen(value));
+ } else {
+ fulfill(promise, value);
+ }
+ }
+
+ function publishRejection(promise) {
+ if (promise._onerror) {
+ promise._onerror(promise._result);
+ }
+
+ publish(promise);
+ }
+
+ function fulfill(promise, value) {
+ if (promise._state !== PENDING) {
+ return;
+ }
+
+ promise._result = value;
+ promise._state = FULFILLED;
+
+ if (promise._subscribers.length !== 0) {
+ asap(publish, promise);
+ }
+ }
+
+ function reject(promise, reason) {
+ if (promise._state !== PENDING) {
+ return;
+ }
+ promise._state = REJECTED;
+ promise._result = reason;
+
+ asap(publishRejection, promise);
+ }
+
+ function subscribe(parent, child, onFulfillment, onRejection) {
+ var _subscribers = parent._subscribers;
+ var length = _subscribers.length;
+
+
+ parent._onerror = null;
+
+ _subscribers[length] = child;
+ _subscribers[length + FULFILLED] = onFulfillment;
+ _subscribers[length + REJECTED] = onRejection;
+
+ if (length === 0 && parent._state) {
+ asap(publish, parent);
+ }
+ }
+
+ function publish(promise) {
+ var subscribers = promise._subscribers;
+ var settled = promise._state;
+
+ if (subscribers.length === 0) {
+ return;
+ }
+
+ var child = void 0,
+ callback = void 0,
+ detail = promise._result;
+
+ for (var i = 0; i < subscribers.length; i += 3) {
+ child = subscribers[i];
+ callback = subscribers[i + settled];
+
+ if (child) {
+ invokeCallback(settled, child, callback, detail);
+ } else {
+ callback(detail);
+ }
+ }
+
+ promise._subscribers.length = 0;
+ }
+
+ function tryCatch(callback, detail) {
+ try {
+ return callback(detail);
+ } catch (e) {
+ TRY_CATCH_ERROR.error = e;
+ return TRY_CATCH_ERROR;
+ }
+ }
+
+ function invokeCallback(settled, promise, callback, detail) {
+ var hasCallback = isFunction(callback),
+ value = void 0,
+ error = void 0,
+ succeeded = void 0,
+ failed = void 0;
+
+ if (hasCallback) {
+ value = tryCatch(callback, detail);
+
+ if (value === TRY_CATCH_ERROR) {
+ failed = true;
+ error = value.error;
+ value.error = null;
+ } else {
+ succeeded = true;
+ }
+
+ if (promise === value) {
+ reject(promise, cannotReturnOwn());
+ return;
+ }
+ } else {
+ value = detail;
+ succeeded = true;
+ }
+
+ if (promise._state !== PENDING) {
+ // noop
+ } else if (hasCallback && succeeded) {
+ resolve(promise, value);
+ } else if (failed) {
+ reject(promise, error);
+ } else if (settled === FULFILLED) {
+ fulfill(promise, value);
+ } else if (settled === REJECTED) {
+ reject(promise, value);
+ }
+ }
+
+ function initializePromise(promise, resolver) {
+ try {
+ resolver(function resolvePromise(value) {
+ resolve(promise, value);
+ }, function rejectPromise(reason) {
+ reject(promise, reason);
+ });
+ } catch (e) {
+ reject(promise, e);
+ }
+ }
+
+ var id = 0;
+ function nextId() {
+ return id++;
+ }
+
+ function makePromise(promise) {
+ promise[PROMISE_ID] = id++;
+ promise._state = undefined;
+ promise._result = undefined;
+ promise._subscribers = [];
+ }
+
+ function validationError() {
+ return new Error('Array Methods must be provided an Array');
+ }
+
+ var Enumerator = function () {
+ function Enumerator(Constructor, input) {
+ classCallCheck(this, Enumerator);
+
+ this._instanceConstructor = Constructor;
+ this.promise = new Constructor(noop);
+
+ if (!this.promise[PROMISE_ID]) {
+ makePromise(this.promise);
+ }
+
+ if (isArray(input)) {
+ this.length = input.length;
+ this._remaining = input.length;
+
+ this._result = new Array(this.length);
+
+ if (this.length === 0) {
+ fulfill(this.promise, this._result);
+ } else {
+ this.length = this.length || 0;
+ this._enumerate(input);
+ if (this._remaining === 0) {
+ fulfill(this.promise, this._result);
+ }
+ }
+ } else {
+ reject(this.promise, validationError());
+ }
+ }
+
+ createClass(Enumerator, [{
+ key: '_enumerate',
+ value: function _enumerate(input) {
+ for (var i = 0; this._state === PENDING && i < input.length; i++) {
+ this._eachEntry(input[i], i);
+ }
+ }
+ }, {
+ key: '_eachEntry',
+ value: function _eachEntry(entry, i) {
+ var c = this._instanceConstructor;
+ var resolve$$1 = c.resolve;
+
+
+ if (resolve$$1 === resolve$1) {
+ var _then = getThen(entry);
+
+ if (_then === then && entry._state !== PENDING) {
+ this._settledAt(entry._state, i, entry._result);
+ } else if (typeof _then !== 'function') {
+ this._remaining--;
+ this._result[i] = entry;
+ } else if (c === Promise$2) {
+ var promise = new c(noop);
+ handleMaybeThenable(promise, entry, _then);
+ this._willSettleAt(promise, i);
+ } else {
+ this._willSettleAt(new c(function (resolve$$1) {
+ return resolve$$1(entry);
+ }), i);
+ }
+ } else {
+ this._willSettleAt(resolve$$1(entry), i);
+ }
+ }
+ }, {
+ key: '_settledAt',
+ value: function _settledAt(state, i, value) {
+ var promise = this.promise;
+
+
+ if (promise._state === PENDING) {
+ this._remaining--;
+
+ if (state === REJECTED) {
+ reject(promise, value);
+ } else {
+ this._result[i] = value;
+ }
+ }
+
+ if (this._remaining === 0) {
+ fulfill(promise, this._result);
+ }
+ }
+ }, {
+ key: '_willSettleAt',
+ value: function _willSettleAt(promise, i) {
+ var enumerator = this;
+
+ subscribe(promise, undefined, function (value) {
+ return enumerator._settledAt(FULFILLED, i, value);
+ }, function (reason) {
+ return enumerator._settledAt(REJECTED, i, reason);
+ });
+ }
+ }]);
+ return Enumerator;
+ }();
+
+ /**
+ `Promise.all` accepts an array of promises, and returns a new promise which
+ is fulfilled with an array of fulfillment values for the passed promises, or
+ rejected with the reason of the first passed promise to be rejected. It casts all
+ elements of the passed iterable to promises as it runs this algorithm.
+
+ Example:
+
+ ```javascript
+ let promise1 = resolve(1);
+ let promise2 = resolve(2);
+ let promise3 = resolve(3);
+ let promises = [ promise1, promise2, promise3 ];
+
+ Promise.all(promises).then(function(array){
+ // The array here would be [ 1, 2, 3 ];
+ });
+ ```
+
+ If any of the `promises` given to `all` are rejected, the first promise
+ that is rejected will be given as an argument to the returned promises's
+ rejection handler. For example:
+
+ Example:
+
+ ```javascript
+ let promise1 = resolve(1);
+ let promise2 = reject(new Error("2"));
+ let promise3 = reject(new Error("3"));
+ let promises = [ promise1, promise2, promise3 ];
+
+ Promise.all(promises).then(function(array){
+ // Code here never runs because there are rejected promises!
+ }, function(error) {
+ // error.message === "2"
+ });
+ ```
+
+ @method all
+ @static
+ @param {Array} entries array of promises
+ @param {String} label optional string for labeling the promise.
+ Useful for tooling.
+ @return {Promise} promise that is fulfilled when all `promises` have been
+ fulfilled, or rejected if any of them become rejected.
+ @static
+ */
+ function all(entries) {
+ return new Enumerator(this, entries).promise;
+ }
+
+ /**
+ `Promise.race` returns a new promise which is settled in the same way as the
+ first passed promise to settle.
+
+ Example:
+
+ ```javascript
+ let promise1 = new Promise(function(resolve, reject){
+ setTimeout(function(){
+ resolve('promise 1');
+ }, 200);
+ });
+
+ let promise2 = new Promise(function(resolve, reject){
+ setTimeout(function(){
+ resolve('promise 2');
+ }, 100);
+ });
+
+ Promise.race([promise1, promise2]).then(function(result){
+ // result === 'promise 2' because it was resolved before promise1
+ // was resolved.
+ });
+ ```
+
+ `Promise.race` is deterministic in that only the state of the first
+ settled promise matters. For example, even if other promises given to the
+ `promises` array argument are resolved, but the first settled promise has
+ become rejected before the other promises became fulfilled, the returned
+ promise will become rejected:
+
+ ```javascript
+ let promise1 = new Promise(function(resolve, reject){
+ setTimeout(function(){
+ resolve('promise 1');
+ }, 200);
+ });
+
+ let promise2 = new Promise(function(resolve, reject){
+ setTimeout(function(){
+ reject(new Error('promise 2'));
+ }, 100);
+ });
+
+ Promise.race([promise1, promise2]).then(function(result){
+ // Code here never runs
+ }, function(reason){
+ // reason.message === 'promise 2' because promise 2 became rejected before
+ // promise 1 became fulfilled
+ });
+ ```
+
+ An example real-world use case is implementing timeouts:
+
+ ```javascript
+ Promise.race([ajax('foo.json'), timeout(5000)])
+ ```
+
+ @method race
+ @static
+ @param {Array} promises array of promises to observe
+ Useful for tooling.
+ @return {Promise} a promise which settles in the same way as the first passed
+ promise to settle.
+ */
+ function race(entries) {
+ /*jshint validthis:true */
+ var Constructor = this;
+
+ if (!isArray(entries)) {
+ return new Constructor(function (_, reject) {
+ return reject(new TypeError('You must pass an array to race.'));
+ });
+ } else {
+ return new Constructor(function (resolve, reject) {
+ var length = entries.length;
+ for (var i = 0; i < length; i++) {
+ Constructor.resolve(entries[i]).then(resolve, reject);
+ }
+ });
+ }
+ }
+
+ /**
+ `Promise.reject` returns a promise rejected with the passed `reason`.
+ It is shorthand for the following:
+
+ ```javascript
+ let promise = new Promise(function(resolve, reject){
+ reject(new Error('WHOOPS'));
+ });
+
+ promise.then(function(value){
+ // Code here doesn't run because the promise is rejected!
+ }, function(reason){
+ // reason.message === 'WHOOPS'
+ });
+ ```
+
+ Instead of writing the above, your code now simply becomes the following:
+
+ ```javascript
+ let promise = Promise.reject(new Error('WHOOPS'));
+
+ promise.then(function(value){
+ // Code here doesn't run because the promise is rejected!
+ }, function(reason){
+ // reason.message === 'WHOOPS'
+ });
+ ```
+
+ @method reject
+ @static
+ @param {Any} reason value that the returned promise will be rejected with.
+ Useful for tooling.
+ @return {Promise} a promise rejected with the given `reason`.
+ */
+ function reject$1(reason) {
+ /*jshint validthis:true */
+ var Constructor = this;
+ var promise = new Constructor(noop);
+ reject(promise, reason);
+ return promise;
+ }
+
+ function needsResolver() {
+ throw new TypeError('You must pass a resolver function as the first argument to the promise constructor');
+ }
+
+ function needsNew() {
+ throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.");
+ }
+
+ /**
+ Promise objects represent the eventual result of an asynchronous operation. The
+ primary way of interacting with a promise is through its `then` method, which
+ registers callbacks to receive either a promise's eventual value or the reason
+ why the promise cannot be fulfilled.
+
+ Terminology
+ -----------
+
+ - `promise` is an object or function with a `then` method whose behavior conforms to this specification.
+ - `thenable` is an object or function that defines a `then` method.
+ - `value` is any legal JavaScript value (including undefined, a thenable, or a promise).
+ - `exception` is a value that is thrown using the throw statement.
+ - `reason` is a value that indicates why a promise was rejected.
+ - `settled` the final resting state of a promise, fulfilled or rejected.
+
+ A promise can be in one of three states: pending, fulfilled, or rejected.
+
+ Promises that are fulfilled have a fulfillment value and are in the fulfilled
+ state. Promises that are rejected have a rejection reason and are in the
+ rejected state. A fulfillment value is never a thenable.
+
+ Promises can also be said to *resolve* a value. If this value is also a
+ promise, then the original promise's settled state will match the value's
+ settled state. So a promise that *resolves* a promise that rejects will
+ itself reject, and a promise that *resolves* a promise that fulfills will
+ itself fulfill.
+
+
+ Basic Usage:
+ ------------
+
+ ```js
+ let promise = new Promise(function(resolve, reject) {
+ // on success
+ resolve(value);
+
+ // on failure
+ reject(reason);
+ });
+
+ promise.then(function(value) {
+ // on fulfillment
+ }, function(reason) {
+ // on rejection
+ });
+ ```
+
+ Advanced Usage:
+ ---------------
+
+ Promises shine when abstracting away asynchronous interactions such as
+ `XMLHttpRequest`s.
+
+ ```js
+ function getJSON(url) {
+ return new Promise(function(resolve, reject){
+ let xhr = new XMLHttpRequest();
+
+ xhr.open('GET', url);
+ xhr.onreadystatechange = handler;
+ xhr.responseType = 'json';
+ xhr.setRequestHeader('Accept', 'application/json');
+ xhr.send();
+
+ function handler() {
+ if (this.readyState === this.DONE) {
+ if (this.status === 200) {
+ resolve(this.response);
+ } else {
+ reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']'));
+ }
+ }
+ };
+ });
+ }
+
+ getJSON('/posts.json').then(function(json) {
+ // on fulfillment
+ }, function(reason) {
+ // on rejection
+ });
+ ```
+
+ Unlike callbacks, promises are great composable primitives.
+
+ ```js
+ Promise.all([
+ getJSON('/posts'),
+ getJSON('/comments')
+ ]).then(function(values){
+ values[0] // => postsJSON
+ values[1] // => commentsJSON
+
+ return values;
+ });
+ ```
+
+ @class Promise
+ @param {Function} resolver
+ Useful for tooling.
+ @constructor
+ */
+
+ var Promise$2 = function () {
+ function Promise(resolver) {
+ classCallCheck(this, Promise);
+
+ this[PROMISE_ID] = nextId();
+ this._result = this._state = undefined;
+ this._subscribers = [];
+
+ if (noop !== resolver) {
+ typeof resolver !== 'function' && needsResolver();
+ this instanceof Promise ? initializePromise(this, resolver) : needsNew();
+ }
+ }
+
+ /**
+ The primary way of interacting with a promise is through its `then` method,
+ which registers callbacks to receive either a promise's eventual value or the
+ reason why the promise cannot be fulfilled.
+ ```js
+ findUser().then(function(user){
+ // user is available
+ }, function(reason){
+ // user is unavailable, and you are given the reason why
+ });
+ ```
+ Chaining
+ --------
+ The return value of `then` is itself a promise. This second, 'downstream'
+ promise is resolved with the return value of the first promise's fulfillment
+ or rejection handler, or rejected if the handler throws an exception.
+ ```js
+ findUser().then(function (user) {
+ return user.name;
+ }, function (reason) {
+ return 'default name';
+ }).then(function (userName) {
+ // If `findUser` fulfilled, `userName` will be the user's name, otherwise it
+ // will be `'default name'`
+ });
+ findUser().then(function (user) {
+ throw new Error('Found user, but still unhappy');
+ }, function (reason) {
+ throw new Error('`findUser` rejected and we're unhappy');
+ }).then(function (value) {
+ // never reached
+ }, function (reason) {
+ // if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'.
+ // If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'.
+ });
+ ```
+ If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream.
+ ```js
+ findUser().then(function (user) {
+ throw new PedagogicalException('Upstream error');
+ }).then(function (value) {
+ // never reached
+ }).then(function (value) {
+ // never reached
+ }, function (reason) {
+ // The `PedgagocialException` is propagated all the way down to here
+ });
+ ```
+ Assimilation
+ ------------
+ Sometimes the value you want to propagate to a downstream promise can only be
+ retrieved asynchronously. This can be achieved by returning a promise in the
+ fulfillment or rejection handler. The downstream promise will then be pending
+ until the returned promise is settled. This is called *assimilation*.
+ ```js
+ findUser().then(function (user) {
+ return findCommentsByAuthor(user);
+ }).then(function (comments) {
+ // The user's comments are now available
+ });
+ ```
+ If the assimliated promise rejects, then the downstream promise will also reject.
+ ```js
+ findUser().then(function (user) {
+ return findCommentsByAuthor(user);
+ }).then(function (comments) {
+ // If `findCommentsByAuthor` fulfills, we'll have the value here
+ }, function (reason) {
+ // If `findCommentsByAuthor` rejects, we'll have the reason here
+ });
+ ```
+ Simple Example
+ --------------
+ Synchronous Example
+ ```javascript
+ let result;
+ try {
+ result = findResult();
+ // success
+ } catch(reason) {
+ // failure
+ }
+ ```
+ Errback Example
+ ```js
+ findResult(function(result, err){
+ if (err) {
+ // failure
+ } else {
+ // success
+ }
+ });
+ ```
+ Promise Example;
+ ```javascript
+ findResult().then(function(result){
+ // success
+ }, function(reason){
+ // failure
+ });
+ ```
+ Advanced Example
+ --------------
+ Synchronous Example
+ ```javascript
+ let author, books;
+ try {
+ author = findAuthor();
+ books = findBooksByAuthor(author);
+ // success
+ } catch(reason) {
+ // failure
+ }
+ ```
+ Errback Example
+ ```js
+ function foundBooks(books) {
+ }
+ function failure(reason) {
+ }
+ findAuthor(function(author, err){
+ if (err) {
+ failure(err);
+ // failure
+ } else {
+ try {
+ findBoooksByAuthor(author, function(books, err) {
+ if (err) {
+ failure(err);
+ } else {
+ try {
+ foundBooks(books);
+ } catch(reason) {
+ failure(reason);
+ }
+ }
+ });
+ } catch(error) {
+ failure(err);
+ }
+ // success
+ }
+ });
+ ```
+ Promise Example;
+ ```javascript
+ findAuthor().
+ then(findBooksByAuthor).
+ then(function(books){
+ // found books
+ }).catch(function(reason){
+ // something went wrong
+ });
+ ```
+ @method then
+ @param {Function} onFulfilled
+ @param {Function} onRejected
+ Useful for tooling.
+ @return {Promise}
+ */
+
+ /**
+ `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same
+ as the catch block of a try/catch statement.
+ ```js
+ function findAuthor(){
+ throw new Error('couldn't find that author');
+ }
+ // synchronous
+ try {
+ findAuthor();
+ } catch(reason) {
+ // something went wrong
+ }
+ // async with promises
+ findAuthor().catch(function(reason){
+ // something went wrong
+ });
+ ```
+ @method catch
+ @param {Function} onRejection
+ Useful for tooling.
+ @return {Promise}
+ */
+
+
+ createClass(Promise, [{
+ key: 'catch',
+ value: function _catch(onRejection) {
+ return this.then(null, onRejection);
+ }
+
+ /**
+ `finally` will be invoked regardless of the promise's fate just as native
+ try/catch/finally behaves
+
+ Synchronous example:
+
+ ```js
+ findAuthor() {
+ if (Math.random() > 0.5) {
+ throw new Error();
+ }
+ return new Author();
+ }
+
+ try {
+ return findAuthor(); // succeed or fail
+ } catch(error) {
+ return findOtherAuther();
+ } finally {
+ // always runs
+ // doesn't affect the return value
+ }
+ ```
+
+ Asynchronous example:
+
+ ```js
+ findAuthor().catch(function(reason){
+ return findOtherAuther();
+ }).finally(function(){
+ // author was either found, or not
+ });
+ ```
+
+ @method finally
+ @param {Function} callback
+ @return {Promise}
+ */
+
+ }, {
+ key: 'finally',
+ value: function _finally(callback) {
+ var promise = this;
+ var constructor = promise.constructor;
+
+ if (isFunction(callback)) {
+ return promise.then(function (value) {
+ return constructor.resolve(callback()).then(function () {
+ return value;
+ });
+ }, function (reason) {
+ return constructor.resolve(callback()).then(function () {
+ throw reason;
+ });
+ });
+ }
+
+ return promise.then(callback, callback);
+ }
+ }]);
+ return Promise;
+ }();
+
+ Promise$2.prototype.then = then;
+ Promise$2.all = all;
+ Promise$2.race = race;
+ Promise$2.resolve = resolve$1;
+ Promise$2.reject = reject$1;
+ Promise$2._setScheduler = setScheduler;
+ Promise$2._setAsap = setAsap;
+ Promise$2._asap = asap;
+
+ /*global self*/
+ function polyfill() {
+ var local = void 0;
+
+ if (typeof global !== 'undefined') {
+ local = global;
+ } else if (typeof self !== 'undefined') {
+ local = self;
+ } else {
+ try {
+ local = Function('return this')();
+ } catch (e) {
+ throw new Error('polyfill failed because global object is unavailable in this environment');
+ }
+ }
+
+ var P = local.Promise;
+
+ if (P) {
+ var promiseToString = null;
+ try {
+ promiseToString = Object.prototype.toString.call(P.resolve());
+ } catch (e) {
+ // silently ignored
+ }
+
+ if (promiseToString === '[object Promise]' && !P.cast) {
+ return;
+ }
+ }
+
+ local.Promise = Promise$2;
+ }
+
+ // Strange compat..
+ Promise$2.polyfill = polyfill;
+ Promise$2.Promise = Promise$2;
+
+ var Promise$1 = typeof Promise !== "undefined" ? Promise : Promise$2;
+
// Register logging callbacks
function registerLoggingCallbacks(obj) {
var i,
}
function runLoggingCallbacks(key, args) {
- var i, l, callbacks;
-
- callbacks = config.callbacks[key];
- for (i = 0, l = callbacks.length; i < l; i++) {
- callbacks[i](args);
+ var callbacks = config.callbacks[key];
+
+ // Handling 'log' callbacks separately. Unlike the other callbacks,
+ // the log callback is not controlled by the processing queue,
+ // but rather used by asserts. Hence to promisfy the 'log' callback
+ // would mean promisfying each step of a test
+ if (key === "log") {
+ callbacks.map(function (callback) {
+ return callback(args);
+ });
+ return;
}
+
+ // ensure that each callback is executed serially
+ return callbacks.reduce(function (promiseChain, callback) {
+ return promiseChain.then(function () {
+ return Promise$1.resolve(callback(args));
+ });
+ }, Promise$1.resolve([]));
}
// Doesn't support IE9, it will return undefined on these browsers
function advance() {
advanceTaskQueue();
- if (!taskQueue.length) {
+ if (!taskQueue.length && !config.blocking && !config.current) {
advanceTestQueue();
}
}
/**
- * Advances the taskQueue to the next task if it is ready and not empty.
+ * Advances the taskQueue with an increased depth
*/
function advanceTaskQueue() {
var start = now();
config.depth = (config.depth || 0) + 1;
- while (taskQueue.length && !config.blocking) {
+ processTaskQueue(start);
+
+ config.depth--;
+ }
+
+ /**
+ * Process the first task on the taskQueue as a promise.
+ * Each task is a function returned by https://github.com/qunitjs/qunit/blob/master/src/test.js#L381
+ */
+ function processTaskQueue(start) {
+ if (taskQueue.length && !config.blocking) {
var elapsedTime = now() - start;
if (!defined.setTimeout || config.updateRate <= 0 || elapsedTime < config.updateRate) {
var task = taskQueue.shift();
- task();
+ Promise$1.resolve(task()).then(function () {
+ if (!taskQueue.length) {
+ advance();
+ } else {
+ processTaskQueue(start);
+ }
+ });
} else {
- setTimeout(advance);
- break;
+ setTimeout$1(advance);
}
}
-
- config.depth--;
}
/**
failed: config.stats.bad,
total: config.stats.all,
runtime: runtime
- });
+ }).then(function () {
- // Clear own storage items if all tests passed
- if (storage && config.stats.bad === 0) {
- for (var i = storage.length - 1; i >= 0; i--) {
- var key = storage.key(i);
+ // Clear own storage items if all tests passed
+ if (storage && config.stats.bad === 0) {
+ for (var i = storage.length - 1; i >= 0; i--) {
+ var key = storage.key(i);
- if (key.indexOf("qunit-test-") === 0) {
- storage.removeItem(key);
+ if (key.indexOf("qunit-test-") === 0) {
+ storage.removeItem(key);
+ }
}
}
- }
+ });
}
var ProcessingQueue = {
key: "start",
value: function start(recordTime) {
if (recordTime) {
- this._startTime = Date.now();
+ this._startTime = performanceNow();
+ if (performance) {
+ performance.mark("qunit_test_start");
+ }
}
return {
key: "end",
value: function end(recordTime) {
if (recordTime) {
- this._endTime = Date.now();
+ this._endTime = performanceNow();
+ if (performance) {
+ performance.mark("qunit_test_end");
+
+ var testName = this.fullName.join(" – ");
+
+ measure("QUnit Test: " + testName, "qunit_test_start", "qunit_test_end");
+ }
}
return extend(this.start(), {
module = module.parentModule;
}
- return modules;
+ // The above push modules from the child to the parent
+ // return a reversed order with the top being the top most parent module
+ return modules.reverse();
}
Test.prototype = {
before: function before() {
- var i,
- startModule,
- module = this.module,
+ var _this = this;
+
+ var module = this.module,
notStartedModules = getNotStartedModules(module);
- for (i = notStartedModules.length - 1; i >= 0; i--) {
- startModule = notStartedModules[i];
- startModule.stats = { all: 0, bad: 0, started: now() };
- emit("suiteStart", startModule.suiteReport.start(true));
- runLoggingCallbacks("moduleStart", {
- name: startModule.name,
- tests: startModule.tests
+ // ensure the callbacks are executed serially for each module
+ var callbackPromises = notStartedModules.reduce(function (promiseChain, startModule) {
+ return promiseChain.then(function () {
+ startModule.stats = { all: 0, bad: 0, started: now() };
+ emit("suiteStart", startModule.suiteReport.start(true));
+ return runLoggingCallbacks("moduleStart", {
+ name: startModule.name,
+ tests: startModule.tests
+ });
});
- }
+ }, Promise$1.resolve([]));
- config.current = this;
-
- this.testEnvironment = extend({}, module.testEnvironment);
+ return callbackPromises.then(function () {
+ config.current = _this;
- this.started = now();
- emit("testStart", this.testReport.start(true));
- runLoggingCallbacks("testStart", {
- name: this.testName,
- module: module.name,
- testId: this.testId,
- previousFailure: this.previousFailure
+ _this.testEnvironment = extend({}, module.testEnvironment);
+
+ _this.started = now();
+ emit("testStart", _this.testReport.start(true));
+ return runLoggingCallbacks("testStart", {
+ name: _this.testName,
+ module: module.name,
+ testId: _this.testId,
+ previousFailure: _this.previousFailure
+ }).then(function () {
+ if (!config.pollution) {
+ saveGlobal();
+ }
+ });
});
-
- if (!config.pollution) {
- saveGlobal();
- }
},
run: function run() {
},
queueHook: function queueHook(hook, hookName, hookOwner) {
- var _this = this;
+ var _this2 = this;
var callHook = function callHook() {
- var promise = hook.call(_this.testEnvironment, _this.assert);
- _this.resolvePromise(promise, hookName);
+ var promise = hook.call(_this2.testEnvironment, _this2.assert);
+ _this2.resolvePromise(promise, hookName);
};
var runHook = function runHook() {
return;
}
- _this.preserveEnvironment = true;
+ _this2.preserveEnvironment = true;
}
// The 'after' hook should only execute when there are not tests left and
return;
}
- config.current = _this;
+ config.current = _this2;
if (config.notrycatch) {
callHook();
return;
try {
callHook();
} catch (error) {
- _this.pushFailure(hookName + " failed on " + _this.testName + ": " + (error.message || error), extractStacktrace(error, 0));
+ _this2.pushFailure(hookName + " failed on " + _this2.testName + ": " + (error.message || error), extractStacktrace(error, 0));
}
};
emit("testEnd", this.testReport.end(true));
this.testReport.slimAssertions();
- runLoggingCallbacks("testDone", {
+ return runLoggingCallbacks("testDone", {
name: testName,
module: moduleName,
skipped: skipped,
// Source of Test
source: this.stack
- });
-
- if (module.testsRun === numberOfTests(module)) {
- logSuiteEnd(module);
+ }).then(function () {
+ if (module.testsRun === numberOfTests(module)) {
+ var completedModules = [module];
+
+ // Check if the parent modules, iteratively, are done. If that the case,
+ // we emit the `suiteEnd` event and trigger `moduleDone` callback.
+ var parent = module.parentModule;
+ while (parent && parent.testsRun === numberOfTests(parent)) {
+ completedModules.push(parent);
+ parent = parent.parentModule;
+ }
- // Check if the parent modules, iteratively, are done. If that the case,
- // we emit the `suiteEnd` event and trigger `moduleDone` callback.
- var parent = module.parentModule;
- while (parent && parent.testsRun === numberOfTests(parent)) {
- logSuiteEnd(parent);
- parent = parent.parentModule;
+ return completedModules.reduce(function (promiseChain, completedModule) {
+ return promiseChain.then(function () {
+ return logSuiteEnd(completedModule);
+ });
+ }, Promise$1.resolve([]));
}
- }
-
- config.current = undefined;
+ }).then(function () {
+ config.current = undefined;
+ });
function logSuiteEnd(module) {
module.hooks = {};
emit("suiteEnd", module.suiteReport.end(true));
- runLoggingCallbacks("moduleDone", {
+ return runLoggingCallbacks("moduleDone", {
name: module.name,
tests: module.tests,
failed: module.stats.bad,
function runTest() {
return [function () {
- test.before();
+ return test.before();
}].concat(toConsumableArray(test.hooks("before")), [function () {
test.preserveTestEnvironment();
}], toConsumableArray(test.hooks("beforeEach")), [function () {
}], toConsumableArray(test.hooks("afterEach").reverse()), toConsumableArray(test.hooks("after").reverse()), [function () {
test.after();
}, function () {
- test.finish();
+ return test.finish();
}]);
}
if (typeof timeoutDuration === "number" && timeoutDuration > 0) {
clearTimeout(config.timeout);
- config.timeout = setTimeout(function () {
+ config.timeout = setTimeout$1(function () {
pushFailure("Test took longer than " + timeoutDuration + "ms; test timed out.", sourceFromStacktrace(2));
internalRecover(test);
}, timeoutDuration);
if (config.timeout) {
clearTimeout(config.timeout);
}
- config.timeout = setTimeout(function () {
+ config.timeout = setTimeout$1(function () {
if (test.semaphore > 0) {
return;
}
}
}
- /**
- * Returns a function that proxies to the given method name on the globals
- * console object. The proxy will also detect if the console doesn't exist and
- * will appropriately no-op. This allows support for IE9, which doesn't have a
- * console if the developer tools are not open.
- */
- function consoleProxy(method) {
- return function () {
- if (console) {
- console[method].apply(console, arguments);
- }
- };
- }
-
- var Logger = {
- warn: consoleProxy("warn")
- };
-
var Assert = function () {
function Assert(testContext) {
classCallCheck(this, Assert);
// We don't want to validate thrown error
if (!expected) {
result = true;
- expected = null;
// Expected is a regexp
} else if (expectedType === "regexp") {
result = expected.test(errorString(actual));
+ // Log the string form of the regexp
+ expected = String(expected);
+
// Expected is a constructor, maybe an Error constructor
} else if (expectedType === "function" && actual instanceof expected) {
result = true;
} else if (expectedType === "object") {
result = actual instanceof expected.constructor && actual.name === expected.name && actual.message === expected.message;
+ // Log the string form of the Error object
+ expected = errorString(expected);
+
// Expected is a validation function which returns true if validation passed
} else if (expectedType === "function" && expected.call({}, actual) === true) {
expected = null;
currentTest.assert.pushResult({
result: result,
- actual: actual,
+
+ // undefined if it didn't throw
+ actual: actual && errorString(actual),
expected: expected,
message: message
});
// We don't want to validate
if (expected === undefined) {
result = true;
- expected = actual;
// Expected is a regexp
} else if (expectedType === "regexp") {
result = expected.test(errorString(actual));
+ // Log the string form of the regexp
+ expected = String(expected);
+
// Expected is a constructor, maybe an Error constructor
} else if (expectedType === "function" && actual instanceof expected) {
result = true;
} else if (expectedType === "object") {
result = actual instanceof expected.constructor && actual.name === expected.name && actual.message === expected.message;
+ // Log the string form of the Error object
+ expected = errorString(expected);
+
// Expected is a validation function which returns true if validation passed
} else {
if (expectedType === "function") {
currentTest.assert.pushResult({
result: result,
- actual: actual,
+
+ // leave rejection value of undefined as-is
+ actual: actual && errorString(actual),
expected: expected,
message: message
});
/**
* Converts an error into a simple string for comparisons.
*
- * @param {Error} error
+ * @param {Error|Object} error
* @return {String}
*/
function errorString(error) {
var resultErrorString = error.toString();
+ // If the error wasn't a subclass of Error but something like
+ // an object literal with name and message properties...
if (resultErrorString.substring(0, 7) === "[object") {
var name = error.name ? error.name.toString() : "Error";
var message = error.message ? error.message.toString() : "";
if (defined.document) {
// QUnit may be defined when it is preconfigured but then only QUnit and QUnit.config may be defined.
- if (window.QUnit && window.QUnit.version) {
+ if (window$1.QUnit && window$1.QUnit.version) {
throw new Error("QUnit has already been defined.");
}
- window.QUnit = QUnit;
+ window$1.QUnit = QUnit;
}
// For nodejs
if (config.current.ignoreGlobalErrors) {
return true;
}
- pushFailure.apply(undefined, [error.message, error.fileName + ":" + error.lineNumber].concat(args));
+ pushFailure.apply(undefined, [error.message, error.stacktrace || error.fileName + ":" + error.lineNumber].concat(args));
} else {
test("global failure", extend(function () {
- pushFailure.apply(undefined, [error.message, error.fileName + ":" + error.lineNumber].concat(args));
+ pushFailure.apply(undefined, [error.message, error.stacktrace || error.fileName + ":" + error.lineNumber].concat(args));
}, { validTest: true }));
}
var runStarted = false;
// Figure out if we're running the tests from a server or not
- QUnit.isLocal = !(defined.document && window.location.protocol !== "file:");
+ QUnit.isLocal = !(defined.document && window$1.location.protocol !== "file:");
// Expose the current QUnit version
- QUnit.version = "2.6.2";
+ QUnit.version = "2.9.1";
extend(QUnit, {
on: on,
// Add a slight delay to allow definition of more modules and tests.
if (defined.setTimeout) {
- setTimeout(function () {
+ setTimeout$1(function () {
begin();
});
} else {
}
}
+ function unblockAndAdvanceQueue() {
+ config.blocking = false;
+ ProcessingQueue.advance();
+ }
+
function begin() {
var i,
l,
runLoggingCallbacks("begin", {
totalTests: Test.count,
modules: modulesLog
- });
+ }).then(unblockAndAdvanceQueue);
+ } else {
+ unblockAndAdvanceQueue();
}
-
- config.blocking = false;
- ProcessingQueue.advance();
}
exportQUnit(QUnit);
(function () {
- if (typeof window === "undefined" || typeof document === "undefined") {
+ if (typeof window$1 === "undefined" || typeof document$1 === "undefined") {
return;
}
return;
}
- var fixture = document.getElementById("qunit-fixture");
+ var fixture = document$1.getElementById("qunit-fixture");
if (fixture) {
config.fixture = fixture.cloneNode(true);
}
return;
}
- var fixture = document.getElementById("qunit-fixture");
+ var fixture = document$1.getElementById("qunit-fixture");
var resetFixtureType = _typeof(config.fixture);
if (resetFixtureType === "string") {
// support user defined values for `config.fixture`
- var newFixture = document.createElement("div");
+ var newFixture = document$1.createElement("div");
newFixture.setAttribute("id", "qunit-fixture");
newFixture.innerHTML = config.fixture;
fixture.parentNode.replaceChild(newFixture, fixture);
(function () {
// Only interact with URLs via window.location
- var location = typeof window !== "undefined" && window.location;
+ var location = typeof window$1 !== "undefined" && window$1.location;
if (!location) {
return;
}
(function () {
// Don't load the HTML Reporter on non-browser environments
- if (typeof window === "undefined" || !window.document) {
+ if (typeof window$1 === "undefined" || !window$1.document) {
return;
}
var config = QUnit.config,
- document$$1 = window.document,
+ hiddenTests = [],
+ document = window$1.document,
collapseNext = false,
hasOwn = Object.prototype.hasOwnProperty,
unfilteredUrl = setUrl({ filter: undefined, module: undefined,
}
function id(name) {
- return document$$1.getElementById && document$$1.getElementById(name);
+ return document.getElementById && document.getElementById(name);
}
function abortTests() {
updatedUrl = setUrl(params);
// Check if we can apply the change without a page refresh
- if ("hidepassed" === field.name && "replaceState" in window.history) {
+ if ("hidepassed" === field.name && "replaceState" in window$1.history) {
QUnit.urlParams[field.name] = value;
config[field.name] = value || false;
tests = id("qunit-tests");
if (tests) {
- toggleClass(tests, "hidepass", value || false);
+ var length = tests.children.length;
+ var children = tests.children;
+
+ if (field.checked) {
+ for (var i = 0; i < length; i++) {
+ var test = children[i];
+
+ if (test && test.className.indexOf("pass") > -1) {
+ hiddenTests.push(test);
+ }
+ }
+
+ var _iteratorNormalCompletion = true;
+ var _didIteratorError = false;
+ var _iteratorError = undefined;
+
+ try {
+ for (var _iterator = hiddenTests[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
+ var hiddenTest = _step.value;
+
+ tests.removeChild(hiddenTest);
+ }
+ } catch (err) {
+ _didIteratorError = true;
+ _iteratorError = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion && _iterator.return) {
+ _iterator.return();
+ }
+ } finally {
+ if (_didIteratorError) {
+ throw _iteratorError;
+ }
+ }
+ }
+ } else {
+ while ((test = hiddenTests.pop()) != null) {
+ tests.appendChild(test);
+ }
+ }
}
- window.history.replaceState(null, "", updatedUrl);
+ window$1.history.replaceState(null, "", updatedUrl);
} else {
- window.location = updatedUrl;
+ window$1.location = updatedUrl;
}
}
arrValue,
i,
querystring = "?",
- location = window.location;
+ location = window$1.location;
params = QUnit.extend(QUnit.extend({}, QUnit.urlParams), params);
}
}
- window.location = setUrl({
+ window$1.location = setUrl({
filter: filter === "" ? undefined : filter,
moduleId: selectedModules.length === 0 ? undefined : selectedModules,
}
function toolbarUrlConfigContainer() {
- var urlConfigContainer = document$$1.createElement("span");
+ var urlConfigContainer = document.createElement("span");
urlConfigContainer.innerHTML = getUrlConfigHtml();
addClass(urlConfigContainer, "qunit-url-config");
}
function abortTestsButton() {
- var button = document$$1.createElement("button");
+ var button = document.createElement("button");
button.id = "qunit-abort-tests-button";
button.innerHTML = "Abort";
addEvent(button, "click", abortTests);
}
function toolbarLooseFilter() {
- var filter = document$$1.createElement("form"),
- label = document$$1.createElement("label"),
- input = document$$1.createElement("input"),
- button = document$$1.createElement("button");
+ var filter = document.createElement("form"),
+ label = document.createElement("label"),
+ input = document.createElement("input"),
+ button = document.createElement("button");
addClass(filter, "qunit-filter");
label.appendChild(input);
filter.appendChild(label);
- filter.appendChild(document$$1.createTextNode(" "));
+ filter.appendChild(document.createTextNode(" "));
filter.appendChild(button);
addEvent(filter, "submit", interceptNavigation);
var allCheckbox,
commit,
reset,
- moduleFilter = document$$1.createElement("form"),
- label = document$$1.createElement("label"),
- moduleSearch = document$$1.createElement("input"),
- dropDown = document$$1.createElement("div"),
- actions = document$$1.createElement("span"),
- dropDownList = document$$1.createElement("ul"),
+ moduleFilter = document.createElement("form"),
+ label = document.createElement("label"),
+ moduleSearch = document.createElement("input"),
+ dropDown = document.createElement("div"),
+ actions = document.createElement("span"),
+ dropDownList = document.createElement("ul"),
dirty = false;
moduleSearch.id = "qunit-modulefilter-search";
label.appendChild(moduleSearch);
actions.id = "qunit-modulefilter-actions";
- actions.innerHTML = "<button style='display:none'>Apply</button>" + "<button type='reset' style='display:none'>Reset</button>" + "<label class='clickable" + (config.moduleId.length ? "" : " checked") + "'><input type='checkbox'" + (config.moduleId.length ? "" : " checked='checked'") + ">All modules</label>";
+ actions.innerHTML = "<button style='display:none'>Apply</button>" + "<button type='reset' style='display:none'>Reset</button>" + "<label class='clickable" + (config.moduleId.length ? "" : " checked") + "'><input type='checkbox'" + (config.moduleId.length ? "" : " checked='checked'") + " />All modules</label>";
allCheckbox = actions.lastChild.firstChild;
commit = actions.firstChild;
reset = commit.nextSibling;
addEvent(moduleFilter, "reset", function () {
// Let the reset happen, then update styles
- window.setTimeout(selectionChange);
+ window$1.setTimeout(selectionChange);
});
// Enables show/hide for the dropdown
}
dropDown.style.display = "block";
- addEvent(document$$1, "click", hideHandler);
- addEvent(document$$1, "keydown", hideHandler);
+ addEvent(document, "click", hideHandler);
+ addEvent(document, "keydown", hideHandler);
// Hide on Escape keydown or outside-container click
function hideHandler(e) {
moduleSearch.focus();
}
dropDown.style.display = "none";
- removeEvent(document$$1, "click", hideHandler);
- removeEvent(document$$1, "keydown", hideHandler);
+ removeEvent(document, "click", hideHandler);
+ removeEvent(document, "keydown", hideHandler);
moduleSearch.value = "";
searchInput();
}
toolbar.appendChild(toolbarUrlConfigContainer());
toolbar.appendChild(toolbarModuleFilter());
toolbar.appendChild(toolbarLooseFilter());
- toolbar.appendChild(document$$1.createElement("div")).className = "clearfix";
+ toolbar.appendChild(document.createElement("div")).className = "clearfix";
}
}
if (tests) {
tests.innerHTML = "";
- result = document$$1.createElement("p");
+ result = document.createElement("p");
result.id = "qunit-testresult";
result.className = "result";
tests.parentNode.insertBefore(result, tests);
if (userAgent) {
userAgent.innerHTML = "";
- userAgent.appendChild(document$$1.createTextNode("QUnit " + QUnit.version + "; " + navigator.userAgent));
+ userAgent.appendChild(document.createTextNode("QUnit " + QUnit.version + "; " + navigator.userAgent));
}
}
var qunit = id("qunit");
if (qunit) {
- qunit.innerHTML = "<h1 id='qunit-header'>" + escapeText(document$$1.title) + "</h1>" + "<h2 id='qunit-banner'></h2>" + "<div id='qunit-testrunner-toolbar'></div>" + appendFilteredTest() + "<h2 id='qunit-userAgent'></h2>" + "<ol id='qunit-tests'></ol>";
+ qunit.innerHTML = "<h1 id='qunit-header'>" + escapeText(document.title) + "</h1>" + "<h2 id='qunit-banner'></h2>" + "<div id='qunit-testrunner-toolbar'></div>" + appendFilteredTest() + "<h2 id='qunit-userAgent'></h2>" + "<ol id='qunit-tests'></ol>";
}
appendHeader();
appendToolbar();
}
- function appendTestsList(modules) {
- var i, l, x, z, test, moduleObj;
-
- for (i = 0, l = modules.length; i < l; i++) {
- moduleObj = modules[i];
-
- for (x = 0, z = moduleObj.tests.length; x < z; x++) {
- test = moduleObj.tests[x];
-
- appendTest(test.name, test.testId, moduleObj.name);
- }
- }
- }
-
function appendTest(name, testId, moduleName) {
var title,
rerunTrigger,
return;
}
- title = document$$1.createElement("strong");
+ title = document.createElement("strong");
title.innerHTML = getNameHtml(name, moduleName);
- rerunTrigger = document$$1.createElement("a");
+ rerunTrigger = document.createElement("a");
rerunTrigger.innerHTML = "Rerun";
rerunTrigger.href = setUrl({ testId: testId });
- testBlock = document$$1.createElement("li");
+ testBlock = document.createElement("li");
testBlock.appendChild(title);
testBlock.appendChild(rerunTrigger);
testBlock.id = "qunit-test-output-" + testId;
- assertList = document$$1.createElement("ol");
+ assertList = document.createElement("ol");
assertList.className = "qunit-assert-list";
testBlock.appendChild(assertList);
// HTML Reporter initialization and load
QUnit.begin(function (details) {
- var i, moduleObj, tests;
+ var i, moduleObj;
// Sort modules by name for the picker
for (i = 0; i < details.modules.length; i++) {
// Initialize QUnit elements
appendInterface();
- appendTestsList(details.modules);
- tests = id("qunit-tests");
- if (tests && config.hidepassed) {
- addClass(tests, "hidepass");
- }
});
QUnit.done(function (details) {
if (test.className === "" || test.className === "running") {
test.className = "aborted";
assertList = test.getElementsByTagName("ol")[0];
- assertLi = document$$1.createElement("li");
+ assertLi = document.createElement("li");
assertLi.className = "fail";
assertLi.innerHTML = "Test aborted.";
assertList.appendChild(assertLi);
id("qunit-testresult-display").innerHTML = html;
}
- if (config.altertitle && document$$1.title) {
+ if (config.altertitle && document.title) {
// Show ✖ for good, ✔ for bad suite result in title
// use escape sequences in case file gets loaded with non-utf-8
// charset
- document$$1.title = [stats.failedTests ? "\u2716" : "\u2714", document$$1.title.replace(/^[\u2714\u2716] /i, "")].join(" ");
+ document.title = [stats.failedTests ? "\u2716" : "\u2714", document.title.replace(/^[\u2714\u2716] /i, "")].join(" ");
}
// Scroll back to top to show results
- if (config.scrolltop && window.scrollTo) {
- window.scrollTo(0, 0);
+ if (config.scrolltop && window$1.scrollTo) {
+ window$1.scrollTo(0, 0);
}
});
}
QUnit.testStart(function (details) {
- var running, testBlock, bad;
+ var running, bad;
- testBlock = id("qunit-test-output-" + details.testId);
- if (testBlock) {
- testBlock.className = "running";
- } else {
-
- // Report later registered tests
- appendTest(details.name, details.testId, details.module);
- }
+ appendTest(details.name, details.testId, details.module);
running = id("qunit-testresult-display");
+
if (running) {
+ addClass(running, "running");
+
bad = QUnit.config.reorder && details.previousFailure;
running.innerHTML = [bad ? "Rerunning previously failed test: <br />" : "Running: <br />", getNameHtml(details.name, details.module)].join("");
assertList = testItem.getElementsByTagName("ol")[0];
- assertLi = document$$1.createElement("li");
+ assertLi = document.createElement("li");
assertLi.className = details.result ? "pass" : "fail";
assertLi.innerHTML = message;
assertList.appendChild(assertLi);
time,
testItem,
assertList,
+ status,
good,
bad,
testCounts,
testItem = id("qunit-test-output-" + details.testId);
+ removeClass(testItem, "running");
+
+ if (details.failed > 0) {
+ status = "failed";
+ } else if (details.todo) {
+ status = "todo";
+ } else {
+ status = details.skipped ? "skipped" : "passed";
+ }
+
assertList = testItem.getElementsByTagName("ol")[0];
good = details.passed;
stats.skippedTests++;
testItem.className = "skipped";
- skipped = document$$1.createElement("em");
+ skipped = document.createElement("em");
skipped.className = "qunit-skipped-label";
skipped.innerHTML = "skipped";
testItem.insertBefore(skipped, testTitle);
testItem.className = testPassed ? "pass" : "fail";
if (details.todo) {
- var todoLabel = document$$1.createElement("em");
+ var todoLabel = document.createElement("em");
todoLabel.className = "qunit-todo-label";
todoLabel.innerHTML = "todo";
testItem.className += " todo";
testItem.insertBefore(todoLabel, testTitle);
}
- time = document$$1.createElement("span");
+ time = document.createElement("span");
time.className = "runtime";
time.innerHTML = details.runtime + " ms";
testItem.insertBefore(time, assertList);
// Show the source of the test when showing assertions
if (details.source) {
- sourceName = document$$1.createElement("p");
- sourceName.innerHTML = "<strong>Source: </strong>" + details.source;
+ sourceName = document.createElement("p");
+ sourceName.innerHTML = "<strong>Source: </strong>" + escapeText(details.source);
addClass(sourceName, "qunit-source");
if (testPassed) {
addClass(sourceName, "qunit-collapsed");
});
testItem.appendChild(sourceName);
}
+
+ if (config.hidepassed && status === "passed") {
+
+ // use removeChild instead of remove because of support
+ hiddenTests.push(testItem);
+
+ tests.removeChild(testItem);
+ }
});
// Avoid readyState issue with phantomjs
// Ref: #818
var notPhantom = function (p) {
return !(p && p.version && p.version.major > 0);
- }(window.phantom);
+ }(window$1.phantom);
- if (notPhantom && document$$1.readyState === "complete") {
+ if (notPhantom && document.readyState === "complete") {
QUnit.load();
} else {
- addEvent(window, "load", QUnit.load);
+ addEvent(window$1, "load", QUnit.load);
}
// Wrap window.onerror. We will call the original window.onerror to see if
// the existing handler fully handles the error; if not, we will call the
// QUnit.onError function.
- var originalWindowOnError = window.onerror;
+ var originalWindowOnError = window$1.onerror;
// Cover uncaught exceptions
// Returning true will suppress the default browser handler,
// returning false will let it run.
- window.onerror = function (message, fileName, lineNumber) {
+ window$1.onerror = function (message, fileName, lineNumber, columnNumber, errorObj) {
var ret = false;
if (originalWindowOnError) {
- for (var _len = arguments.length, args = Array(_len > 3 ? _len - 3 : 0), _key = 3; _key < _len; _key++) {
- args[_key - 3] = arguments[_key];
+ for (var _len = arguments.length, args = Array(_len > 5 ? _len - 5 : 0), _key = 5; _key < _len; _key++) {
+ args[_key - 5] = arguments[_key];
}
- ret = originalWindowOnError.call.apply(originalWindowOnError, [this, message, fileName, lineNumber].concat(args));
+ ret = originalWindowOnError.call.apply(originalWindowOnError, [this, message, fileName, lineNumber, columnNumber, errorObj].concat(args));
}
// Treat return value as window.onerror itself does,
lineNumber: lineNumber
};
+ // According to
+ // https://blog.sentry.io/2016/01/04/client-javascript-reporting-window-onerror,
+ // most modern browsers support an errorObj argument; use that to
+ // get a full stack trace if it's available.
+ if (errorObj && errorObj.stack) {
+ error.stacktrace = extractStacktrace(errorObj, 0);
+ }
+
ret = QUnit.onError(error);
}
};
// Listen for unhandled rejections, and call QUnit.onUnhandledRejection
- window.addEventListener("unhandledrejection", function (event) {
+ window$1.addEventListener("unhandledrejection", function (event) {
QUnit.onUnhandledRejection(event.reason);
});
})();
}
// Cancel selection
- } ).mousedown( function () {
+ } ).on( 'mousedown', function () {
if ( config.cancelSelection ) {
this.onselectstart = function () {
return false;
var prevCheckbox = null,
$box = this;
// When our boxes are clicked..
- $box.click( function ( e ) {
+ $box.on( 'click', function ( e ) {
// And one has been clicked before...
if ( prevCheckbox !== null && e.shiftKey ) {
// Check or uncheck this one and all in-between checkboxes,
}
}
+ // eslint-disable-next-line jquery/no-animate-toggle
$containers.toggle( action === 'expand' );
hookCallback();
}
// Only fetch if the value in the textbox changed and is not empty, or if the results were hidden
// if the textbox is empty then clear the result div, but leave other settings intouched
if ( val.length === 0 ) {
+ // eslint-disable-next-line jquery/no-animate-toggle
$.suggestions.hide( context );
context.data.prevText = '';
} else if (
if ( context.data !== undefined ) {
if ( context.data.$textbox.val().length === 0 ) {
// Hide the div when no suggestion exist
+ // eslint-disable-next-line jquery/no-animate-toggle
$.suggestions.hide( context );
} else {
// Rebuild the suggestions list
.addClass( 'suggestions-result' )
.attr( 'rel', i )
.data( 'text', context.config.suggestions[ i ] )
- .mousemove( function () {
+ .on( 'mousemove', function () {
context.data.selectedWithMouse = true;
$.suggestions.highlight(
context,
break;
// Escape
case 27:
+ // eslint-disable-next-line jquery/no-animate-toggle
$.suggestions.hide( context );
$.suggestions.restore( context );
$.suggestions.cancel( context );
case 13:
preventDefault = wasVisible;
selected = context.data.$container.find( '.suggestions-result-current' );
+ // eslint-disable-next-line jquery/no-animate-toggle
$.suggestions.hide( context );
if ( selected.length === 0 || context.data.selectedWithMouse ) {
// If nothing is selected or if something was selected with the mouse
// Can't use click() because the container div is hidden when the
// textbox loses focus. Instead, listen for a mousedown followed
// by a mouseup on the same div.
- .mousedown( function ( e ) {
+ .on( 'mousedown', function ( e ) {
context.data.mouseDownOn = $( e.target ).closest( '.suggestions-results .suggestions-result' );
} )
- .mouseup( function ( e ) {
+ .on( 'mouseup', function ( e ) {
var $result = $( e.target ).closest( '.suggestions-results .suggestions-result' ),
$other = context.data.mouseDownOn;
// This will hide the link we're just clicking on, which causes problems
// when done synchronously in at least Firefox 3.6 (T64858).
setTimeout( function () {
+ // eslint-disable-next-line jquery/no-animate-toggle
$.suggestions.hide( context );
- }, 0 );
+ } );
}
// Always bring focus to the textbox, as that's probably where the user expects it
// if they were just typing.
// Can't use click() because the container div is hidden when the
// textbox loses focus. Instead, listen for a mousedown followed
// by a mouseup on the same div.
- .mousedown( function ( e ) {
+ .on( 'mousedown', function ( e ) {
context.data.mouseDownOn = $( e.target ).closest( '.suggestions-special' );
} )
- .mouseup( function ( e ) {
+ .on( 'mouseup', function ( e ) {
var $special = $( e.target ).closest( '.suggestions-special' ),
$other = context.data.mouseDownOn;
// This will hide the link we're just clicking on, which causes problems
// when done synchronously in at least Firefox 3.6 (T64858).
setTimeout( function () {
+ // eslint-disable-next-line jquery/no-animate-toggle
$.suggestions.hide( context );
- }, 0 );
+ } );
}
// Always bring focus to the textbox, as that's probably where the user expects it
// if they were just typing.
context.data.$textbox.focus();
} )
- .mousemove( function ( e ) {
+ .on( 'mousemove', function ( e ) {
context.data.selectedWithMouse = true;
$.suggestions.highlight(
context, $( e.target ).closest( '.suggestions-special' ), false
$( this )
// Stop browser autocomplete from interfering
.attr( 'autocomplete', 'off' )
- .keydown( function ( e ) {
+ .on( 'keydown', function ( e ) {
// Store key pressed to handle later
context.data.keypressed = e.which;
context.data.keypressedCount = 0;
} )
- .keypress( function ( e ) {
+ .on( 'keypress', function ( e ) {
context.data.keypressedCount++;
+ // eslint-disable-next-line jquery/no-event-shorthand
$.suggestions.keypress( e, context, context.data.keypressed );
} )
- .keyup( function ( e ) {
+ .on( 'keyup', function ( e ) {
// The keypress event is fired when a key is pressed down and that key normally
// produces a character value. We also want to handle some keys that don't
// produce a character value so we also attach to the keydown/keyup events.
e.which === context.data.keypressed &&
allowed.indexOf( e.which ) !== -1
) {
+ // eslint-disable-next-line jquery/no-event-shorthand
$.suggestions.keypress( e, context, context.data.keypressed );
}
} )
- .blur( function () {
+ .on( 'blur', function () {
// When losing focus because of a mousedown
// on a suggestion, don't hide the suggestions
if ( context.data.mouseDownOn.length > 0 ) {
return;
}
+ // eslint-disable-next-line jquery/no-animate-toggle
$.suggestions.hide( context );
$.suggestions.cancel( context );
} );
}
isSample = false;
- $( this ).focus();
+ $( this ).trigger( 'focus' );
if ( options.selectionStart !== undefined ) {
$( this ).textSelection( 'setSelection', { start: options.selectionStart, end: options.selectionEnd } );
}
} );
// Add form submission handler
- $( '#editform' ).submit( function () {
+ $( '#editform' ).on( 'submit', function () {
allowCloseWindow.release();
} );
} );
if ( scrollTop.value ) {
editBox.scrollTop = scrollTop.value;
}
- $editForm.submit( function () {
+ $editForm.on( 'submit', function () {
scrollTop.value = editBox.scrollTop;
} );
}
// Can't use fadeTo because it calls show(), and we might want to keep some elements hidden
// (e.g. empty #catlinks)
+ // FIXME: Use CSS transition
+ // eslint-disable-next-line jquery/no-animate
$copyElements.animate( { opacity: 0.4 }, 'fast' );
api = new mw.Api();
.append( $( '<a>' )
.attr( {
href: mw.util.getUrl( template.title ),
- 'class': ( template.exists ? '' : 'new' )
+ class: ( template.exists ? '' : 'new' )
} )
.text( template.title )
);
mw.hook( 'wikipage.editform' ).fire( $editform );
} ).always( function () {
$spinner.hide();
+ // FIXME: Use CSS transition
+ // eslint-disable-next-line jquery/no-animate
$copyElements.animate( {
opacity: 1
}, 'fast' );
if ( !document.getElementById( 'p-lang' ) && document.getElementById( 'p-tb' ) && mw.config.get( 'skin' ) === 'vector' ) {
$( '.portal:last' ).after(
$( '<div>' ).attr( {
- 'class': 'portal',
+ class: 'portal',
id: 'p-lang',
role: 'navigation',
'aria-labelledby': 'p-lang-label'
return true;
}
- $lis.find( 'input[name="diff"], input[name="oldid"]' ).click( updateDiffRadios );
+ $lis.find( 'input[name="diff"], input[name="oldid"]' ).on( 'click', updateDiffRadios );
// Set initial state
updateDiffRadios();
// Ideally we'd use e.target instead of $historySubmitter, but e.target points
// to the form element for submit actions, so.
- $historyCompareForm.find( '.historysubmit' ).click( function () {
+ $historyCompareForm.find( '.historysubmit' ).on( 'click', function () {
$historySubmitter = $( this );
} );
// Without the cloning we'd be changing the real form, which is slower, could make
// the page look broken for a second in slow browsers and might show the form broken
// again when coming back from a "next" page.
- $historyCompareForm.submit( function ( e ) {
+ $historyCompareForm.on( 'submit', function ( e ) {
var $copyForm, $copyRadios, $copyAction;
if ( $historySubmitter ) {
$copyForm.find( ':submit' ).remove();
}
- // IE7 doesn't do submission from an off-DOM clone, so insert hidden into document first
+ // Firefox requires the form to be attached, so insert hidden into document first
// Also remove potentially conflicting id attributes that we don't need anyway
$copyForm
.css( 'display', 'none' )
.find( '[id]' ).removeAttr( 'id' )
.end()
.insertAfter( $historyCompareForm )
- .submit();
+ .trigger( 'submit' );
e.preventDefault();
return false; // Because the submit is special, return false as well.
$a = $( '#ca-edit a' );
// Not every page has an edit link (T59713)
if ( $a.length ) {
+ // eslint-disable-next-line jquery/no-event-shorthand
$a.get( 0 ).click();
}
}
}
$popup = $( '<div>' ).addClass( 'postedit mw-notification' ).append( $content )
- .click( function () {
+ .on( 'click', function () {
clearTimeout( timeoutId );
fadeOutConfirmation();
} );
if ( e.target.nodeName.toLowerCase() !== 'a' ) {
// Trigger native HTMLElement click instead of opening URL (T45052)
e.preventDefault();
+ // eslint-disable-next-line jquery/no-event-shorthand
$edit.get( 0 ).click();
}
} );
}
} );
tokenPromise.done( function () {
- $form.submit();
+ $form.trigger( 'submit' );
} );
} );
$checkboxes.prop( 'checked', check );
}
- $( '.mw-checkbox-all' ).click( function () {
+ $( '.mw-checkbox-all' ).on( 'click', function () {
selectAll( true );
} );
- $( '.mw-checkbox-none' ).click( function () {
+ $( '.mw-checkbox-none' ).on( 'click', function () {
selectAll( false );
} );
- $( '.mw-checkbox-invert' ).click( function () {
+ $( '.mw-checkbox-invert' ).on( 'click', function () {
$checkboxes.prop( 'checked', function ( i, val ) {
return !val;
} );
hovzer.$.append( this.$container );
hovzer.update();
- $( '.mw-debug-panelink' ).click( this.switchPane );
+ $( '.mw-debug-panelink' ).on( 'click', this.switchPane );
},
/**
// Hide the current pane
if ( requestedPaneId === currentPaneId ) {
+ // FIXME: Use CSS transition
+ // eslint-disable-next-line jquery/no-slide
$currentPane.slideUp( updateHov );
debug.$container.data( 'currentPane', null );
return;
debug.$container.data( 'currentPane', requestedPaneId );
if ( currentPaneId === undefined || currentPaneId === null ) {
+ // FIXME: Use CSS transition
+ // eslint-disable-next-line jquery/no-slide
$requestedPane.slideDown( updateHov );
} else {
$currentPane.hide();
// Override toggle handler because we don't need it for this popup
// object at all. Sort of nasty, but it gets the job done.
- dialog.getPopup().toggle = $.noop;
+ dialog.getPopup().toggle = function () {};
}
}() );
$errorBox = this.$errorBox;
if ( errors.length === 0 ) {
+ // FIXME: Use CSS transition
+ // eslint-disable-next-line jquery/no-slide
$errorBox.slideUp( function () {
$errorBox
.removeAttr( 'class' )
.removeAttr( 'class' )
.detach();
}
+ // FIXME: Use CSS transition
+ // eslint-disable-next-line jquery/no-slide
$errorBox
.attr( 'class', 'error' )
.empty()
.slideDown();
};
if ( $oldErrorBox !== $errorBox && $oldErrorBox.hasClass( 'error' ) ) {
+ // eslint-disable-next-line jquery/no-slide
$oldErrorBox.slideUp( showFunc );
} else {
showFunc();
deleteButton.$element.closest( 'li.mw-htmlform-cloner-li' ).remove();
} );
} else {
- $element.filter( ':input' ).click( function ( ev ) {
- ev.preventDefault();
+ $element.filter( ':input' ).on( 'click', function ( e ) {
+ e.preventDefault();
$( this ).closest( 'li.mw-htmlform-cloner-li' ).remove();
} );
}
appendToCloner( createButton.$element );
} );
} else {
- $createElement.filter( ':input' ).click( function ( ev ) {
- ev.preventDefault();
+ $createElement.filter( ':input' ).on( 'click', function ( e ) {
+ e.preventDefault();
appendToCloner( $( this ) );
} );
name: name,
multiple: 'multiple',
'data-placeholder': dataPlaceholder.plain(),
- 'class': 'htmlform-chzn-select mw-input ' + oldClass
+ class: 'htmlform-chzn-select mw-input ' + oldClass
} );
$oldContainer.find( 'input' ).each( function () {
var $oldInput = $( this ),
// cache the current selection to avoid expensive lookup
currentValReasonList = $reasonList.val();
- $reasonList.change( function () {
+ $reasonList.on( 'change', function () {
currentValReasonList = $reasonList.val();
} );
* @param {Array} nodes List of nodes
* @return {string} Other message
*/
- 'int': function ( nodes ) {
+ int: function ( nodes ) {
var msg = textify( nodes[ 0 ] );
return mw.jqueryMsg.getMessageFunction()( msg.charAt( 0 ).toLowerCase() + msg.slice( 1 ) );
},
}
if ( mw.config.get( 'wgCascadeableLevels' ) !== undefined ) {
- $( 'form#mw-Protect-Form' ).submit( this.toggleUnchainedInputs.bind( ProtectionForm, true ) );
+ $( 'form#mw-Protect-Form' ).on( 'submit', this.toggleUnchainedInputs.bind( ProtectionForm, true ) );
}
this.getExpirySelectors().each( function () {
- $( this ).change( ProtectionForm.updateExpiryList.bind( ProtectionForm, this ) );
+ $( this ).on( 'change', ProtectionForm.updateExpiryList.bind( ProtectionForm, this ) );
} );
this.getExpiryInputs().each( function () {
$( this ).on( 'keyup change', ProtectionForm.updateExpiry.bind( ProtectionForm, this ) );
} );
this.getLevelSelectors().each( function () {
- $( this ).change( ProtectionForm.updateLevels.bind( ProtectionForm, this ) );
+ $( this ).on( 'change', ProtectionForm.updateLevels.bind( ProtectionForm, this ) );
} );
$( '#mwProtectSet > tbody > tr:first' ).after( $row );
$cell.append(
$( '<input>' )
.attr( { id: 'mwProtectUnchained', type: 'checkbox' } )
- .click( this.onChainClick.bind( this ) )
+ .on( 'click', this.onChainClick.bind( this ) )
.prop( 'checked', !this.areAllTypesMatching() ),
document.createTextNode( ' ' ),
$( '<label>' )
* @property {Object}
*/
autoHideSeconds: {
- 'short': 5,
- 'long': 30
+ short: 5,
+ long: 30
},
/**
if ( !bound ) {
bound = true;
$( window )
- .resize( $.debounce( 300, true, handleResizeStart ) )
- .resize( $.debounce( 300, handleResizeEnd ) );
+ .on( 'resize', $.debounce( 300, true, handleResizeStart ) )
+ .on( 'resize', $.debounce( 300, handleResizeEnd ) );
}
} );
} );
mw.hook( 'wikipage.categories' ).fire( $nodes );
}
- $( '#t-print a' ).click( function ( e ) {
+ $( '#t-print a' ).on( 'click', function ( e ) {
window.print();
e.preventDefault();
} );
$links = $links.filter( ':not( #bodyContent *, #content * )' );
}
- $links.click( function ( e ) {
+ $links.on( 'click', function ( e ) {
var mwTitle, action, api, $link;
mwTitle = mw.Title.newFromText( title );
// Collect views
allViews = $.extend( true, {
- 'default': {
+ default: {
title: mw.msg( 'rcfilters-filterlist-title' ),
groups: filterGroups
}
id,
obj.label,
normalizedData,
- { 'default': isDefault }
+ { default: isDefault }
)
] );
randomID,
label,
normalizedData,
- { 'default': isDefault }
+ { default: isDefault }
)
] );
name: 'namespace', // parameter name is singular
type: 'string_options',
title: mw.msg( 'namespaces' ),
- labelPrefixKey: { 'default': 'rcfilters-tag-prefix-namespace', inverted: 'rcfilters-tag-prefix-namespace-inverted' },
+ labelPrefixKey: { default: 'rcfilters-tag-prefix-namespace', inverted: 'rcfilters-tag-prefix-namespace-inverted' },
separator: ';',
fullCoverage: true,
filters: items
hidden: true,
filters: [ {
name: 'invert',
- 'default': '0'
+ default: '0'
} ]
} ]
};
max: 1000
},
sortFunc: function ( a, b ) { return Number( a.name ) - Number( b.name ); },
- 'default': mw.user.options.get( this.limitPreferenceName, displayConfig.limitDefault ),
+ default: mw.user.options.get( this.limitPreferenceName, displayConfig.limitDefault ),
sticky: true,
filters: displayConfig.limitArray.map( function ( num ) {
return controller._createFilterDataFromNumber( num, num );
( Number( i ) * 24 ).toFixed( 2 ) :
Number( i );
},
- 'default': mw.user.options.get( this.daysPreferenceName, displayConfig.daysDefault ),
+ default: mw.user.options.get( this.daysPreferenceName, displayConfig.daysDefault ),
sticky: true,
filters: [
// Hours (1, 2, 6, 12)
filters: [
{
name: 'enhanced',
- 'default': String( mw.user.options.get( 'usenewrc', 0 ) )
+ default: String( mw.user.options.get( 'usenewrc', 0 ) )
}
]
}
filters: [
{
name: 'target',
- 'default': ''
+ default: ''
}
]
},
filters: [
{
name: 'showlinkedto',
- 'default': false
+ default: false
}
]
}
$firstNew.after( $indicator );
}
+ // FIXME: Use CSS transition
+ // eslint-disable-next-line jquery/no-fade
$newChanges
.hide()
.fadeIn( 1000 );
this.queriesModel.connect( this, {
itemUpdate: 'onSavedQueriesItemUpdate',
initialize: 'onSavedQueriesInitialize',
- 'default': 'reevaluateResetRestoreState'
+ default: 'reevaluateResetRestoreState'
} );
}
)
)
) {
+ // eslint-disable-next-line jquery/no-animate
$( container ).animate( {
scrollTop: newScrollTop
} );
this.menu = new mw.rcfilters.ui.GroupWidget( {
events: {
click: 'menuItemClick',
- 'delete': 'menuItemDelete',
- 'default': 'menuItemDefault',
+ delete: 'menuItemDelete',
+ default: 'menuItemDefault',
edit: 'menuItemEdit'
},
classes: [ 'mw-rcfilters-ui-savedLinksListWidget-menu' ],
// OO.ui.ButtonWidget doesn't take focus itself (T128054)
$focus = $( '#mw-apisandbox-ui' ).find( document.activeElement );
if ( $focus.length ) {
+ // eslint-disable-next-line jquery/no-event-shorthand
$focus[ 0 ].blur();
}
// it makes it too hard to read and our "disabled"
// isn't really disabled.
widgetField.onFieldDisable( false );
- widgetField.onFieldDisable = $.noop;
+ widgetField.onFieldDisable = function () {};
widgetField.apiParamIndex = ppi.index;
widget = Util.createWidgetForParameter( {
name: name,
type: 'string',
- 'default': ''
+ default: ''
}, {
nooptional: true
} );
}
that.deprecatedItemsFieldset = new OO.ui.FieldsetLayout().addItems( deprecatedItems ).toggle( false );
+ // eslint-disable-next-line jquery/no-animate-toggle
tmp = $( '<fieldset>' )
.toggle( !that.deprecatedItemsFieldset.isEmpty() )
.append(
hideUserField = infuseIfExists( $( '#mw-input-wpHideUser' ).closest( '.oo-ui-fieldLayout' ) ),
watchUserField = infuseIfExists( $( '#mw-input-wpWatch' ).closest( '.oo-ui-fieldLayout' ) ),
expiryWidget = infuseIfExists( $( '#mw-input-wpExpiry' ) ),
+ editingWidget = infuseIfExists( $( '#mw-input-wpEditing' ) ),
editingRestrictionWidget = infuseIfExists( $( '#mw-input-wpEditingRestriction' ) ),
preventTalkPageEdit = infuseIfExists( $( '#mw-input-wpDisableUTEdit' ) ),
pageRestrictionsWidget = infuseIfExists( $( '#mw-input-wpPageRestrictions' ) );
// infinityValues are the values the SpecialBlock class accepts as infinity (sf. wfIsInfinity)
infinityValues = [ 'infinite', 'indefinite', 'infinity', 'never' ],
isIndefinite = infinityValues.indexOf( expiryValue ) !== -1,
- editingRestrictionValue = editingRestrictionWidget ? editingRestrictionWidget.getValue() : undefined;
+ editingRestrictionValue = editingRestrictionWidget ? editingRestrictionWidget.getValue() : undefined,
+ editingIsSelected = editingWidget ? editingWidget.isSelected() : undefined;
if ( enableAutoblockField ) {
enableAutoblockField.toggle( !( isNonEmptyIp ) );
watchUserField.toggle( !( isIpRange && !isEmpty ) );
}
if ( pageRestrictionsWidget ) {
- pageRestrictionsWidget.setDisabled( editingRestrictionValue === 'sitewide' );
+ editingRestrictionWidget.setDisabled( !editingIsSelected );
+ pageRestrictionsWidget.setDisabled( !editingIsSelected || editingRestrictionValue === 'sitewide' );
}
if ( preventTalkPageEdit ) {
// TODO: (T210475) this option is disabled for partial blocks unless
// a namespace restriction for User_talk namespace is in place.
// This needs to be updated once Namespace restrictions is available
- if ( editingRestrictionValue === 'partial' ) {
+ if ( editingRestrictionValue === 'partial' && editingIsSelected ) {
preventTalkPageEdit.setDisabled( true );
} else {
preventTalkPageEdit.setDisabled( false );
if ( editingRestrictionWidget ) {
editingRestrictionWidget.on( 'change', updateBlockOptions );
}
+ if ( editingWidget ) {
+ editingWidget.on( 'change', updateBlockOptions );
+ }
// Call them now to set initial state (ie. Special:Block/Foobar?wpBlockExpiry=2+hours)
updateBlockOptions();
$( function () {
var $projectField = $( '#mw-import-table-interwiki #interwiki' );
if ( $projectField.length ) {
- $projectField.change( updateImportSubprojectList );
+ $projectField.on( 'change', updateImportSubprojectList );
updateImportSubprojectList();
}
} );
// Bind to change event, and trigger once to set the initial state of the checkboxes.
rc.updateCheckboxes();
- $select.change( rc.updateCheckboxes );
+ $select.on( 'change', rc.updateCheckboxes );
}
};
for ( i = 0; i < results.length; i++ ) {
result = results[ i ];
- imageCaption = mw.html.element( 'span', { 'class': 'iw-result__mini-gallery__caption' }, result.title );
+ imageCaption = mw.html.element( 'span', { class: 'iw-result__mini-gallery__caption' }, result.title );
imageThumbnailSrc = ( result.thumbnail ) ? result.thumbnail.source : '';
resultOutput += '<div class="iw-result__mini-gallery">' +
/* escaping response content */
mw.html.element( 'a', {
href: '/wiki/' + result.title,
- 'class': 'iw-result__mini-gallery__image',
+ class: 'iw-result__mini-gallery__image',
style: 'background-image: url(' + imageThumbnailSrc + ');'
}, new mw.html.Raw( imageCaption ) ) +
'</div>';
// Emulate HTML5 autofocus behavior in non HTML5 compliant browsers
if ( !( 'autofocus' in document.createElement( 'input' ) ) ) {
- $( 'input[autofocus]' ).eq( 0 ).focus();
+ $( 'input[autofocus]' ).eq( 0 ).trigger( 'focus' );
}
// Attach handler for check all/none buttons
$checkboxes = $( '#powersearch input[id^=mw-search-ns]' );
- $( '#mw-search-toggleall' ).click( function () {
+ $( '#mw-search-toggleall' ).on( 'click', function () {
$checkboxes.prop( 'checked', true );
} );
- $( '#mw-search-togglenone' ).click( function () {
+ $( '#mw-search-togglenone' ).on( 'click', function () {
$checkboxes.prop( 'checked', false );
} );
updateHeaderLinks( searchWidget.getValue() );
// When saving settings, use the proper request method (POST instead of GET).
- $( '#mw-search-powersearch-remember' ).change( function () {
+ $( '#mw-search-powersearch-remember' ).on( 'change', function () {
this.form.method = this.checked ? 'post' : 'get';
} ).trigger( 'change' );
summaryByteLimit = mw.config.get( 'wgCommentByteLimit' ),
wpComment = OO.ui.infuse( $( '#wpComment' ).closest( '.oo-ui-widget' ) );
- $( '#mw-undelete-invert' ).click( function () {
+ $( '#mw-undelete-invert' ).on( 'click', function () {
$( '.mw-undelete-revlist input[type="checkbox"]' ).prop( 'checked', function ( i, val ) {
return !val;
} );
*/
( function () {
$( function () {
- $( 'a.mw-watch-link' ).click( function ( e ) {
+ $( 'a.mw-watch-link' ).on( 'click', function ( e ) {
var promise,
api = new mw.Api(),
$link = $( this ),
if ( ajaxUploadDestCheck ) {
// Insert an event handler that fetches upload warnings when wpDestFile
// has been changed
- $( '#wpDestFile' ).change( function () {
+ $( '#wpDestFile' ).on( 'change', function () {
uploadWarning.checkNow( $( this ).val() );
} );
// Insert a row where the warnings will be displayed just below the
if ( mw.config.get( 'wgAjaxLicensePreview' ) && $license.length ) {
// License selector check
- $license.change( function () {
+ $license.on( 'change', function () {
// We might show a preview
uploadTemplatePreview.getPreview( $license, $( '#mw-license-preview' ) );
} );
// fillDestFile setup
mw.config.get( 'wgUploadSourceIds' ).forEach( function ( sourceId ) {
- $( '#' + sourceId ).change( function () {
+ $( '#' + sourceId ).on( 'change', function () {
var path, slash, backslash, fname;
if ( !mw.config.get( 'wgUploadAutoFill' ) ) {
return;
/* Initialization */
if ( hasFileAPI() ) {
// Update thumbnail when the file selection control is updated.
- $( '#wpUploadFile' ).change( function () {
+ $( '#wpUploadFile' ).on( 'change', function () {
var file;
clearPreview();
if ( this.files && this.files.length ) {
namespace: 'uploadwarning'
} );
- $uploadForm.submit( function () {
+ $uploadForm.on( 'submit', function () {
allowCloseWindow.release();
} );
} );
// Change tabindex only when main div has focus
if ( $( this ).is( ':focus' ) ) {
- $( this ).find( 'a' ).first().focus();
+ $( this ).find( 'a' ).first().trigger( 'focus' );
setEditTabindex( '0' );
}
} );
// Dynamically show/hide the "other time" input under each dropdown
$( '.mw-userrights-nested select' ).on( 'change', function ( e ) {
+ // eslint-disable-next-line jquery/no-animate-toggle
$( e.target.parentNode ).find( 'input' ).toggle( $( e.target ).val() === 'other' );
} );
// If the user wants to reset their watchlist, use an API call to do so (no reload required)
// Adapted from a user script by User:NQ of English Wikipedia
// (User:NQ/WatchlistResetConfirm.js)
- $resetForm.submit( function ( event ) {
+ $resetForm.on( 'submit', function ( event ) {
var $button = $resetForm.find( 'input[name=mw-watchlist-reset-submit]' );
event.preventDefault();
} ).fail( function () {
// On error, fall back to server-side reset
// First remove this submit listener and then re-submit the form
- $resetForm.off( 'submit' ).submit();
+ $resetForm.off( 'submit' ).trigger( 'submit' );
} );
} );
// add a listener on all form elements in the header form
$( '#mw-watchlist-form input, #mw-watchlist-form select' ).on( 'change', function () {
// submit the form when one of the input fields is modified
- $( '#mw-watchlist-form' ).submit();
+ $( '#mw-watchlist-form' ).trigger( 'submit' );
} );
}
event.preventDefault();
event.stopPropagation();
- $unwatchLink.blur();
+ $unwatchLink.trigger( 'blur' );
} );
}
} );
@ooui-spacing-radio-label: 26 / @ooui-font-size-browser / @ooui-font-size-base; // Equals `1.85714286em`≈`26px`
-.mw-block-page-restrictions.oo-ui-fieldLayout {
- margin-top: 0;
- margin-left: @ooui-spacing-radio-label;
+body.mw-special-Block {
+ .mw-block-editing-restriction.oo-ui-fieldLayout {
+ margin-left: @ooui-spacing-radio-label;
+ }
+
+ .mw-block-page-restrictions.oo-ui-fieldLayout {
+ margin-left: 2 * @ooui-spacing-radio-label;
+
+ .oo-ui-widget {
+ max-width: 50em - 2 * @ooui-spacing-radio-label;
+ }
+ }
+
+ .oo-ui-panelLayout-framed {
+ border: 0;
+ }
- .oo-ui-widget {
- max-width: 50em - @ooui-spacing-radio-label;
+ .oo-ui-panelLayout-padded {
+ padding: 0;
}
}
// Hide/show the table of contents element
function toggleToc() {
if ( $tocList.is( ':hidden' ) ) {
+ // FIXME: Use CSS transitions
+ // eslint-disable-next-line jquery/no-slide
$tocList.slideDown( 'fast' );
$tocToggleLink.text( mw.msg( 'hidetoc' ) );
$this.removeClass( 'tochidden' );
mw.cookie.set( 'hidetoc', null );
} else {
+ // eslint-disable-next-line jquery/no-slide
$tocList.slideUp( 'fast' );
$tocToggleLink.text( mw.msg( 'showtoc' ) );
$this.addClass( 'tochidden' );
);
}
if ( $field.is( ':input' ) ) {
- $field.select();
+ $field.trigger( 'select' );
}
return false;
}
this.setValue( this.formatter.getDefaultDate() );
}
if ( $field.is( ':input' ) ) {
- $field.select();
+ $field.trigger( 'select' );
}
if ( this.calendar ) {
* are set up. Note: The promise must have an .abort() functionality.
*/
mw.widgets.APIResultsQueue.prototype.setup = function () {
- return $.Deferred().resolve().promise( { abort: $.noop } );
+ return $.Deferred().resolve().promise( { abort: function () {} } );
};
/**
provider = this;
if ( !this.isValid() ) {
- return $.Deferred().reject().promise( { abort: $.noop } );
+ return $.Deferred().reject().promise( { abort: function () {} } );
}
api = this.isLocal ? new mw.Api() : new mw.ForeignApi( this.getAPIurl(), { anonymous: true } );
if ( e.which === OO.ui.Keys.TAB ) {
if ( e.shiftKey ) {
// Tabbing backward from text input: normal browser behavior
- $.noop();
} else {
// Tabbing forward from text input: just focus the calendar
this.calendar.$element.focus();
self = this;
// reuse the searchSuggest function from mw.searchSuggest
- promise = mw.searchSuggest.request( api, this.getQueryValue(), $.noop, this.limit, this.getNamespace() );
+ promise = mw.searchSuggest.request( api, this.getQueryValue(), function () {}, this.limit, this.getNamespace() );
// tracking purposes
promise.done( function ( data, jqXHR ) {
<?php
+use MediaWiki\Block\Restriction\PageRestriction;
use MediaWiki\MediaWikiServices;
/**
'wgEmailAuthentication' => true,
] );
- $this->setUserPerm( [ "createpage", "move" ] );
+ $this->setUserPerm( [ 'createpage', 'edit', 'move', 'rollback', 'patrol', 'upload', 'purge' ] );
$this->setTitle( NS_HELP, "test page" );
# $wgEmailConfirmToEdit only applies to 'edit' action
'expiry' => 10,
'systemBlock' => 'test',
] );
- $this->assertEquals( [ [ 'systemblockedtext',
+
+ $errors = [ [ 'systemblockedtext',
'[[User:Useruser|Useruser]]', 'no reason given', '127.0.0.1',
'Useruser', 'test', '23:00, 31 December 1969', '127.0.8.1',
- $wgLang->timeanddate( wfTimestamp( TS_MW, $now ), true ) ] ],
+ $wgLang->timeanddate( wfTimestamp( TS_MW, $now ), true ) ] ];
+
+ $this->assertEquals( $errors,
+ $this->title->getUserPermissionsErrors( 'edit', $this->user ) );
+ $this->assertEquals( $errors,
$this->title->getUserPermissionsErrors( 'move-target', $this->user ) );
+ $this->assertEquals( $errors,
+ $this->title->getUserPermissionsErrors( 'rollback', $this->user ) );
+ $this->assertEquals( $errors,
+ $this->title->getUserPermissionsErrors( 'patrol', $this->user ) );
+ $this->assertEquals( $errors,
+ $this->title->getUserPermissionsErrors( 'upload', $this->user ) );
+ $this->assertEquals( [],
+ $this->title->getUserPermissionsErrors( 'purge', $this->user ) );
// partial block message test
$this->user->mBlockedby = $this->user->getName();
'expiry' => 10,
] );
- $this->assertEquals( [ [ 'blockedtext-partial',
+ $this->assertEquals( [],
+ $this->title->getUserPermissionsErrors( 'edit', $this->user ) );
+ $this->assertEquals( [],
+ $this->title->getUserPermissionsErrors( 'move-target', $this->user ) );
+ $this->assertEquals( [],
+ $this->title->getUserPermissionsErrors( 'rollback', $this->user ) );
+ $this->assertEquals( [],
+ $this->title->getUserPermissionsErrors( 'patrol', $this->user ) );
+ $this->assertEquals( [],
+ $this->title->getUserPermissionsErrors( 'upload', $this->user ) );
+ $this->assertEquals( [],
+ $this->title->getUserPermissionsErrors( 'purge', $this->user ) );
+
+ $this->user->mBlock->setRestrictions( [
+ ( new PageRestriction( 0, $this->title->getArticleID() ) )->setTitle( $this->title ),
+ ] );
+
+ $errors = [ [ 'blockedtext-partial',
'[[User:Useruser|Useruser]]', 'no reason given', '127.0.0.1',
'Useruser', null, '23:00, 31 December 1969', '127.0.8.1',
- $wgLang->timeanddate( wfTimestamp( TS_MW, $now ), true ) ] ],
+ $wgLang->timeanddate( wfTimestamp( TS_MW, $now ), true ) ] ];
+
+ $this->assertEquals( $errors,
+ $this->title->getUserPermissionsErrors( 'edit', $this->user ) );
+ $this->assertEquals( $errors,
$this->title->getUserPermissionsErrors( 'move-target', $this->user ) );
+ $this->assertEquals( $errors,
+ $this->title->getUserPermissionsErrors( 'rollback', $this->user ) );
+ $this->assertEquals( $errors,
+ $this->title->getUserPermissionsErrors( 'patrol', $this->user ) );
+ $this->assertEquals( [],
+ $this->title->getUserPermissionsErrors( 'upload', $this->user ) );
+ $this->assertEquals( [],
+ $this->title->getUserPermissionsErrors( 'purge', $this->user ) );
}
}
public function testCheckPasswordValidity() {
$uppCalled = 0;
- $uppStatus = \Status::newGood();
+ $uppStatus = \Status::newGood( [] );
$this->setMwGlobals( [
'wgPasswordPolicy' => [
'policies' => [
$this->assertNotNull( $ret );
$this->assertSame( 'resetpass-validity-soft', $ret->msg->getKey() );
$this->assertFalse( $ret->hard );
+
+ $this->manager->removeAuthenticationSessionData( null );
+ $row->user_password_expires = null;
+ $status = \Status::newGood( [ 'forceChange' => true ] );
+ $status->error( 'testing' );
+ $providerPriv->setPasswordResetFlag( $userName, $status, $row );
+ $ret = $this->manager->getAuthenticationSessionData( 'reset-pass' );
+ $this->assertNotNull( $ret );
+ $this->assertSame( 'resetpass-validity', $ret->msg->getKey() );
+ $this->assertTrue( $ret->hard );
}
public function testAuthentication() {
protected $policies = [
'checkuser' => [
- 'MinimalPasswordLength' => 10,
+ 'MinimalPasswordLength' => [ 'value' => 10, 'forceChange' => true ],
'MinimumPasswordLengthToLogin' => 6,
'PasswordCannotMatchUsername' => true,
],
'MinimumPasswordLengthToLogin' => 1,
'PasswordCannotMatchBlacklist' => true,
'MaximalPasswordLength' => 4096,
+ // test null handling
+ 'PasswordCannotMatchUsername' => null,
],
];
public function testGetPoliciesForUser() {
$upp = $this->getUserPasswordPolicy();
- $user = User::newFromName( 'TestUserPolicy' );
- $user->addToDatabase();
- $user->addGroup( 'sysop' );
-
+ $user = $this->getTestUser( [ 'sysop' ] )->getUser();
$this->assertArrayEquals(
[
'MinimalPasswordLength' => 8,
'MinimumPasswordLengthToLogin' => 1,
- 'PasswordCannotMatchUsername' => 1,
+ 'PasswordCannotMatchUsername' => true,
+ 'PasswordCannotMatchBlacklist' => true,
+ 'MaximalPasswordLength' => 4096,
+ ],
+ $upp->getPoliciesForUser( $user )
+ );
+
+ $user = $this->getTestUser( [ 'sysop', 'checkuser' ] )->getUser();
+ $this->assertArrayEquals(
+ [
+ 'MinimalPasswordLength' => [ 'value' => 10, 'forceChange' => true ],
+ 'MinimumPasswordLengthToLogin' => 6,
+ 'PasswordCannotMatchUsername' => true,
'PasswordCannotMatchBlacklist' => true,
'MaximalPasswordLength' => 4096,
],
$this->assertArrayEquals(
[
- 'MinimalPasswordLength' => 10,
+ 'MinimalPasswordLength' => [ 'value' => 10, 'forceChange' => true ],
'MinimumPasswordLengthToLogin' => 6,
'PasswordCannotMatchUsername' => true,
'PasswordCannotMatchBlacklist' => true,
/**
* @dataProvider provideCheckUserPassword
*/
- public function testCheckUserPassword( $username, $groups, $password, $valid, $ok, $msg ) {
+ public function testCheckUserPassword( $groups, $password, StatusValue $expectedStatus ) {
$upp = $this->getUserPasswordPolicy();
-
- $user = User::newFromName( $username );
- $user->addToDatabase();
- foreach ( $groups as $group ) {
- $user->addGroup( $group );
- }
+ $user = $this->getTestUser( $groups )->getUser();
$status = $upp->checkUserPassword( $user, $password );
- $this->assertSame( $valid, $status->isGood(), $msg . ' - password valid' );
- $this->assertSame( $ok, $status->isOK(), $msg . ' - can login' );
+ $this->assertSame( $expectedStatus->isGood(), $status->isGood(), 'password valid' );
+ $this->assertSame( $expectedStatus->isOK(), $status->isOK(), 'can login' );
+ $this->assertSame( $expectedStatus->getValue(), $status->getValue(), 'flags' );
}
public function provideCheckUserPassword() {
+ $success = Status::newGood( [] );
+ $warning = Status::newGood( [] );
+ $forceChange = Status::newGood( [ 'forceChange' => true ] );
+ $fatal = Status::newGood( [] );
+ // the message does not matter, we only test for state and value
+ $warning->warning( 'invalid-password' );
+ $forceChange->warning( 'invalid-password' );
+ $warning->warning( 'invalid-password' );
+ $fatal->fatal( 'invalid-password' );
return [
- [
- 'PassPolicyUser',
+ 'No groups, default policy, password too short to login' => [
[],
'',
- false,
- false,
- 'No groups, default policy, password too short to login'
+ $fatal,
],
- [
- 'PassPolicyUser',
+ 'Default policy, short password' => [
[ 'user' ],
'aaa',
- false,
- true,
- 'Default policy, short password'
+ $warning,
],
- [
- 'PassPolicyUser',
+ 'Sysop with good password' => [
[ 'sysop' ],
'abcdabcdabcd',
- true,
- true,
- 'Sysop with good password'
+ $success,
],
- [
- 'PassPolicyUser',
+ 'Sysop with short password' => [
[ 'sysop' ],
'abcd',
- false,
- true,
- 'Sysop with short password'
+ $warning,
],
- [
- 'PassPolicyUser',
+ 'Checkuser with short password' => [
[ 'sysop', 'checkuser' ],
'abcdabcd',
- false,
- true,
- 'Checkuser with short password'
+ $forceChange,
],
- [
- 'PassPolicyUser',
+ 'Checkuser with too short password to login' => [
[ 'sysop', 'checkuser' ],
'abcd',
- false,
- false,
- 'Checkuser with too short password to login'
- ],
- [
- 'Useruser',
- [ 'user' ],
- 'Passpass',
- false,
- true,
- 'Username & password on blacklist'
+ $fatal,
],
];
}
+ public function testCheckUserPassword_blacklist() {
+ $upp = $this->getUserPasswordPolicy();
+ $user = User::newFromName( 'Useruser' );
+ $user->addToDatabase();
+
+ $status = $upp->checkUserPassword( $user, 'Passpass' );
+ $this->assertFalse( $status->isGood(), 'password invalid' );
+ $this->assertTrue( $status->isOK(), 'can login' );
+ }
+
/**
* @dataProvider provideMaxOfPolicies
*/
- public function testMaxOfPolicies( $p1, $p2, $max, $msg ) {
+ public function testMaxOfPolicies( $p1, $p2, $max ) {
$this->assertArrayEquals(
$max,
- UserPasswordPolicy::maxOfPolicies( $p1, $p2 ),
- $msg
+ UserPasswordPolicy::maxOfPolicies( $p1, $p2 )
);
}
public function provideMaxOfPolicies() {
return [
- [
+ 'Basic max in p1' => [
[ 'MinimalPasswordLength' => 8 ], // p1
[ 'MinimalPasswordLength' => 2 ], // p2
[ 'MinimalPasswordLength' => 8 ], // max
- 'Basic max in p1'
],
- [
+ 'Basic max in p2' => [
[ 'MinimalPasswordLength' => 2 ], // p1
[ 'MinimalPasswordLength' => 8 ], // p2
[ 'MinimalPasswordLength' => 8 ], // max
- 'Basic max in p2'
],
- [
- [ 'MinimalPasswordLength' => 8 ], // p1
+ 'Missing items in p1' => [
+ [
+ 'MinimalPasswordLength' => 8,
+ ], // p1
[
'MinimalPasswordLength' => 2,
'PasswordCannotMatchUsername' => 1,
'MinimalPasswordLength' => 8,
'PasswordCannotMatchUsername' => 1,
], // max
- 'Missing items in p1'
],
- [
+ 'Missing items in p2' => [
[
'MinimalPasswordLength' => 8,
'PasswordCannotMatchUsername' => 1,
'MinimalPasswordLength' => 8,
'PasswordCannotMatchUsername' => 1,
], // max
- 'Missing items in p2'
+ ],
+ 'complex value in p1' => [
+ [
+ 'MinimalPasswordLength' => [
+ 'value' => 8,
+ 'foo' => 1,
+ ],
+ ], // p1
+ [
+ 'MinimalPasswordLength' => 2,
+ ], // p2
+ [
+ 'MinimalPasswordLength' => [
+ 'value' => 8,
+ 'foo' => 1,
+ ],
+ ], // max
+ ],
+ 'complex value in p2' => [
+ [
+ 'MinimalPasswordLength' => 8,
+ ], // p1
+ [
+ 'MinimalPasswordLength' => [
+ 'value' => 2,
+ 'foo' => 1,
+ ],
+ ], // p2
+ [
+ 'MinimalPasswordLength' => [
+ 'value' => 8,
+ 'foo' => 1,
+ ],
+ ], // max
+ ],
+ 'complex value in both p1 and p2' => [
+ [
+ 'MinimalPasswordLength' => [
+ 'value' => 8,
+ 'foo' => 1,
+ 'baz' => false,
+ ],
+ ], // p1
+ [
+ 'MinimalPasswordLength' => [
+ 'value' => 2,
+ 'bar' => 2,
+ 'baz' => true,
+ ],
+ ], // p2
+ [
+ 'MinimalPasswordLength' => [
+ 'value' => 8,
+ 'foo' => 1,
+ 'bar' => 2,
+ 'baz' => true,
+ ],
+ ], // max
],
];
}
// html5sec SVG vectors
[
'<svg xmlns="http://www.w3.org/2000/svg"><script>alert(1)</script></svg>',
- true,
- true,
+ true, /* SVG is well formed */
+ true, /* Evil SVG detected */
'Script tag in svg (http://html5sec.org/#47)'
],
[
true,
false,
'DTD with aliased entities apos (Should be allowed)'
- ]
+ ],
+ [
+ '<svg xmlns="http://www.w3.org/2000/svg"><g filter="url( \'#foo\' )"></g></svg>',
+ true,
+ false,
+ 'SVG with local filter (T69044)'
+ ],
+ [
+ '<svg xmlns="http://www.w3.org/2000/svg"><g filter="url( http://example.com/#foo )"></g></svg>',
+ true,
+ true,
+ 'SVG with non-local filter (T69044)'
+ ],
+
];
// phpcs:enable
}
"sinon": false
},
"rules": {
- "operator-linebreak": 0,
+ "operator-linebreak": "off",
"quote-props": [ "error", "as-needed" ],
- "valid-jsdoc": 0,
- "qunit/require-expect": 0,
- "qunit/resolve-async": 0
+ "valid-jsdoc": "off",
+ "qunit/require-expect": "off",
+ "qunit/resolve-async": "off",
+ "jquery/no-parse-html-literal": "off"
}
}
if ( warn === undefined ) {
warn = mw.log.warn;
error = mw.log.error;
- mw.log.warn = mw.log.error = $.noop;
+ mw.log.warn = mw.log.error = function () {};
}
}
var done = assert.async(),
$canvas = $( '<div>' ).css( 'background-color', '#fff' ).appendTo( '#qunit-fixture' );
+ // eslint-disable-next-line jquery/no-animate
$canvas.animate( { 'background-color': '#000' }, 3 ).promise()
.done( function () {
var endColors = $.colorUtil.getRGB( $canvas.css( 'background-color' ) );
callback( $table );
} else {
$table.tablesorter();
- $table.find( '#sortme' ).click();
+ $table.find( '#sortme' ).trigger( 'click' );
}
// Table sorting is done synchronously; if it ever needs to change back
planetsAscName,
function ( $table ) {
$table.tablesorter();
- $table.find( '.headerSort:eq(0)' ).click();
+ $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
}
);
tableTest(
planetsAscName,
function ( $table ) {
$table.tablesorter();
- $table.find( '.headerSort:eq(0)' ).click();
+ $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
}
);
tableTest(
planetsAscName,
function ( $table ) {
$table.tablesorter();
- $table.find( '.headerSort:eq(0)' ).click();
- $table.find( '.headerSort:eq(1)' ).click();
- $table.find( '.headerSort:eq(0)' ).click();
+ $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
+ $table.find( '.headerSort:eq(1)' ).trigger( 'click' );
+ $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
}
);
tableTest(
reversed( planetsAscName ),
function ( $table ) {
$table.tablesorter();
- $table.find( '.headerSort:eq(0)' ).click().click();
+ $table.find( '.headerSort:eq(0)' ).trigger( 'click' ).trigger( 'click' );
}
);
tableTest(
planetsAscRadius,
function ( $table ) {
$table.tablesorter();
- $table.find( '.headerSort:eq(1)' ).click();
+ $table.find( '.headerSort:eq(1)' ).trigger( 'click' );
}
);
tableTest(
reversed( planetsAscRadius ),
function ( $table ) {
$table.tablesorter();
- $table.find( '.headerSort:eq(1)' ).click().click();
+ $table.find( '.headerSort:eq(1)' ).trigger( 'click' ).trigger( 'click' );
}
);
tableTest(
$table.tablesorter(
{ sortList: [ { 0: 'asc' }, { 1: 'asc' } ] }
);
- $table.find( '.headerSort:eq(0)' ).click();
+ $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
}
);
tableTest(
$table.tablesorter(
{ sortList: [ { 0: 'desc' }, { 1: 'desc' } ] }
);
- $table.find( '.headerSort:eq(0)' ).click();
+ $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
// Pretend to click while pressing the multi-sort key
event = $.Event( 'click' );
$table.find( 'tr:eq(0) th:eq(0)' ).attr( 'colspan', '3' );
$table.tablesorter();
- $table.find( '.headerSort:eq(0)' ).click();
+ $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
}
);
tableTest( 'Sorting with colspanned headers: sort spanned column twice',
$table.find( 'tr:eq(0) th:eq(0)' ).attr( 'colspan', '3' );
$table.tablesorter();
- $table.find( '.headerSort:eq(0)' ).click();
- $table.find( '.headerSort:eq(0)' ).click();
+ $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
+ $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
}
);
tableTest( 'Sorting with colspanned headers: subsequent column',
$table.find( 'tr:eq(0) th:eq(0)' ).attr( 'colspan', '3' );
$table.tablesorter();
- $table.find( '.headerSort:eq(1)' ).click();
+ $table.find( '.headerSort:eq(1)' ).trigger( 'click' );
}
);
tableTest( 'Sorting with colspanned headers: sort subsequent column twice',
$table.find( 'tr:eq(0) th:eq(0)' ).attr( 'colspan', '3' );
$table.tablesorter();
- $table.find( '.headerSort:eq(1)' ).click();
- $table.find( '.headerSort:eq(1)' ).click();
+ $table.find( '.headerSort:eq(1)' ).trigger( 'click' );
+ $table.find( '.headerSort:eq(1)' ).trigger( 'click' );
}
);
$table.find( 'tr:eq(0) > th:eq(0)' ).addClass( 'unsortable' );
$table.tablesorter();
- $table.find( 'tr:eq(0) > th:eq(0)' ).click();
+ $table.find( 'tr:eq(0) > th:eq(0)' ).trigger( 'click' );
assert.deepEqual(
tableExtract( $table ),
);
$cell = $table.find( 'tr:eq(0) > th:eq(0)' );
- $table.find( 'tr:eq(0) > th:eq(1)' ).click();
+ $table.find( 'tr:eq(0) > th:eq(1)' ).trigger( 'click' );
assert.strictEqual(
$cell.hasClass( 'headerSortUp' ) || $cell.hasClass( 'headerSortDown' ),
mw.config.set( 'wgPageContentLanguage', 'de' );
$table.tablesorter();
- $table.find( '.headerSort:eq(0)' ).click();
+ $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
}
);
mw.config.set( 'wgDefaultDateFormat', 'mdy' );
$table.tablesorter();
- $table.find( '.headerSort:eq(0)' ).click();
+ $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
}
);
ipv4Sorted,
function ( $table ) {
$table.tablesorter();
- $table.find( '.headerSort:eq(0)' ).click();
+ $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
}
);
tableTest(
reversed( ipv4Sorted ),
function ( $table ) {
$table.tablesorter();
- $table.find( '.headerSort:eq(0)' ).click().click();
+ $table.find( '.headerSort:eq(0)' ).trigger( 'click' ).trigger( 'click' );
}
);
} );
$table.tablesorter();
- $table.find( '.headerSort:eq(0)' ).click();
+ $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
}
);
} );
$table.tablesorter();
- $table.find( '.headerSort:eq(0)' ).click();
+ $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
}
);
$table.find( 'tr:eq(2) td:eq(1)' ).attr( 'rowspan', '3' );
$table.tablesorter();
- $table.find( '.headerSort:eq(0)' ).click();
+ $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
}
);
tableTest(
$table.find( 'tr:eq(2) td:eq(0)' ).attr( 'rowspan', '3' );
$table.tablesorter();
- $table.find( '.headerSort:eq(0)' ).click();
+ $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
}
);
mw.config.set( 'wgDefaultDateFormat', 'mdy' );
$table.tablesorter();
- $table.find( '.headerSort:eq(0)' ).click();
+ $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
}
);
currencySorted,
function ( $table ) {
$table.tablesorter();
- $table.find( '.headerSort:eq(0)' ).click();
+ $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
}
);
function ( $table ) {
$table.find( 'tr:last' ).addClass( 'sortbottom' );
$table.tablesorter();
- $table.find( '.headerSort:eq(0)' ).click();
+ $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
}
);
'</table>'
);
$table.tablesorter();
- $table.find( '.headerSort:eq(0)' ).click();
+ $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
assert.strictEqual(
$table.data( 'tablesorter' ).config.parsers[ 0 ].id,
'<tr><td data-sort-value="Cherry">Dolphin</td></tr>' +
'</tbody></table>'
);
- $table.tablesorter().find( '.headerSort:eq(0)' ).click();
+ $table.tablesorter().find( '.headerSort:eq(0)' ).trigger( 'click' );
data = [];
$table.find( 'tbody > tr' ).each( function ( i, tr ) {
'<tr><td><span data-sort-value="D">H</span></td></tr>' +
'</tbody></table>'
);
- $table.tablesorter().find( '.headerSort:eq(0)' ).click();
+ $table.tablesorter().find( '.headerSort:eq(0)' ).trigger( 'click' );
data = [];
$table.find( 'tbody > tr' ).each( function ( i, tr ) {
// initialize table sorter and sort once
$table
.tablesorter()
- .find( '.headerSort:eq(0)' ).click();
+ .find( '.headerSort:eq(0)' ).trigger( 'click' );
// Change the sortValue data properties (T40152)
// - change data
$table.find( 'td:contains(G)' ).removeData( 'sortValue' );
// Now sort again (twice, so it is back at Ascending)
- $table.find( '.headerSort:eq(0)' ).click();
- $table.find( '.headerSort:eq(0)' ).click();
+ $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
+ $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
data = [];
$table.find( 'tbody > tr' ).each( function ( i, tr ) {
[ 'Numbers' ], numbers, numbersAsc,
function ( $table ) {
$table.tablesorter();
- $table.find( '.headerSort:eq(0)' ).click();
+ $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
}
);
[ 'Numbers' ], numbers, reversed( numbersAsc ),
function ( $table ) {
$table.tablesorter();
- $table.find( '.headerSort:eq(0)' ).click().click();
+ $table.find( '.headerSort:eq(0)' ).trigger( 'click' ).trigger( 'click' );
}
);
// TODO add numbers sorting tests for T10115 with a different language
mw.config.set( 'wgDefaultDateFormat', 'mdy' );
$table.tablesorter();
- $table.find( '.headerSort:eq(0)' ).click();
+ $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
}
);
mw.config.set( 'wgDefaultDateFormat', 'dmy' );
$table.tablesorter();
- $table.find( '.headerSort:eq(0)' ).click();
+ $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
}
);
mw.config.set( 'wgDefaultDateFormat', 'dmy' );
$table.tablesorter();
- $table.find( '.headerSort:eq(0)' ).click();
+ $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
}
);
'<tr><td>1</td></tr>' +
'</table>'
);
- $table.tablesorter().find( '.headerSort:eq(0)' ).click();
+ $table.tablesorter().find( '.headerSort:eq(0)' ).trigger( 'click' );
assert.strictEqual(
$table.find( 'td' ).first().text(),
'<tr><td><img alt="A" />C</tr>' +
'</table>'
);
- $table.tablesorter().find( '.headerSort:eq(0)' ).click();
+ $table.tablesorter().find( '.headerSort:eq(0)' ).trigger( 'click' );
assert.strictEqual(
$table.find( 'td' ).text(),
'<tr><td>4</td></tr>' +
'</table>'
);
- $table.tablesorter().find( '.headerSort:eq(0)' ).click();
+ $table.tablesorter().find( '.headerSort:eq(0)' ).trigger( 'click' );
assert.strictEqual(
$table.find( 'td' ).text(),
'</table>'
);
$table.tablesorter();
- $table.find( '.headerSort:eq(0)' ).click();
+ $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
// now the first row have 2 columns
- $table.find( '.headerSort:eq(1)' ).click();
+ $table.find( '.headerSort:eq(1)' ).trigger( 'click' );
parsers = $table.data( 'tablesorter' ).config.parsers;
'</table>'
);
$table.tablesorter();
- $table.find( '.headerSort:eq(0)' ).click();
+ $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
assert.deepEqual(
tableExtract( $table ),
'</table>'
);
$table.tablesorter();
- $table.find( '.headerSort:eq(0)' ).click();
+ $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
assert.deepEqual(
tableExtract( $table ),
$options = $( '#namespace' ).find( 'option' );
$options.eq( 0 ).removeProp( 'selected' );
$options.eq( 1 ).prop( 'selected', true );
- $( '#namespace' ).change();
+ $( '#namespace' ).trigger( 'change' );
// ... and checkboxes should be enabled again
assert.strictEqual( $( '#nsinvert' ).prop( 'disabled' ), false );
// select first option ( 'all' namespace)...
$options.eq( 1 ).removeProp( 'selected' );
$options.eq( 0 ).prop( 'selected', true );
- $( '#namespace' ).change();
+ $( '#namespace' ).trigger( 'change' );
// ... and checkboxes should now be disabled
assert.strictEqual( $( '#nsinvert' ).prop( 'disabled' ), true );
assert.strictEqual( $tocList.is( ':hidden' ), false, 'The table of contents is now visible' );
- $toggleLink.click();
+ $toggleLink.trigger( 'click' );
return $tocList.promise().then( function () {
assert.strictEqual( $tocList.is( ':hidden' ), true, 'The table of contents is now hidden' );
- $toggleLink.click();
+ $toggleLink.trigger( 'click' );
return $tocList.promise();
} );
} );