"disallowKeywordsOnNewLine": null,
"disallowQuotedKeysInObjects": null,
+ "disallowImplicitTypeConversion": null,
+ "requireLineBreakAfterVariableAssignment": null,
+ "requireSpaceAfterLineComment": null,
+ "requireSpacesInsideParentheses": null,
"requireSpacesInsideArrayBrackets": null,
"validateIndentation": null
}
background with white fallback color, rather than just white background.
* MediaWikiBagOStuff class removed, make sure any object cache config
uses SqlBagOStuff instead.
+* The 'daemonized' flag must be set to true in $wgJobTypeConf for any redis
+ job queues. This means that mediawiki/services/jobrunner service has to
+ be installed and running for any such queues to work.
=== New features in 1.25 ===
* (T64861) Updated plural rules to CLDR 26. Includes incompatible changes
'ImageQueryPage' => __DIR__ . '/includes/specialpage/ImageQueryPage.php',
'ImportReporter' => __DIR__ . '/includes/specials/SpecialImport.php',
'ImportSiteScripts' => __DIR__ . '/maintenance/importSiteScripts.php',
+ 'ImportSource' => __DIR__ . '/includes/Import.php',
'ImportStreamSource' => __DIR__ . '/includes/Import.php',
'ImportStringSource' => __DIR__ . '/includes/Import.php',
'ImportTitleFactory' => __DIR__ . '/includes/title/ImportTitleFactory.php',
'MediaHandler' => __DIR__ . '/includes/media/MediaHandler.php',
'MediaStatisticsPage' => __DIR__ . '/includes/specials/SpecialMediaStatistics.php',
'MediaTransformError' => __DIR__ . '/includes/media/MediaTransformOutput.php',
+ 'MediaTransformInvalidParametersException' => __DIR__ . '/includes/media/MediaTransformInvalidParametersException.php',
'MediaTransformOutput' => __DIR__ . '/includes/media/MediaTransformOutput.php',
'MediaWiki' => __DIR__ . '/includes/MediaWiki.php',
'MediaWikiI18N' => __DIR__ . '/includes/skins/MediaWikiI18N.php',
'MessageBlobStore' => __DIR__ . '/includes/MessageBlobStore.php',
'MessageCache' => __DIR__ . '/includes/cache/MessageCache.php',
'MessageContent' => __DIR__ . '/includes/content/MessageContent.php',
+ 'MessageSpecifier' => __DIR__ . '/includes/libs/MessageSpecifier.php',
'MigrateUserGroup' => __DIR__ . '/maintenance/migrateUserGroup.php',
'MimeMagic' => __DIR__ . '/includes/MimeMagic.php',
'MinifyScript' => __DIR__ . '/maintenance/minify.php',
'RebuildSitesCache' => __DIR__ . '/maintenance/rebuildSitesCache.php',
'RebuildTextIndex' => __DIR__ . '/maintenance/rebuildtextindex.php',
'RecentChange' => __DIR__ . '/includes/changes/RecentChange.php',
+ 'RecentChangesUpdateJob' => __DIR__ . '/includes/jobqueue/jobs/RecentChangesUpdateJob.php',
'RecompressTracked' => __DIR__ . '/maintenance/storage/recompressTracked.php',
'RedirectSpecialArticle' => __DIR__ . '/includes/specialpage/RedirectSpecialPage.php',
'RedirectSpecialPage' => __DIR__ . '/includes/specialpage/RedirectSpecialPage.php',
'StatCounter' => __DIR__ . '/includes/StatCounter.php',
'StatsOutput' => __DIR__ . '/maintenance/language/StatOutputs.php',
'Status' => __DIR__ . '/includes/Status.php',
+ 'StatusValue' => __DIR__ . '/includes/libs/StatusValue.php',
'StorageTypeStats' => __DIR__ . '/maintenance/storage/storageTypeStats.php',
'StoreFileOp' => __DIR__ . '/includes/filebackend/FileOp.php',
'StreamFile' => __DIR__ . '/includes/StreamFile.php',
},
"require-dev": {
"justinrainbow/json-schema": "~1.3",
- "phpunit/phpunit": "*"
+ "phpunit/phpunit": "~4.5"
},
"suggest": {
"ext-fileinfo": "*",
"Unlicense"
]
},
+ "ResourceFileModulePaths": {
+ "type": "object",
+ "description": "Default paths to use for all ResourceLoader file modules",
+ "additionalProperties": false,
+ "properties": {
+ "localBasePath": {
+ "type": "string",
+ "description": "Base path to prepend to all local paths, relative to current directory"
+ },
+ "remoteExtPath": {
+ "type": "string",
+ "description": "Base path to prepend to all remote paths, relative to $wgExtensionAssetsPath"
+ },
+ "remoteSkinPath": {
+ "type": "string",
+ "description": "Base path to prepend to all remote paths, relative to $wgStylePath"
+ }
+ }
+ },
"ResourceLoaderModules": {
"type": "object",
"description": "ResourceLoader modules to register",
or request state must be added through MakeGlobalVariablesScript instead.
&$vars: array( variable name => value )
+'ResourceLoaderGetLessVars': Called in ResourceLoader::getLessVars after variables
+from $wgResourceLoaderLESSVars are added. Can be used to add context-based variables.
+&$lessVars: array of variables already added
+
'ResourceLoaderRegisterModules': Right before modules information is required,
such as when responding to a resource
loader request or generating HTML output.
header( 'Cache-Control: no-cache' );
header( 'Content-Type: text/html; charset=utf-8' );
echo <<<ENDS
+<!DOCTYPE html>
<html>
+<head>
+<meta charset="UTF-8" />
+<title>$msgHdr</title>
+</head>
<body>
<h1>$msgHdr</h1>
<p>$detailMsg</p>
*
* Changes to LESS variables do not trigger cache invalidation.
*
+ * If the LESS variables need to be dynamic, you can use the
+ * ResourceLoaderGetLessVars hook (since 1.25).
+ *
* @par Example:
* @code
* $wgResourceLoaderLESSVars = array(
'AssembleUploadChunks' => 'AssembleUploadChunksJob',
'PublishStashedFile' => 'PublishStashedFileJob',
'ThumbnailRender' => 'ThumbnailRenderJob',
+ 'recentChangesUpdate' => 'RecentChangesUpdateJob',
'null' => 'NullJob'
);
/**
* Creates an ImportXMLReader drawing from the source provided
- * @param ImportStreamSource $source
+ * @param ImportSource $source
* @param Config $config
*/
- function __construct( ImportStreamSource $source, Config $config = null ) {
+ function __construct( ImportSource $source, Config $config = null ) {
$this->reader = new XMLReader();
if ( !$config ) {
wfDeprecated( __METHOD__ . ' without a Config instance', '1.25' );
private $mPosition;
/**
- * @param ImportStreamSource $source
+ * @param ImportSource $source
* @return string
*/
- static function registerSource( ImportStreamSource $source ) {
+ static function registerSource( ImportSource $source ) {
$id = wfRandomString();
self::$sourceRegistrations[$id] = $source;
}
+/**
+ * Source interface for XML import.
+ */
+interface ImportSource {
+
+ /**
+ * Indicates whether the end of the input has been reached.
+ * Will return true after a finite number of calls to readChunk.
+ *
+ * @return bool true if there is no more input, false otherwise.
+ */
+ function atEnd();
+
+ /**
+ * Return a chunk of the input, as a (possibly empty) string.
+ * When the end of input is reached, readChunk() returns false.
+ * If atEnd() returns false, readChunk() will return a string.
+ * If atEnd() returns true, readChunk() will return false.
+ *
+ * @return bool|string
+ */
+ function readChunk();
+}
+
/**
* Used for importing XML dumps where the content of the dump is in a string.
* This class is ineffecient, and should only be used for small dumps.
*
* @ingroup SpecialPage
*/
-class ImportStringSource {
+class ImportStringSource implements ImportSource {
function __construct( $string ) {
$this->mString = $string;
$this->mRead = false;
* Imports a XML dump from a file (either from file upload, files on disk, or HTTP)
* @ingroup SpecialPage
*/
-class ImportStreamSource {
+class ImportStreamSource implements ImportSource {
function __construct( $handle ) {
$this->mHandle = $handle;
}
*
* @since 1.17
*/
-class Message {
+class Message implements MessageSpecifier {
/**
* In which language to get this message. True, which is the default,
* Returns the message key.
*
* If a list of multiple possible keys was supplied to the constructor, this method may
- * return any of these keys. After the message ahs been fetched, this method will return
+ * return any of these keys. After the message has been fetched, this method will return
* the key that was actually used to fetch the message.
*
* @since 1.21
* so that a lack of error-handling will be explicit.
*/
class Status {
- /** @var bool */
- public $ok = true;
+ /** @var StatusValue */
+ protected $sv;
/** @var mixed */
public $value;
-
- /** Counters for batch operations */
- /** @var int */
+ /** @var array Map of (key => bool) to indicate success of each part of batch operations */
+ public $success = array();
+ /** @var int Counter for batch operations */
public $successCount = 0;
-
- /** @var int */
+ /** @var int Counter for batch operations */
public $failCount = 0;
- /** Array to indicate which items of the batch operations were successful */
- /** @var array */
- public $success = array();
-
- /** @var array */
- public $errors = array();
-
/** @var callable */
public $cleanCallback = false;
+ /**
+ * @param StatusValue $sv [optional]
+ */
+ public function __construct( StatusValue $sv = null ) {
+ $this->sv = ( $sv === null ) ? new StatusValue() : $sv;
+ // B/C field aliases
+ $this->value =& $this->sv->value;
+ $this->successCount =& $this->sv->successCount;
+ $this->failCount =& $this->sv->failCount;
+ $this->success =& $this->sv->success;
+ }
+
+ /**
+ * Succinct helper method to wrap a StatusValue
+ *
+ * This is is useful when formatting StatusValue objects:
+ * <code>
+ * $this->getOutput()->addHtml( Status::wrap( $sv )->getHTML() );
+ * </code>
+ *
+ * @param StatusValue|Status $sv
+ * @return Status
+ */
+ public static function wrap( $sv ) {
+ return $sv instanceof Status ? $sv : new self( $sv );
+ }
+
/**
* Factory function for fatal errors
*
* @param string|Message $message Message name or object
* @return Status
*/
- static function newFatal( $message /*, parameters...*/ ) {
- $params = func_get_args();
- $result = new self;
- call_user_func_array( array( &$result, 'error' ), $params );
- $result->ok = false;
- return $result;
+ public static function newFatal( $message /*, parameters...*/ ) {
+ return new self( call_user_func_array(
+ array( 'StatusValue', 'newFatal' ), func_get_args()
+ ) );
}
/**
* @param mixed $value
* @return Status
*/
- static function newGood( $value = null ) {
- $result = new self;
- $result->value = $value;
- return $result;
+ public static function newGood( $value = null ) {
+ $sv = new StatusValue();
+ $sv->value = $value;
+
+ return new self( $sv );
}
/**
* @param mixed $value
*/
public function setResult( $ok, $value = null ) {
- $this->ok = $ok;
- $this->value = $value;
+ $this->sv->setResult( $ok, $value );
}
/**
* @return bool
*/
public function isGood() {
- return $this->ok && !$this->errors;
+ return $this->sv->isGood();
}
/**
* @return bool
*/
public function isOK() {
- return $this->ok;
+ return $this->sv->isOK();
}
/**
* @param string|Message $message Message name or object
*/
public function warning( $message /*, parameters... */ ) {
- $params = array_slice( func_get_args(), 1 );
- $this->errors[] = array(
- 'type' => 'warning',
- 'message' => $message,
- 'params' => $params );
+ call_user_func_array( array( $this->sv, 'warning' ), func_get_args() );
}
/**
* @param string|Message $message Message name or object
*/
public function error( $message /*, parameters... */ ) {
- $params = array_slice( func_get_args(), 1 );
- $this->errors[] = array(
- 'type' => 'error',
- 'message' => $message,
- 'params' => $params );
+ call_user_func_array( array( $this->sv, 'error' ), func_get_args() );
}
/**
* @param string|Message $message Message name or object
*/
public function fatal( $message /*, parameters... */ ) {
- $params = array_slice( func_get_args(), 1 );
- $this->errors[] = array(
- 'type' => 'error',
- 'message' => $message,
- 'params' => $params );
- $this->ok = false;
- }
-
- /**
- * Don't save the callback when serializing, because Closures can't be
- * serialized and we're going to clear it in __wakeup anyway.
- */
- public function __sleep() {
- $keys = array_keys( get_object_vars( $this ) );
- return array_diff( $keys, array( 'cleanCallback' ) );
- }
-
- /**
- * Sanitize the callback parameter on wakeup, to avoid arbitrary execution.
- */
- public function __wakeup() {
- $this->cleanCallback = false;
+ call_user_func_array( array( $this->sv, 'fatal' ), func_get_args() );
}
/**
* @param array $params
* @return array
*/
- protected function cleanParams( $params ) {
+ protected function cleanParams( array $params ) {
if ( !$this->cleanCallback ) {
return $params;
}
* @return string
*/
public function getWikiText( $shortContext = false, $longContext = false ) {
- if ( count( $this->errors ) == 0 ) {
- if ( $this->ok ) {
- $this->fatal( 'internalerror_info',
+ $rawErrors = $this->sv->getErrors();
+ if ( count( $rawErrors ) == 0 ) {
+ if ( $this->sv->isOK() ) {
+ $this->sv->fatal( 'internalerror_info',
__METHOD__ . " called for a good result, this is incorrect\n" );
} else {
- $this->fatal( 'internalerror_info',
+ $this->sv->fatal( 'internalerror_info',
__METHOD__ . ": Invalid result object: no error text but not OK\n" );
}
+ $rawErrors = $this->sv->getErrors(); // just added a fatal
}
- if ( count( $this->errors ) == 1 ) {
- $s = $this->getErrorMessage( $this->errors[0] )->plain();
+ if ( count( $rawErrors ) == 1 ) {
+ $s = $this->getErrorMessage( $rawErrors[0] )->plain();
if ( $shortContext ) {
$s = wfMessage( $shortContext, $s )->plain();
} elseif ( $longContext ) {
$s = wfMessage( $longContext, "* $s\n" )->plain();
}
} else {
- $errors = $this->getErrorMessageArray( $this->errors );
+ $errors = $this->getErrorMessageArray( $rawErrors );
foreach ( $errors as &$error ) {
$error = $error->plain();
}
* @return Message
*/
public function getMessage( $shortContext = false, $longContext = false ) {
- if ( count( $this->errors ) == 0 ) {
- if ( $this->ok ) {
- $this->fatal( 'internalerror_info',
+ $rawErrors = $this->sv->getErrors();
+ if ( count( $rawErrors ) == 0 ) {
+ if ( $this->sv->isOK() ) {
+ $this->sv->fatal( 'internalerror_info',
__METHOD__ . " called for a good result, this is incorrect\n" );
} else {
- $this->fatal( 'internalerror_info',
+ $this->sv->fatal( 'internalerror_info',
__METHOD__ . ": Invalid result object: no error text but not OK\n" );
}
+ $rawErrors = $this->sv->getErrors(); // just added a fatal
}
- if ( count( $this->errors ) == 1 ) {
- $s = $this->getErrorMessage( $this->errors[0] );
+ if ( count( $rawErrors ) == 1 ) {
+ $s = $this->getErrorMessage( $rawErrors[0] );
if ( $shortContext ) {
$s = wfMessage( $shortContext, $s );
} elseif ( $longContext ) {
$s = wfMessage( $longContext, $wrapper );
}
} else {
- $msgs = $this->getErrorMessageArray( $this->errors );
+ $msgs = $this->getErrorMessageArray( $rawErrors );
$msgCount = count( $msgs );
if ( $shortContext ) {
* @param bool $overwriteValue Whether to override the "value" member
*/
public function merge( $other, $overwriteValue = false ) {
- $this->errors = array_merge( $this->errors, $other->errors );
- $this->ok = $this->ok && $other->ok;
- if ( $overwriteValue ) {
- $this->value = $other->value;
- }
- $this->successCount += $other->successCount;
- $this->failCount += $other->failCount;
+ $this->sv->merge( $other->sv, $overwriteValue );
}
/**
*
* @return array A list in which each entry is an array with a message key as its first element.
* The remaining array elements are the message parameters.
+ * @deprecated 1.25
*/
public function getErrorsArray() {
- return $this->getStatusArray( "error" );
+ return $this->getStatusArray( 'error' );
}
/**
*
* @return array A list in which each entry is an array with a message key as its first element.
* The remaining array elements are the message parameters.
+ * @deprecated 1.25
*/
public function getWarningsArray() {
- return $this->getStatusArray( "warning" );
+ return $this->getStatusArray( 'warning' );
}
/**
* Returns a list of status messages of the given type (or all if false)
+ *
+ * @note: this handles RawMessage poorly
+ *
* @param string $type
* @return array
*/
protected function getStatusArray( $type = false ) {
$result = array();
- foreach ( $this->errors as $error ) {
+
+ foreach ( $this->sv->getErrors() as $error ) {
if ( $type === false || $error['type'] === $type ) {
- if ( $error['message'] instanceof Message ) {
+ if ( $error['message'] instanceof MessageSpecifier ) {
$result[] = array_merge(
array( $error['message']->getKey() ),
$error['message']->getParams()
* @return array
*/
public function getErrorsByType( $type ) {
- $result = array();
- foreach ( $this->errors as $error ) {
- if ( $error['type'] === $type ) {
- $result[] = $error;
- }
- }
- return $result;
+ return $this->sv->getErrorsByType( $type );
}
/**
* @return bool
*/
public function hasMessage( $message ) {
- if ( $message instanceof Message ) {
- $message = $message->getKey();
- }
- foreach ( $this->errors as $error ) {
- if ( $error['message'] instanceof Message
- && $error['message']->getKey() === $message
- ) {
- return true;
- } elseif ( $error['message'] === $message ) {
- return true;
- }
- }
- return false;
+ return $this->sv->hasMessage( $message );
}
/**
* @return bool Return true if the replacement was done, false otherwise.
*/
public function replaceMessage( $source, $dest ) {
- $replaced = false;
- foreach ( $this->errors as $index => $error ) {
- if ( $error['message'] === $source ) {
- $this->errors[$index]['message'] = $dest;
- $replaced = true;
- }
- }
- return $replaced;
+ return $this->sv->replaceMessage( $source, $dest );
}
/**
* @return mixed
*/
public function getValue() {
- return $this->value;
+ return $this->sv->getValue();
}
/**
- * @return string
+ * Backwards compatibility logic
+ *
+ * @param string $name
*/
- public function __toString() {
- $status = $this->isOK() ? "OK" : "Error";
- if ( count( $this->errors ) ) {
- $errorcount = "collected " . ( count( $this->errors ) ) . " error(s) on the way";
- } else {
- $errorcount = "no errors detected";
+ function __get( $name ) {
+ if ( $name === 'ok' ) {
+ return $this->sv->getOK();
+ } elseif ( $name === 'errors' ) {
+ return $this->sv->getErrors();
}
- if ( isset( $this->value ) ) {
- $valstr = gettype( $this->value ) . " value set";
- if ( is_object( $this->value ) ) {
- $valstr .= "\"" . get_class( $this->value ) . "\" instance";
- }
+ throw new Exception( "Cannot get '$name' property." );
+ }
+
+ /**
+ * Backwards compatibility logic
+ *
+ * @param string $name
+ * @param mixed $value
+ */
+ function __set( $name, $value ) {
+ if ( $name === 'ok' ) {
+ $this->sv->setOK( $value );
+ } elseif ( !property_exists( $this, $name ) ) {
+ // Caller is using undeclared ad-hoc properties
+ $this->$name = $value;
} else {
- $valstr = "no value set";
+ throw new Exception( "Cannot set '$name' property." );
}
- $out = sprintf( "<%s, %s, %s>",
- $status,
- $errorcount,
- $valstr
- );
- if ( count( $this->errors ) > 0 ) {
- $hdr = sprintf( "+-%'-4s-+-%'-25s-+-%'-40s-+\n", "", "", "" );
- $i = 1;
- $out .= "\n";
- $out .= $hdr;
- foreach ( $this->getStatusArray() as $stat ) {
- $out .= sprintf( "| %4d | %-25.25s | %-40.40s |\n",
- $i,
- $stat[0],
- implode( " ", array_slice( $stat, 1 ) )
- );
- $i += 1;
- }
- $out .= $hdr;
- };
- return $out;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString() {
+ return $this->sv->__toString();
+ }
+
+ /**
+ * Don't save the callback when serializing, because Closures can't be
+ * serialized and we're going to clear it in __wakeup anyway.
+ */
+ function __sleep() {
+ $keys = array_keys( get_object_vars( $this ) );
+ return array_diff( $keys, array( 'cleanCallback' ) );
+ }
+
+ /**
+ * Sanitize the callback parameter on wakeup, to avoid arbitrary execution.
+ */
+ function __wakeup() {
+ $this->cleanCallback = false;
}
}
/**
* Replaces User::addUserGroup()
* @param string $group
+ *
+ * @return bool
*/
function addGroup( $group ) {
$this->db->insert( 'user_groups',
),
__METHOD__,
array( 'IGNORE' ) );
+
+ return true;
}
/**
* Replaces User::removeUserGroup()
* @param string $group
+ *
+ * @return bool
*/
function removeGroup( $group ) {
$this->db->delete( 'user_groups',
'ug_group' => $group,
),
__METHOD__ );
+
+ return true;
}
/**
"apihelp-createaccount-param-mailpassword": "Pokud je nastaveno na libovolnou hodnotu, zašle se náhodně vygenerované heslo na e-mail uživatele.",
"apihelp-createaccount-example-mail": "Vytvořit uživatele <kbd>testmailuser</kbd> a zaslat mu e-mail s náhodně vygenerovaným heslem.",
"apihelp-delete-description": "Smazat stránku.",
+ "apihelp-disabled-description": "Tento modul byl deaktivován.",
"apihelp-edit-description": "Vytvářet a upravovat stránky.",
+ "apihelp-edit-param-sectiontitle": "Název nové sekce.",
"apihelp-edit-param-text": "Obsah stránky.",
"apihelp-edit-param-minor": "Malá editace.",
"apihelp-edit-param-notminor": "Nemalá editace.",
"apihelp-edit-param-watchlist": "Bezpodmíněnečně přidat nebo odstranit stránku ze sledovaných stránek aktuálního uživatele, použít nastavení nebo neměnit sledování.",
"apihelp-edit-param-redirect": "Automaticky opravit přesměrování.",
"apihelp-edit-example-edit": "Upravit stránku.",
+ "apihelp-emailuser-description": "Poslat uživateli e-mail.",
+ "apihelp-emailuser-param-text": "Tělo zprávy.",
+ "apihelp-emailuser-param-ccme": "Odeslat mi kopii této zprávy.",
"apihelp-feedcontributions-param-year": "Od roku (a dříve).",
"apihelp-feedcontributions-param-month": "Od měsíce (a dříve)",
"apihelp-feedcontributions-param-deletedonly": "Zobrazit pouze smazané příspěvky.",
"apihelp-feedrecentchanges-param-target": "Zobrazit jen změny na stránkách odkazovaných z této stránky.",
"apihelp-feedrecentchanges-example-simple": "Zobrazit poslední změny.",
"apihelp-feedrecentchanges-example-30days": "Zobrazit poslední změny za 30 dní.",
+ "apihelp-filerevert-description": "Revertovat soubor na starší verzi.",
+ "apihelp-filerevert-param-filename": "Cílový název souboru, bez prefixu Soubor:",
"apihelp-filerevert-param-comment": "Vložit komentář.",
"apihelp-help-description": "Zobrazuje nápovědu k uvedeným modulům.",
"apihelp-help-param-modules": "Moduly, pro které se má zobrazit nápověda (hodnoty parametrů action= a format= nebo „main“). Submoduly lze zadávat pomocí „+“.",
"apihelp-login-param-name": "Uživatelské jméno.",
"apihelp-login-param-password": "Heslo.",
"apihelp-login-example-login": "Přihlášení",
+ "apihelp-logout-example-logout": "Odhlášení aktuálního uživatele.",
"apihelp-move-description": "Přesunout stránku.",
"apihelp-move-param-reason": "Důvod k přejmenování.",
"apihelp-move-param-movetalk": "Přejmenovat diskuzní stránku, pokud existuje.",
"apihelp-move-param-watch": "Přidat stránku a přesměrování do sledovaných stránek aktuálního uživatele.",
"apihelp-move-param-unwatch": "Odstranit stránku a přesměrování ze sledovaných stránek současného uživatele.",
"apihelp-move-param-ignorewarnings": "Ignorovat všechna varování.",
+ "apihelp-opensearch-param-search": "Hledání řetězce.",
+ "apihelp-opensearch-param-limit": "Maximální počet vrácených výsledků",
"apihelp-opensearch-param-namespace": "Jmenné prostory pro vyhledávání.",
"apihelp-opensearch-param-format": "Formát výstupu.",
"apihelp-opensearch-example-te": "Najít stránky, začínající s <kbd>Te</kbd>.",
"apihelp-main-param-format": "出力する形式です。",
"apihelp-main-param-smaxage": "<code>s-maxage</code> ヘッダーにこの秒数を設定します。エラーがキャッシュされることはありません。",
"apihelp-main-param-maxage": "<code>max-age</code> ヘッダーにこの秒数を設定します。エラーがキャッシュされることはありません。",
- "apihelp-main-param-assert": "\"user\" を設定した場合は利用者がログイン済みかどうかを、\"bot\" を指定した場合はボット権限があるかどうかを、それぞれ検証します。",
+ "apihelp-main-param-assert": "<kbd>user</kbd> を設定した場合は利用者がログイン済みかどうかを、<kbd>bot</kbd> を指定した場合はボット権限があるかどうかを、それぞれ検証します。",
"apihelp-main-param-requestid": "任意の値を指定でき、その値が結果に含められます。リクエストを識別するために使用できます。",
"apihelp-main-param-servedby": "リクエストを処理したホスト名を結果に含めます。",
"apihelp-main-param-curtimestamp": "現在のタイムスタンプを結果に含めます。",
"apihelp-block-description": "利用者をブロックします。",
"apihelp-block-param-user": "ブロックする利用者名、IPアドレスまたはIPレンジ。",
"apihelp-block-param-reason": "ブロックの理由。",
- "apihelp-block-param-anononly": "匿名利用者のみブロックします(つまり、このIPからの匿名での編集を不可能にします)。",
+ "apihelp-block-param-anononly": "å\8c¿å\90\8då\88©ç\94¨è\80\85ã\81®ã\81¿ã\83\96ã\83ã\83\83ã\82¯ã\81\97ã\81¾ã\81\99ï¼\88ã\81¤ã\81¾ã\82\8aã\80\81ã\81\93ã\81®IPã\82¢ã\83\89ã\83¬ã\82¹ã\81\8bã\82\89ã\81®å\8c¿å\90\8dã\81§ã\81®ç·¨é\9b\86ã\82\92ä¸\8då\8f¯è\83½ã\81«ã\81\97ã\81¾ã\81\99ï¼\89ã\80\82",
"apihelp-block-param-nocreate": "アカウントの作成を禁止します。",
"apihelp-block-param-autoblock": "その利用者が最後に使用したIPアドレスと、ブロック後に編集を試みた際のIPアドレスを自動的にブロックします。",
- "apihelp-block-param-noemail": "Wikiを通して電子メールを送信することを禁止します。(\"blockemail\" 権限が必要です)",
- "apihelp-block-param-hidename": "ブロック記録から利用者名を秘匿します。(\"hideuser\" 権限が必要です)",
+ "apihelp-block-param-noemail": "Wikiを通して電子メールを送信することを禁止します。(<code>blockemail</code> 権限が必要です)",
+ "apihelp-block-param-hidename": "ブロック記録から利用者名を秘匿します。(<code>hideuser</code> 権限が必要です)",
"apihelp-block-param-reblock": "その利用者がすでにブロックされている場合、ブロックを上書きします。",
- "apihelp-block-param-watchuser": "その利用者またはIPの利用者ページとトークページをウォッチします。",
- "apihelp-block-example-ip-simple": "IP 192.0.2.5 を \"First strike\" という理由で3日ブロックする",
- "apihelp-block-example-user-complex": "利用者 \"Vandal\" を \"Vandalism\" という理由で無期限ブロックし、新たなアカウント作成とメールの送信を禁止する。",
+ "apihelp-block-param-watchuser": "ã\81\9dã\81®å\88©ç\94¨è\80\85ã\81¾ã\81\9fã\81¯IPã\82¢ã\83\89ã\83¬ã\82¹ã\81®å\88©ç\94¨è\80\85ã\83\9aã\83¼ã\82¸ã\81¨ã\83\88ã\83¼ã\82¯ã\83\9aã\83¼ã\82¸ã\82\92ã\82¦ã\82©ã\83\83ã\83\81ã\81\97ã\81¾ã\81\99ã\80\82",
+ "apihelp-block-example-ip-simple": "IPアドレス <kbd>192.0.2.5</kbd> を <kbd>First strike<kbd> という理由で3日ブロックする",
+ "apihelp-block-example-user-complex": "利用者 <kbd>Vandal</kbd> を <kbd>Vandalism</kbd> という理由で無期限ブロックし、新たなアカウント作成とメールの送信を禁止する。",
"apihelp-createaccount-description": "新しい利用者アカウントを作成します。",
"apihelp-createaccount-param-name": "利用者名。",
"apihelp-createaccount-param-password": "パスワード (<var>$1mailpassword</var> が設定されると無視されます)。",
"apihelp-createaccount-param-token": "最初のリクエストで得られたアカウント作成用トークンです。",
"apihelp-createaccount-param-email": "利用者の電子メールアドレス (任意)。",
"apihelp-createaccount-param-mailpassword": "設定されると (その値を問わず)、ランダムなパスワードがその利用者に電子メールで送られます。",
- "apihelp-createaccount-example-mail": "利用者「testuser」を作成し、ランダムに生成されたパスワードをメールで送る",
+ "apihelp-createaccount-example-mail": "利用者 <kbd>testuser</kbd>を作成し、ランダムに生成されたパスワードをメールで送る",
"apihelp-delete-description": "ページを削除します。",
- "apihelp-delete-param-title": "削除するページ名です。 $1pageid とは同時に使用できません。",
- "apihelp-delete-param-pageid": "削除するページIDです。 $1title とは同時に使用できません。",
+ "apihelp-delete-param-title": "削除するページ名です。<var>$1pageid</var> とは同時に使用できません。",
+ "apihelp-delete-param-pageid": "削除するページIDです。<var>$1title</var> とは同時に使用できません。",
"apihelp-delete-param-reason": "削除の理由です。入力しない場合、自動的に生成された理由が使用されます。",
"apihelp-delete-param-watch": "そのページをウォッチリストに追加します。",
"apihelp-delete-param-unwatch": "そのページをウォッチリストから除去します。",
- "apihelp-delete-example-simple": "「Main Page」を削除する",
- "apihelp-delete-example-reason": "\"Preparing for move\" という理由で Main Page を削除する",
+ "apihelp-delete-example-simple": "<kbd>Main Page</kbd> を削除する",
+ "apihelp-delete-example-reason": "<kbd>Preparing for move</kbd> という理由で <kbd>Main Page</kbd> を削除する",
"apihelp-disabled-description": "このモジュールは無効化されています。",
"apihelp-edit-description": "ページを作成、編集します。",
- "apihelp-edit-param-title": "編集するページ名です。$1pageid とは同時に使用できません。",
- "apihelp-edit-param-pageid": "編集するページIDです。$1title とは同時に使用できません。",
+ "apihelp-edit-param-title": "編集するページ名です。<var>$1pageid</var> とは同時に使用できません。",
+ "apihelp-edit-param-pageid": "編集するページIDです。<var>$1title</var> とは同時に使用できません。",
"apihelp-edit-param-text": "ページの本文。",
"apihelp-edit-param-minor": "細部の編集",
"apihelp-edit-param-createonly": "すでにそのページが存在する場合は編集を行いません。",
"apihelp-emailuser-param-target": "送信先の利用者名。",
"apihelp-emailuser-param-text": "電子メールの本文。",
"apihelp-emailuser-param-ccme": "電子メールの複製を自分にも送信します。",
+ "apihelp-feedcontributions-example-simple": "利用者 <kbd>Example</kbd> の投稿記録を取得する。",
"apihelp-filerevert-example-revert": "<kbd>Wiki.png</kbd> を <kbd>2011-03-05T15:27:40Z</kbd> の版に差し戻す。",
"apihelp-help-description": "指定したモジュールのヘルプを表示します。",
- "apihelp-help-param-modules": "ヘルプを表示するモジュールです (action= パラメーターおよび format= パラメーターの値、または \"main\")。\"+\" を使用して下位モジュールを指定できます。",
+ "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-login-param-password": "パスワード。",
"apihelp-login-example-login": "ログイン",
"apihelp-move-description": "ページを移動します。",
- "apihelp-move-param-from": "移動するページのページ名です。 $1fromid とは同時に使用できません。",
- "apihelp-move-param-fromid": "移動するページのページIDです。 $1from とは同時に使用できません。",
+ "apihelp-move-param-from": "移動するページのページ名です。<var>$1fromid</var> とは同時に使用できません。",
+ "apihelp-move-param-fromid": "移動するページのページIDです。<var>$1from</var> とは同時に使用できません。",
"apihelp-move-param-to": "移動後のページ名。",
"apihelp-move-param-reason": "移動の理由。",
"apihelp-move-param-movetalk": "存在する場合、トークページも移動します。",
"apihelp-move-param-watch": "そのページと転送ページをウォッチリストに追加します。",
"apihelp-move-param-unwatch": "そのページと転送ページをウォッチリストから除去します。",
"apihelp-move-param-ignorewarnings": "あらゆる警告を無視",
- "apihelp-move-example-move": "「Badtitle」を「Goodtitle」に転送ページを残さず移動",
+ "apihelp-move-example-move": "<kbd>Badtitle</kbd> を <kbd>Goodtitle</kbd> に転送ページを残さず移動",
"apihelp-patrol-example-rcid": "最近の更新を巡回",
"apihelp-format-example-generic": "クエリの結果を $1 形式に整形します",
- "apihelp-dbg-description": "データを PHP の var_export() 形式で出力します。",
- "apihelp-dbgfm-description": "データを PHP の var_export() 形式 (HTML に埋め込んだ形式) で出力します。",
- "apihelp-dump-description": "データを PHP の var_dump() 形式で出力します。",
- "apihelp-dumpfm-description": "データを PHP の var_dump() 形式 (HTML に埋め込んだ形式) で出力します。",
+ "apihelp-dbg-description": "データを PHP の <code>var_export()</code> 形式で出力します。",
+ "apihelp-dbgfm-description": "データを PHP の <code>var_export()</code> 形式 (HTML に埋め込んだ形式) で出力します。",
+ "apihelp-dump-description": "データを PHP の <code>var_dump()</code> 形式で出力します。",
+ "apihelp-dumpfm-description": "データを PHP の <code>var_dump()</code> 形式 (HTML に埋め込んだ形式) で出力します。",
"apihelp-json-description": "データを JSON 形式で出力します。",
"apihelp-json-param-callback": "指定すると、指定した関数呼び出しで出力をラップします。安全のため、利用者固有のデータはすべて制限されます。",
"apihelp-json-param-utf8": "指定すると、大部分の非 ASCII 文字 (すべてではありません) を、16 進のエスケープ シーケンスに置換する代わりに UTF-8 として符号化します。",
"apihelp-php-description": "データを PHP のシリアル化した形式で出力します。",
"apihelp-phpfm-description": "データを PHP のシリアル化した形式 (HTML に埋め込んだ形式) で出力します。",
"apihelp-rawfm-description": "データをデバッグ要素付きで JSON 形式 (HTML に埋め込んだ形式) で出力します。",
- "apihelp-txt-description": "データを PHP の print_r() 形式で出力します。",
- "apihelp-txtfm-description": "データを PHP の print_r() 形式 (HTML に埋め込んだ形式) で出力します。",
+ "apihelp-txt-description": "データを PHP の <code>print_r()</code> 形式で出力します。",
+ "apihelp-txtfm-description": "データを PHP の <code>print_r()</code> 形式 (HTML に埋め込んだ形式) で出力します。",
"apihelp-wddx-description": "データを WDDX 形式で出力します。",
"apihelp-wddxfm-description": "データを WDDX 形式 (HTML に埋め込んだ形式) で出力します。",
"apihelp-xml-description": "データを XML 形式で出力します。",
"apihelp-yaml-description": "データを YAML 形式で出力します。",
"apihelp-yamlfm-description": "データを YAML 形式 (HTML に埋め込んだ形式) で出力します。",
"api-format-title": "MediaWiki API の結果",
- "api-format-prettyprint-header": "このページは $1 形式を HTML で表現したものです。HTML はデバッグに役立ちますが、アプリケーションでの使用には適していません。\n\nformat パラメーターを指定すると出力形式を変更できます 。$1 形式の非 HTML 版を閲覧するには、format=$2 を設定してください。\n\n詳細情報については [https://www.mediawiki.org/wiki/API 完全な説明文書]または [[Special:ApiHelp/main|API のヘルプ]]を参照してください。",
+ "api-format-prettyprint-header": "このページは $1 形式を HTML で表現したものです。HTML はデバッグに役立ちますが、アプリケーションでの使用には適していません。\n\n<var>format</var> パラメーターを指定すると出力形式を変更できます 。$1 形式の非 HTML 版を閲覧するには、format=$2 を設定してください。\n\n詳細情報については [[mw:API|完全な説明文書]]または [[Special:ApiHelp/main|API のヘルプ]]を参照してください。",
"api-help-title": "MediaWiki API ヘルプ",
"api-help-lead": "このページは自動生成された MediaWiki API の説明文書ページです。\n\n説明文書と例: https://www.mediawiki.org/wiki/API",
"api-help-main-header": "メイン モジュール",
"api-help-parameters": "{{PLURAL:$1|パラメーター}}:",
"api-help-param-deprecated": "廃止予定です。",
"api-help-param-required": "このパラメーターは必須です。",
- "api-help-param-list": "{{PLURAL:$1|1=値 (いずれか1つ)|2=値 (「{{!}}」で区切る)}}: $2",
+ "api-help-param-list": "{{PLURAL:$1|1=値 (いずれか1つ)|2=値 (<kbd>{{!}}</kbd>で区切る)}}: $2",
"api-help-param-list-can-be-empty": "{{PLURAL:$1|0=空欄にしてください|空欄にするか、または $2}}",
"api-help-param-integer-min": "{{PLURAL:$1|値}}は $2 以上にしてください。",
"api-help-param-integer-max": "{{PLURAL:$1|値}}は $3 以下にしてください。",
"api-help-param-integer-minmax": "{{PLURAL:$1|値}}は $2 以上 $3 以下にしてください。",
"api-help-param-upload": "multipart/form-data 形式でファイルをアップロードしてください。",
- "api-help-param-multi-separate": "複数の値は「|」で区切ってください。",
+ "api-help-param-multi-separate": "複数の値は <kbd>|</kbd> で区切ってください。",
"api-help-param-multi-max": "値の最大値は {{PLURAL:$1|$1}} (ボットの場合は {{PLURAL:$2|$2}}) です。",
"api-help-param-default": "既定値: $1",
"api-help-param-default-empty": "既定値: <span class=\"apihelp-empty\">(空)</span>",
--- /dev/null
+{
+ "@metadata": {
+ "authors": [
+ "Fasouzafreitas"
+ ]
+ },
+ "apihelp-main-param-requestid": "Qualquer valor dado aqui será incluído na resposta. Pode ser usado para distinguir requisições.",
+ "apihelp-block-description": "Bloquear um usuário",
+ "apihelp-block-param-user": "Nome de usuário, endereço IP ou faixa de IP para bloquear.",
+ "apihelp-feedrecentchanges-param-hidemyself": "Ocultar alterações feitas pelo usuário atual.",
+ "apihelp-feedrecentchanges-example-30days": "Mostrar as alterações recentes por 30 dias.",
+ "apihelp-move-param-movetalk": "Renomear a página de discussão, se existir.",
+ "apihelp-options-example-reset": "Resetar todas as preferências"
+}
"apihelp-expandtemplates-param-title": "Sidans rubrik.",
"apihelp-expandtemplates-param-text": "Wikitext att konvertera.",
"apihelp-expandtemplates-param-revid": "Revision ID, för <nowiki>{{REVISIONID}}</nowiki> och liknande variabler.",
+ "apihelp-expandtemplates-param-includecomments": "Om HTML-kommentarer skall inkluderas i utdata.",
"apihelp-expandtemplates-example-simple": "Expandera wikitexten <kbd><nowiki>{{Projekt:Sandbox}}</nowiki></kbd>.",
+ "apihelp-feedcontributions-param-namespace": "Vilken namnrymd att filtrera bidrag med.",
"apihelp-feedcontributions-param-year": "Från år (och tidigare).",
"apihelp-feedcontributions-param-month": "Från månad (och tidigare).",
"apihelp-feedcontributions-param-tagfilter": "Filtrera bidrag som har dessa taggar.",
"apihelp-feedcontributions-param-newonly": "Visa endast redigeringar där sidor skapas.",
"apihelp-feedcontributions-param-showsizediff": "Visa skillnaden i storlek mellan revisioner.",
"apihelp-feedcontributions-example-simple": "Returnera bidrag för [[User:Example]]",
+ "apihelp-feedrecentchanges-param-namespace": "Namnrymder att begränsa resultaten till.",
+ "apihelp-feedrecentchanges-param-invert": "Alla namnrymder utom den valda.",
"apihelp-feedrecentchanges-param-days": "Dagar att begränsa resultaten till.",
"apihelp-feedrecentchanges-param-limit": "Maximalt antal resultat att returnera.",
"apihelp-feedrecentchanges-param-from": "Visa förändringar sedan dess.",
"apihelp-feedwatchlist-param-linktosections": "Länka direkt till ändrade avsnitt om möjligt.",
"apihelp-filerevert-description": "Återställ en fil till en äldre version.",
"apihelp-filerevert-param-comment": "Ladda upp kommentar.",
+ "apihelp-filerevert-param-archivename": "Arkiv-namn för revisionen att gå tillbaka till.",
"apihelp-filerevert-example-revert": "Återställ <kbd>Wiki.png</kbd> till versionen från <kbd>2011-03-05T15:27:40Z</kbd>",
+ "apihelp-help-description": "Visa hjälp för de angivna modulerna.",
+ "apihelp-help-param-submodules": "Inkludera hjälp för undermoduler av den namngivna modulen.",
+ "apihelp-help-param-recursivesubmodules": "Inkludera hjälp för undermoduler rekursivt.",
+ "apihelp-help-param-helpformat": "Formatet för hjälp-utdata.",
+ "apihelp-help-param-toc": "Inkludera en innehållsförteckning i HTML-utdata.",
"apihelp-help-example-main": "Hjälp för huvudmodul",
"apihelp-help-example-recursive": "All hjälp på en sida",
"apihelp-help-example-help": "Hjälp för själva hjälpmodulen",
"apihelp-imagerotate-param-rotation": "Grader att rotera bild medurs.",
"apihelp-imagerotate-example-simple": "Rotera <kbd>File:Example.png</kbd> med <kbd>90</kbd> grader",
"apihelp-imagerotate-example-generator": "Rotera alla bilder i <kbd>Category:Flip</kbd> med <kbd>180</kbd> grader.",
+ "apihelp-import-description": "Importera en sida från en annan wiki, eller en XML fil. \n\nNotera att HTTP POST måste bli gjord som en fil uppladdning (d.v.s med multipart/form-data) när man skickar en fil för <var>xml</var> parametern.",
"apihelp-import-param-summary": "Importera sammanfattning.",
"apihelp-import-param-xml": "Uppladdad XML-fil.",
"apihelp-import-param-interwikisource": "För interwiki-importer: wiki som du vill importera från.",
"apihelp-login-example-login": "Logga in",
"apihelp-logout-description": "Logga ut och rensa sessionsdata.",
"apihelp-logout-example-logout": "Logga ut den aktuella användaren",
+ "apihelp-managetags-param-tag": "Tagg att skapa, radera, aktivera eller inaktivera. För skapande av tagg, får taggen inte existera. För raderande av tagg, så måste taggen existera. För aktiverande av tagg, taggen måste existera och inte användas i en förlängning. För tagg inaktivering, taggen måste användas just nu och vara manuellt definierat.",
+ "apihelp-managetags-param-reason": "En icke-obligatorisk orsak för att skapa, radera, aktivera, eller inaktivera taggen.",
+ "apihelp-managetags-param-ignorewarnings": "Om du vill ignorera varningar som utfärdas under operationen.",
"apihelp-move-description": "Flytta en sida.",
"apihelp-move-param-from": "Titeln på sidan du vill flytta. Kan inte användas tillsammans med <var>$1fromid</var>.",
"apihelp-move-param-to": "Titel att byta namn på sidan till.",
"apihelp-move-param-watch": "Lägg till sidan och omdirigeringen till den aktuella användarens bevakningslista.",
"apihelp-move-param-unwatch": "Ta bort sidan och omdirigeringen från den aktuella användarens bevakningslista.",
"apihelp-move-param-ignorewarnings": "Ignorera alla varningar.",
+ "apihelp-opensearch-description": "Sök wikin med protokollet OpenSearch.",
"apihelp-opensearch-param-search": "Söksträng.",
"apihelp-opensearch-param-limit": "Maximalt antal resultat att returnera.",
"apihelp-opensearch-param-namespace": "Namnrymder att genomsöka.",
"apihelp-options-example-complex": "Återställ alla inställningar, ställ sedan in <kbd>skin</kbd> och <kbd>nickname</kbd>.",
"apihelp-paraminfo-description": "Få information om API moduler.",
"apihelp-paraminfo-param-helpformat": "Format för hjälpsträngar.",
+ "apihelp-patrol-example-rcid": "Patrullera en nykommen ändring.",
"apihelp-patrol-example-revid": "Patrullera en sidversion",
"apihelp-protect-description": "Ändra skyddsnivån för en sida.",
"apihelp-protect-example-protect": "Skydda en sida",
"apihelp-query+backlinks-param-pageid": "要搜索的页面ID。不能与<var>$1title</var>一起使用。",
"apihelp-query+backlinks-param-namespace": "要列举的名字空间。",
"apihelp-query+backlinks-param-dir": "罗列所采用的方向。",
+ "apihelp-query+backlinks-param-limit": "返回总计页面数。如果<var>$1redirect</var>被启用,则限定分别适用于每一等级(这意味着将返回多达2 * <var>$1limit</var>个结果)。",
"apihelp-query+backlinks-example-simple": "显示至<kbd>Main page<kbd>的链接。",
"apihelp-query+backlinks-example-generator": "获得关于链接至<kbd>Main page<kbd>的页面的信息。",
"apihelp-query+blocks-description": "列出所有被封禁的用户和IP地址。",
"apihelp-query+links-param-limit": "返回多少链接。",
"apihelp-query+links-param-dir": "罗列所采用的方向。",
"apihelp-query+links-example-simple": "从页面<kbd>Main Page</kbd>获得链接",
- "apihelp-query+links-example-generator": "è\8e·å\8f\96æ\9c\89å\85³[[é¦\96页]]é\93¾æ\8e¥é¡µé\9d¢ç\9a\84ä¿¡æ\81¯",
+ "apihelp-query+links-example-generator": "è\8e·å¾\97æ\9c\89å\85³å\9c¨é¡µé\9d¢<kbd>Main Page</kbd>ä¸è¿\9eæ\8e¥ç\9a\84页é\9d¢ç\9a\84ä¿¡æ\81¯ã\80\82",
"apihelp-query+links-example-namespaces": "获得在{{ns:user}}和{{ns:template}}名字空间中来自页面<kbd>Main Page</kbd>的链接。",
"apihelp-query+linkshere-param-namespace": "只包括这些名字空间的页面。",
"apihelp-query+linkshere-param-limit": "返回多少。",
"apihelp-query+redirects-example-generator": "获取所有重定向至[[首页]]的信息",
"apihelp-query+revisions-example-last5": "获取<kbd>Main Page</kbd>的最近5次修订。",
"apihelp-query+revisions-example-first5": "获取“首页”的前5次修订版本",
- "apihelp-query+revisions-example-first5-after": "è\8e·å\8f\96â\80\9cé¦\96页â\80\9däº\8e2006å¹´05æ\9c\8801æ\97¥ä¹\8bå\90\8eå\81\9aå\87ºç\9a\84å\89\8d5次修订ç\89\88æ\9c¬",
+ "apihelp-query+revisions-example-first5-after": "è\8e·å¾\97<kbd>Main Page</kbd>äº\8e2006å¹´05æ\9c\8801æ\97¥ä¹\8bå\90\8eå\81\9aå\87ºç\9a\84å\89\8d5次修订ç\89\88æ\9c¬ã\80\82",
"apihelp-query+search-param-search": "搜索所有拥有此值的页面标题(或内容)。",
"apihelp-query+search-param-namespace": "只在这些名字空间搜索。",
"apihelp-query+search-param-info": "要返回的元数据。",
"apihelp-query+search-param-interwiki": "搜索结果中包含跨wiki结果,如果可用。",
"apihelp-query+search-example-simple": "搜索<kbd>meaning</kbd>。",
"apihelp-query+search-example-text": "搜索文本<kbd>meaning</kbd>。",
+ "apihelp-query+search-example-generator": "获得有关搜索<kbd>meaning</kbd>返回页面的页面信息。",
"apihelp-query+siteinfo-param-numberingroup": "列出用户组中的用户数。",
"apihelp-query+siteinfo-example-simple": "获取网站信息",
"apihelp-query+siteinfo-example-interwiki": "获取本地跨wiki前缀列表",
"apihelp-query+transcludedin-param-namespace": "至包含这些名字空间的页面。",
"apihelp-query+transcludedin-param-limit": "返回多少。",
"apihelp-query+transcludedin-example-simple": "获取嵌入[[首页]]的页面列表",
- "apihelp-query+transcludedin-example-generator": "è\8e·å\8f\96æ\9c\89å\85³åµ\8cå\85¥[[é¦\96页]]ç\9a\84页é\9d¢ç\9a\84ä¿¡æ\81¯",
+ "apihelp-query+transcludedin-example-generator": "è\8e·å¾\97æ\9c\89å\85³åµ\8cå\85¥<kbd>Main Page</kbd>ç\9a\84页é\9d¢ç\9a\84ä¿¡æ\81¯ã\80\82",
"apihelp-query+usercontribs-description": "获取一位用户的所有编辑。",
"apihelp-query+usercontribs-param-namespace": "只列出这些名字空间的贡献。",
"apihelp-query+usercontribs-example-user": "显示用户<kbd>Example</kbd>的贡献。",
"apihelp-query+userinfo-example-data": "获取有关当前用户的额外信息",
"apihelp-query+users-description": "获取有关列出用户的信息。",
"apihelp-query+users-param-token": "请改用<kbd>[[Special:ApiHelp/query+tokens|action=query&meta=tokens]]</kbd>。",
- "apihelp-query+users-example-simple": "返回[[User:Example]]的信息",
+ "apihelp-query+users-example-simple": "返回用户<kbd>Example</kbd>的信息。",
"apihelp-query+watchlist-param-user": "只列出此用户的更改。",
"apihelp-query+watchlist-param-excludeuser": "不要列出此用户的更改。",
"apihelp-query+watchlist-param-token": "允许访问其他用户监视列表的安全密钥(可通过用户的[[Special:Preferences#mw-prefsection-watchlist|参数设置]]找到)。",
"apihelp-watch-param-title": "要(取消)监视的页面。也可使用<var>$1titles</var>。",
"apihelp-watch-example-watch": "监视页面<kbd>Main Page</kbd>。",
"apihelp-watch-example-unwatch": "取消监视页面<kbd>首页</kbd>。",
+ "apihelp-format-example-generic": "格式化查询结果为$1格式。",
"apihelp-dbg-description": "输出数据为PHP的<code>var_export()</code>格式。",
"apihelp-dbgfm-description": "输出数据为PHP的<code>var_export()</code>格式(HTML优质打印效果)。",
"apihelp-dump-description": "输出数据为PHP的<code>var_dump()</code>格式。",
"api-help-param-default-empty": "默认:<span class=\"apihelp-empty\">(空)</span>",
"api-help-param-token": "从[[Special:ApiHelp/query+tokens|action=query&meta=tokens]]取回的“$1”令牌",
"api-help-param-disabled-in-miser-mode": "由于[https://www.mediawiki.org/wiki/Manual:$wgMiserMode miser模式]而禁用。",
- "api-help-param-limited-in-miser-mode": "'''注意:'''由于[https://www.mediawiki.org/wiki/Manual:$wgMiserMode miser模式],使用这个可能导致继续前返回少于“$1limit”个结果;极端情况下可能不会返回任何结果。",
+ "api-help-param-limited-in-miser-mode": "<strong>注意:</strong>由于[[mw:Manual:$wgMiserMode|miser模式]],使用这个可能导致继续前返回少于<var>$1limit</var>个结果;极端情况下可能不会返回任何结果。",
"api-help-param-direction": "列举的方向:\n;newer:最早的优先。注意:$1start应早于$1end。\n;older:最新的优先(默认)。注意:$1start应晚于$1end。",
"api-help-param-continue": "当更多结果可用时,使用这个继续。",
"api-help-param-no-description": "<span class=\"apihelp-empty\">(没有说明)</span>",
return ChangesList::showCharacterDifference( $old, $new );
}
- /**
- * Purge expired changes from the recentchanges table
- * @since 1.22
- */
- public static function purgeExpiredChanges() {
- if ( wfReadOnly() ) {
- return;
- }
-
- $method = __METHOD__;
- $dbw = wfGetDB( DB_MASTER );
- $dbw->onTransactionIdle( function () use ( $dbw, $method ) {
- global $wgRCMaxAge;
-
- $cutoff = $dbw->timestamp( time() - $wgRCMaxAge );
- $dbw->delete(
- 'recentchanges',
- array( 'rc_timestamp < ' . $dbw->addQuotes( $cutoff ) ),
- $method
- );
- } );
- }
-
private static function checkIPAddress( $ip ) {
global $wgRequest;
if ( $ip ) {
*
* @return bool|mixed The value from the field, or false on failure.
*/
- public function selectField( $table, $var, $cond = '', $fname = __METHOD__,
- $options = array()
+ public function selectField(
+ $table, $var, $cond = '', $fname = __METHOD__, $options = array()
) {
+ if ( $var === '*' ) { // sanity
+ throw new DBUnexpectedError( $this, "Cannot use a * field: got '$var'" );
+ }
+
if ( !is_array( $options ) ) {
$options = array( $options );
}
$options['LIMIT'] = 1;
$res = $this->select( $table, $var, $cond, $fname, $options );
-
if ( $res === false || !$this->numRows( $res ) ) {
return false;
}
}
}
+ /**
+ * A SELECT wrapper which returns a list of single field values from result rows.
+ *
+ * Usually throws a DBQueryError on failure. If errors are explicitly
+ * ignored, returns false on failure.
+ *
+ * If no result rows are returned from the query, false is returned.
+ *
+ * @param string|array $table Table name. See DatabaseBase::select() for details.
+ * @param string $var The field name to select. This must be a valid SQL
+ * fragment: do not use unvalidated user input.
+ * @param string|array $cond The condition array. See DatabaseBase::select() for details.
+ * @param string $fname The function name of the caller.
+ * @param string|array $options The query options. See DatabaseBase::select() for details.
+ *
+ * @return bool|array The values from the field, or false on failure
+ * @since 1.25
+ */
+ public function selectFieldValues(
+ $table, $var, $cond = '', $fname = __METHOD__, $options = array()
+ ) {
+ if ( $var === '*' ) { // sanity
+ throw new DBUnexpectedError( $this, "Cannot use a * field: got '$var'" );
+ }
+
+ if ( !is_array( $options ) ) {
+ $options = array( $options );
+ }
+
+ $res = $this->select( $table, $var, $cond, $fname, $options );
+ if ( $res === false ) {
+ return false;
+ }
+
+ $values = array();
+ foreach ( $res as $row ) {
+ $values[] = $row->$var;
+ }
+
+ return $values;
+ }
+
/**
* Returns an optional USE INDEX clause to go after the table, and a
* string to go at the end of the query.
* Create a new fatal error
*
* @param string $message
- * @return FileRepoStatus
+ * @return Status
*/
public function newFatal( $message /*, parameters...*/ ) {
- $params = func_get_args();
- array_unshift( $params, $this );
+ $status = call_user_func_array( array( 'Status', 'newFatal' ), func_get_args() );
+ $status->cleanCallback = $this->getErrorCleanupFunction();
- return call_user_func_array( array( 'FileRepoStatus', 'newFatal' ), $params );
+ return $status;
}
/**
* Create a new good result
*
* @param null|string $value
- * @return FileRepoStatus
+ * @return Status
*/
public function newGood( $value = null ) {
- return FileRepoStatus::newGood( $this, $value );
+ $status = Status::newGood( $this, $value );
+ $status->cleanCallback = $this->getErrorCleanupFunction();
+
+ return $status;
}
/**
/**
* Generic operation result class for FileRepo-related operations
* @ingroup FileRepo
+ * @deprecated 1.25
*/
-class FileRepoStatus extends Status {
- /**
- * Factory function for fatal errors
- *
- * @param FileRepo $repo
- * @return FileRepoStatus
- */
- static function newFatal( $repo /*, parameters...*/ ) {
- $params = array_slice( func_get_args(), 1 );
- $result = new self( $repo );
- call_user_func_array( array( &$result, 'error' ), $params );
- $result->ok = false;
-
- return $result;
- }
-
- /**
- * @param FileRepo|bool $repo Default: false
- * @param mixed $value
- * @return FileRepoStatus
- */
- static function newGood( $repo = false, $value = null ) {
- $result = new self( $repo );
- $result->value = $value;
-
- return $result;
- }
-
- /**
- * @param bool|FileRepo $repo
- */
- function __construct( $repo = false ) {
- if ( $repo ) {
- $this->cleanCallback = $repo->getErrorCleanupFunction();
- }
- }
-}
+class FileRepoStatus extends Status {}
"Fabsouza1",
"Rodrigo codignoli",
"Tuliouel",
- "Marcos dias de oliveira"
+ "Marcos dias de oliveira",
+ "Fasouzafreitas"
]
},
"config-desc": "O instalador do MediaWiki",
"config-install-done": "<strong>Parabéns!</strong>\nVocê concluiu a instalação do MediaWiki.\n\nO instalador gerou um arquivo <code>LocalSettings.php</code>.\nEste arquivo contém todas as suas configurações.\n\nVocê precisa fazer o download desse arquivo e colocá-lo na raiz da sua instalação (o mesmo diretório onde está o arquivo <code>index.php</code>). Este download deve ter sido iniciado automaticamente.\n\nSe o download não foi iniciado, ou se ele foi cancelado, pode recomeçá-lo clicando no link abaixo:\n\n$3\n\n<strong>Nota</strong>: Se não fizer isto agora, o arquivo que foi gerado não estará disponível depois que você sair do processo de instalação sem baixá-lo.\n\nQuando isso tiver sido feito, pode <strong>[$2 entrar na sua wiki]</strong>.",
"config-download-localsettings": "Baixar <code>LocalSettings.php</code>",
"config-help": "ajuda",
+ "config-help-tooltip": "clique para expandir",
"config-nofile": "O arquivo \"$1\" não foi encontrado. Ele foi apagado?",
"config-extension-link": "Você sabia que sua wiki suporta [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Extensions extensões]?\n\nVocê pode explorar as [//www.mediawiki.org/wiki/Special:MyLanguage/Category:Extensions_by_category extensões por categoria] ou visitar a [//www.mediawiki.org/wiki/Extension_Matrix Matriz de Extensões] para ver a lista completa.",
"mainpagetext": "<strong>O MediaWiki foi instalado com sucesso.</strong>",
/**
* Class to handle job queues stored in Redis
*
- * This is faster, less resource intensive, queue that JobQueueDB.
+ * This is a faster and less resource-intensive job queue than JobQueueDB.
* All data for a queue using this class is placed into one redis server.
*
* There are eight main redis keys used to track jobs:
*
* This class requires Redis 2.6 as it makes use Lua scripts for fast atomic operations.
* Additionally, it should be noted that redis has different persistence modes, such
- * as rdb snapshots, journaling, and no persistent. Appropriate configuration should be
+ * as rdb snapshots, journaling, and no persistence. Appropriate configuration should be
* made on the servers based on what queues are using it and what tolerance they have.
*
* @ingroup JobQueue
protected $server;
/** @var string Compression method to use */
protected $compression;
- /** @var bool */
- protected $daemonized;
const MAX_AGE_PRUNE = 604800; // integer; seconds a job can live once claimed (7 days)
$this->server = $params['redisServer'];
$this->compression = isset( $params['compression'] ) ? $params['compression'] : 'none';
$this->redisPool = RedisConnectionPool::singleton( $params['redisConfig'] );
- $this->daemonized = !empty( $params['daemonized'] );
+ if ( empty( $params['daemonized'] ) ) {
+ throw new Exception(
+ "Non-daemonized mode is no longer supported. Please install the " .
+ "mediawiki/services/jobrunner service and update \$wgJobTypeConf as needed." );
+ }
$this->checkDelay = true; // always enabled
}
protected function doPop() {
$job = false;
- // Push ready delayed jobs into the queue every 10 jobs to spread the load.
- // This is also done as a periodic task, but we don't want too much done at once.
- if ( !$this->daemonized && mt_rand( 0, 9 ) == 0 ) {
- $this->recyclePruneAndUndelayJobs();
- }
-
$conn = $this->getConnection();
try {
do {
- // Keep the claimed job list down for high-traffic queues
- if ( !$this->daemonized && mt_rand( 0, 99 ) == 0 ) {
- $this->recyclePruneAndUndelayJobs();
- }
$blob = $this->popAndAcquireBlob( $conn );
if ( !is_string( $blob ) ) {
break; // no jobs; nothing to do
continue;
}
- // If $item is invalid, recyclePruneAndUndelayJobs() will cleanup as needed
+ // If $item is invalid, the runner loop recyling will cleanup as needed
$job = $this->getJobFromFields( $item ); // may be false
} while ( !$job ); // job may be false if invalid
} catch ( RedisException $e ) {
}
}
- /**
- * Recycle or destroy any jobs that have been claimed for too long
- * and release any ready delayed jobs into the queue
- *
- * @return int Number of jobs recycled/deleted/undelayed
- * @throws MWException|JobQueueError
- */
- public function recyclePruneAndUndelayJobs() {
- $count = 0;
- // For each job item that can be retried, we need to add it back to the
- // main queue and remove it from the list of currenty claimed job items.
- // For those that cannot, they are marked as dead and kept around for
- // investigation and manual job restoration but are eventually deleted.
- $conn = $this->getConnection();
- try {
- $now = time();
- static $script =
-<<<LUA
- local kClaimed, kAttempts, kUnclaimed, kData, kAbandoned, kDelayed = unpack(KEYS)
- local released,abandoned,pruned,undelayed = 0,0,0,0
- -- Get all non-dead jobs that have an expired claim on them.
- -- The score for each item is the last claim timestamp (UNIX).
- local staleClaims = redis.call('zRangeByScore',kClaimed,0,ARGV[1])
- for k,id in ipairs(staleClaims) do
- local timestamp = redis.call('zScore',kClaimed,id)
- local attempts = redis.call('hGet',kAttempts,id)
- if attempts < ARGV[3] then
- -- Claim expired and retries left: re-enqueue the job
- redis.call('lPush',kUnclaimed,id)
- released = released + 1
- else
- -- Claim expired and no retries left: mark the job as dead
- redis.call('zAdd',kAbandoned,timestamp,id)
- abandoned = abandoned + 1
- end
- redis.call('zRem',kClaimed,id)
- end
- -- Get all of the dead jobs that have been marked as dead for too long.
- -- The score for each item is the last claim timestamp (UNIX).
- local deadClaims = redis.call('zRangeByScore',kAbandoned,0,ARGV[2])
- for k,id in ipairs(deadClaims) do
- -- Stale and out of retries: remove any traces of the job
- redis.call('zRem',kAbandoned,id)
- redis.call('hDel',kAttempts,id)
- redis.call('hDel',kData,id)
- pruned = pruned + 1
- end
- -- Get the list of ready delayed jobs, sorted by readiness (UNIX timestamp)
- local ids = redis.call('zRangeByScore',kDelayed,0,ARGV[4])
- -- Migrate the jobs from the "delayed" set to the "unclaimed" list
- for k,id in ipairs(ids) do
- redis.call('lPush',kUnclaimed,id)
- redis.call('zRem',kDelayed,id)
- end
- undelayed = #ids
- return {released,abandoned,pruned,undelayed}
-LUA;
- $res = $conn->luaEval( $script,
- array(
- $this->getQueueKey( 'z-claimed' ), # KEYS[1]
- $this->getQueueKey( 'h-attempts' ), # KEYS[2]
- $this->getQueueKey( 'l-unclaimed' ), # KEYS[3]
- $this->getQueueKey( 'h-data' ), # KEYS[4]
- $this->getQueueKey( 'z-abandoned' ), # KEYS[5]
- $this->getQueueKey( 'z-delayed' ), # KEYS[6]
- $now - $this->claimTTL, # ARGV[1]
- $now - self::MAX_AGE_PRUNE, # ARGV[2]
- $this->maxTries, # ARGV[3]
- $now # ARGV[4]
- ),
- 6 # number of first argument(s) that are keys
- );
- if ( $res ) {
- list( $released, $abandoned, $pruned, $undelayed ) = $res;
- $count += $released + $pruned + $undelayed;
- JobQueue::incrStats( 'job-recycle', $this->type, $released, $this->wiki );
- JobQueue::incrStats( 'job-abandon', $this->type, $abandoned, $this->wiki );
- JobQueue::incrStats( 'job-undelay', $this->type, $undelayed, $this->wiki );
- }
- } catch ( RedisException $e ) {
- $this->throwRedisException( $conn, $e );
- }
-
- return $count;
- }
-
/**
* @return array
*/
protected function doGetPeriodicTasks() {
- if ( $this->daemonized ) {
- return array(); // managed in the runner loop
- }
- $periods = array( 300 ); // 5 min; delayed/stale jobs
- if ( $this->claimTTL > 0 ) {
- $periods[] = ceil( $this->claimTTL / 2 ); // halved to avoid bad timing
- }
- $period = min( $periods );
- $period = max( $period, 30 ); // sanity
-
- return array(
- 'recyclePruneAndUndelayJobs' => array(
- 'callback' => array( $this, 'recyclePruneAndUndelayJobs' ),
- 'period' => $period,
- )
- );
+ return array(); // managed in the runner loop
}
/**
--- /dev/null
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @author Aaron Schulz
+ */
+
+/**
+ * Job for pruning recent changes
+ *
+ * @ingroup JobQueue
+ * @since 1.25
+ */
+class RecentChangesUpdateJob extends Job {
+ function __construct( $title, $params ) {
+ parent::__construct( 'recentChangesUpdate', $title, $params );
+
+ if ( !isset( $params['type'] ) ) {
+ throw new Exception( "Missing 'type' parameter." );
+ }
+
+ $this->removeDuplicates = true;
+ }
+
+ /**
+ * @return RecentChangesUpdateJob
+ */
+ final public static function newPurgeJob() {
+ return new self(
+ SpecialPage::getTitleFor( 'Recentchanges' ), array( 'type' => 'purge' )
+ );
+ }
+
+ public function run() {
+ if ( $this->params['type'] === 'purge' ) {
+ $this->purgeExpiredRows();
+ } else {
+ throw new Exception( "Invalid 'type' parameter '{$this->params['type']}'." );
+ }
+
+ return true;
+ }
+
+ protected function purgeExpiredRows() {
+ global $wgRCMaxAge;
+
+ $dbw = wfGetDB( DB_MASTER );
+ if ( !$dbw->lock( 'recentchanges-prune', __METHOD__, 1 ) ) {
+ return true; // already in progress
+ }
+
+ $cutoff = $dbw->timestamp( time() - $wgRCMaxAge );
+ do {
+ $rcIds = $dbw->selectFieldValues( 'recentchanges',
+ 'rc_id',
+ array( 'rc_timestamp < ' . $dbw->addQuotes( $cutoff ) ),
+ __METHOD__,
+ array( 'LIMIT' => 100 ) // avoid slave lag
+ );
+ if ( $rcIds ) {
+ $dbw->delete( 'recentchanges', array( 'rc_id' => $rcIds ), __METHOD__ );
+ }
+ } while ( $rcIds );
+
+ $dbw->unlock( 'recentchanges-prune', __METHOD__ );
+ }
+}
--- /dev/null
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+interface MessageSpecifier {
+ /**
+ * Returns the message key
+ *
+ * If a list of multiple possible keys was supplied to the constructor, this method may
+ * return any of these keys. After the message has been fetched, this method will return
+ * the key that was actually used to fetch the message.
+ *
+ * @return string
+ */
+ public function getKey();
+
+ /**
+ * Returns the message parameters
+ *
+ * @return array
+ */
+ public function getParams();
+}
--- /dev/null
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+/**
+ * Generic operation result class
+ * Has warning/error list, boolean status and arbitrary value
+ *
+ * "Good" means the operation was completed with no warnings or errors.
+ *
+ * "OK" means the operation was partially or wholly completed.
+ *
+ * An operation which is not OK should have errors so that the user can be
+ * informed as to what went wrong. Calling the fatal() function sets an error
+ * message and simultaneously switches off the OK flag.
+ *
+ * The recommended pattern for Status objects is to return a StatusValue
+ * unconditionally, i.e. both on success and on failure -- so that the
+ * developer of the calling code is reminded that the function can fail, and
+ * so that a lack of error-handling will be explicit.
+ *
+ * The use of Message objects should be avoided when serializability is needed.
+ *
+ * @since 1.25
+ */
+class StatusValue {
+ /** @var bool */
+ protected $ok = true;
+ /** @var array */
+ protected $errors = array();
+
+ /** @var mixed */
+ public $value;
+ /** @var array Map of (key => bool) to indicate success of each part of batch operations */
+ public $success = array();
+ /** @var int Counter for batch operations */
+ public $successCount = 0;
+ /** @var int Counter for batch operations */
+ public $failCount = 0;
+
+ /**
+ * Factory function for fatal errors
+ *
+ * @param string|MessageSpecifier $message Message key or object
+ * @return Status
+ */
+ public static function newFatal( $message /*, parameters...*/ ) {
+ $params = func_get_args();
+ $result = new static();
+ call_user_func_array( array( &$result, 'fatal' ), $params );
+ return $result;
+ }
+
+ /**
+ * Factory function for good results
+ *
+ * @param mixed $value
+ * @return Status
+ */
+ public static function newGood( $value = null ) {
+ $result = new static();
+ $result->value = $value;
+ return $result;
+ }
+
+ /**
+ * Returns whether the operation completed and didn't have any error or
+ * warnings
+ *
+ * @return bool
+ */
+ public function isGood() {
+ return $this->ok && !$this->errors;
+ }
+
+ /**
+ * Returns whether the operation completed
+ *
+ * @return bool
+ */
+ public function isOK() {
+ return $this->ok;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getValue() {
+ return $this->value;
+ }
+
+ /**
+ * Get the list of errors
+ *
+ * Each error is a (message:string or MessageSpecifier,params:array) map
+ *
+ * @return array
+ */
+ public function getErrors() {
+ return $this->errors;
+ }
+
+ /**
+ * Change operation status
+ *
+ * @param bool $ok
+ */
+ public function setOK( $ok ) {
+ $this->ok = $ok;
+ }
+
+ /**
+ * Change operation resuklt
+ *
+ * @param bool $ok Whether the operation completed
+ * @param mixed $value
+ */
+ public function setResult( $ok, $value = null ) {
+ $this->ok = $ok;
+ $this->value = $value;
+ }
+
+ /**
+ * Add a new warning
+ *
+ * @param string|MessageSpecifier $message Message key or object
+ */
+ public function warning( $message /*, parameters... */ ) {
+ $this->errors[] = array(
+ 'type' => 'warning',
+ 'message' => $message,
+ 'params' => array_slice( func_get_args(), 1 )
+ );
+ }
+
+ /**
+ * Add an error, do not set fatal flag
+ * This can be used for non-fatal errors
+ *
+ * @param string|MessageSpecifier $message Message key or object
+ */
+ public function error( $message /*, parameters... */ ) {
+ $this->errors[] = array(
+ 'type' => 'error',
+ 'message' => $message,
+ 'params' => array_slice( func_get_args(), 1 )
+ );
+ }
+
+ /**
+ * Add an error and set OK to false, indicating that the operation
+ * as a whole was fatal
+ *
+ * @param string|MessageSpecifier $message Message key or object
+ */
+ public function fatal( $message /*, parameters... */ ) {
+ $this->errors[] = array(
+ 'type' => 'error',
+ 'message' => $message,
+ 'params' => array_slice( func_get_args(), 1 )
+ );
+ $this->ok = false;
+ }
+
+ /**
+ * Merge another status object into this one
+ *
+ * @param Status $other Other Status object
+ * @param bool $overwriteValue Whether to override the "value" member
+ */
+ public function merge( $other, $overwriteValue = false ) {
+ $this->errors = array_merge( $this->errors, $other->errors );
+ $this->ok = $this->ok && $other->ok;
+ if ( $overwriteValue ) {
+ $this->value = $other->value;
+ }
+ $this->successCount += $other->successCount;
+ $this->failCount += $other->failCount;
+ }
+
+ /**
+ * Returns a list of status messages of the given type
+ *
+ * Each entry is a map of (message:string or MessageSpecifier,params:array))
+ *
+ * @param string $type
+ * @return array
+ */
+ public function getErrorsByType( $type ) {
+ $result = array();
+ foreach ( $this->errors as $error ) {
+ if ( $error['type'] === $type ) {
+ $result[] = $error;
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Returns true if the specified message is present as a warning or error
+ *
+ * @param string|MessageSpecifier $message Message key or object to search for
+ *
+ * @return bool
+ */
+ public function hasMessage( $message ) {
+ if ( $message instanceof MessageSpecifier ) {
+ $message = $message->getKey();
+ }
+ foreach ( $this->errors as $error ) {
+ if ( $error['message'] instanceof MessageSpecifier
+ && $error['message']->getKey() === $message
+ ) {
+ return true;
+ } elseif ( $error['message'] === $message ) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * If the specified source message exists, replace it with the specified
+ * destination message, but keep the same parameters as in the original error.
+ *
+ * Note, due to the lack of tools for comparing IStatusMessage objects, this
+ * function will not work when using such an object as the search parameter.
+ *
+ * @param IStatusMessage|string $source Message key or object to search for
+ * @param IStatusMessage|string $dest Replacement message key or object
+ * @return bool Return true if the replacement was done, false otherwise.
+ */
+ public function replaceMessage( $source, $dest ) {
+ $replaced = false;
+
+ foreach ( $this->errors as $index => $error ) {
+ if ( $error['message'] === $source ) {
+ $this->errors[$index]['message'] = $dest;
+ $replaced = true;
+ }
+ }
+
+ return $replaced;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString() {
+ $status = $this->isOK() ? "OK" : "Error";
+ if ( count( $this->errors ) ) {
+ $errorcount = "collected " . ( count( $this->errors ) ) . " error(s) on the way";
+ } else {
+ $errorcount = "no errors detected";
+ }
+ if ( isset( $this->value ) ) {
+ $valstr = gettype( $this->value ) . " value set";
+ if ( is_object( $this->value ) ) {
+ $valstr .= "\"" . get_class( $this->value ) . "\" instance";
+ }
+ } else {
+ $valstr = "no value set";
+ }
+ $out = sprintf( "<%s, %s, %s>",
+ $status,
+ $errorcount,
+ $valstr
+ );
+ if ( count( $this->errors ) > 0 ) {
+ $hdr = sprintf( "+-%'-4s-+-%'-25s-+-%'-40s-+\n", "", "", "" );
+ $i = 1;
+ $out .= "\n";
+ $out .= $hdr;
+ foreach ( $this->errors as $error ) {
+ if ( $error['message'] instanceof MessageSpecifier ) {
+ $key = $error['message']->getKey();
+ $params = $error['message']->getParams();
+ } elseif ( $error['params'] ) {
+ $key = $error['message'];
+ $params = $error['params'];
+ } else {
+ $key = $error['message'];
+ $params = array();
+ }
+
+ $out .= sprintf( "| %4d | %-25.25s | %-40.40s |\n",
+ $i,
+ $key,
+ implode( " ", $params )
+ );
+ $i += 1;
+ }
+ $out .= $hdr;
+ }
+
+ return $out;
+ }
+}
);
}
- $common = $file->getCommonMetaArray();
-
- if ( $common !== false ) {
- foreach ( $common as $key => $value ) {
- $fileMetadata[$key] = array(
- 'value' => $value,
- 'source' => 'file-metadata',
- );
- }
- }
-
return $fileMetadata;
}
} elseif ( isset( $params['width'] ) ) {
$width = $params['width'];
} else {
- throw new MWException( 'No width specified to ' . __METHOD__ );
+ throw new MediaTransformInvalidParametersException( 'No width specified to ' . __METHOD__ );
}
# Removed for ProofreadPage
--- /dev/null
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+/**
+ * MediaWiki exception thrown by some methods when the transform parameter array is invalid
+ *
+ * @ingroup Exception
+ */
+class MediaTransformInvalidParametersException extends MWException {}
* @return bool
* @throws MWException
*/
- public function cas( $casToken, $key, $value, $exptime = 0 ) {
+ protected function cas( $casToken, $key, $value, $exptime = 0 ) {
// APC's CAS functions only work on integers
throw new MWException( "CAS is not implemented in " . __CLASS__ );
}
*/
abstract public function set( $key, $value, $exptime = 0 );
- /**
- * Check and set an item.
- * @param mixed $casToken
- * @param string $key
- * @param mixed $value
- * @param int $exptime Either an interval in seconds or a unix timestamp for expiry
- * @return bool Success
- */
- abstract public function cas( $casToken, $key, $value, $exptime = 0 );
-
/**
* Delete an item.
* @param string $key
return $success;
}
+ /**
+ * Check and set an item.
+ * @param mixed $casToken
+ * @param string $key
+ * @param mixed $value
+ * @param int $exptime Either an interval in seconds or a unix timestamp for expiry
+ * @return bool Success
+ */
+ abstract protected function cas( $casToken, $key, $value, $exptime = 0 );
+
/**
* @see BagOStuff::merge()
*
* @param mixed $casToken [optional]
* @return bool
*/
- function get( $key, &$casToken = null ) {
+ public function get( $key, &$casToken = null ) {
return false;
}
* @param int $exp
* @return bool
*/
- function set( $key, $value, $exp = 0 ) {
+ public function set( $key, $value, $exp = 0 ) {
return true;
}
* @param int $exp
* @return bool
*/
- function cas( $casToken, $key, $value, $exp = 0 ) {
+ protected function cas( $casToken, $key, $value, $exp = 0 ) {
return true;
}
* @param string $key
* @return bool
*/
- function delete( $key ) {
+ public function delete( $key ) {
return true;
}
* @param mixed $casToken [optional]
* @return bool|mixed
*/
- function get( $key, &$casToken = null ) {
+ public function get( $key, &$casToken = null ) {
if ( !isset( $this->bag[$key] ) ) {
return false;
}
* @param int $exptime
* @return bool
*/
- function set( $key, $value, $exptime = 0 ) {
+ public function set( $key, $value, $exptime = 0 ) {
$this->bag[$key] = array( $value, $this->convertExpiry( $exptime ) );
return true;
}
* @param int $exptime
* @return bool
*/
- function cas( $casToken, $key, $value, $exptime = 0 ) {
+ protected function cas( $casToken, $key, $value, $exptime = 0 ) {
if ( $this->get( $key ) === $casToken ) {
return $this->set( $key, $value, $exptime );
}
* @param int $exptime
* @return bool
*/
- public function cas( $casToken, $key, $value, $exptime = 0 ) {
+ protected function cas( $casToken, $key, $value, $exptime = 0 ) {
return $this->client->cas( $casToken, $this->encodeKey( $key ),
$value, $this->fixExpiry( $exptime ) );
}
* @param int $exptime
* @return bool
*/
- public function cas( $casToken, $key, $value, $exptime = 0 ) {
+ protected function cas( $casToken, $key, $value, $exptime = 0 ) {
$this->debugLog( "cas($key)" );
return $this->checkResult( $key, parent::cas( $casToken, $key, $value, $exptime ) );
}
* @return bool
* @throws MWException
*/
- public function cas( $casToken, $key, $value, $exptime = 0 ) {
+ protected function cas( $casToken, $key, $value, $exptime = 0 ) {
throw new MWException( "CAS is not implemented in " . __CLASS__ );
}
* @ingroup Cache
*/
class ObjectCacheSessionHandler {
+ /** @var array Map of (session ID => SHA-1 of the data) */
+ protected static $hashCache = array();
+
/**
* Install a session handler for the current web request
*/
* Get the cache storage object to use for session storage
* @return BagOStuff
*/
- static function getCache() {
+ protected static function getCache() {
global $wgSessionCacheType;
+
return ObjectCache::getInstance( $wgSessionCacheType );
}
* @param string $id Session id
* @return string Cache key
*/
- static function getKey( $id ) {
+ protected static function getKey( $id ) {
return wfMemcKey( 'session', $id );
}
+ /**
+ * @param mixed $data
+ * @return string
+ */
+ protected static function getHash( $data ) {
+ return sha1( serialize( $data ) );
+ }
+
/**
* Callback when opening a session.
*
*/
static function read( $id ) {
$data = self::getCache()->get( self::getKey( $id ) );
- if ( $data === false ) {
- return '';
- }
- return $data;
+
+ self::$hashCache = array( $id => self::getHash( $data ) );
+
+ return ( $data === false ) ? '' : $data;
}
/**
*/
static function write( $id, $data ) {
global $wgObjectCacheSessionExpiry;
- self::getCache()->set( self::getKey( $id ), $data, $wgObjectCacheSessionExpiry );
+
+ // Only issue a write if anything changed (PHP 5.6 already does this)
+ if ( !isset( self::$hashCache[$id] )
+ || self::getHash( $data ) !== self::$hashCache[$id]
+ ) {
+ self::getCache()->set( self::getKey( $id ), $data, $wgObjectCacheSessionExpiry );
+ }
+
return true;
}
*/
static function destroy( $id ) {
self::getCache()->delete( self::getKey( $id ) );
+
return true;
}
return $result;
}
- public function cas( $casToken, $key, $value, $expiry = 0 ) {
+ protected function cas( $casToken, $key, $value, $expiry = 0 ) {
list( $server, $conn ) = $this->getConnection( $key );
if ( !$conn ) {
* @param int $exptime
* @return bool
*/
- public function cas( $casToken, $key, $value, $exptime = 0 ) {
+ protected function cas( $casToken, $key, $value, $exptime = 0 ) {
list( $serverIndex, $tableName ) = $this->getTableByKey( $key );
try {
$db = $this->getDB( $serverIndex );
* @param int $exptime Expiration time
* @return bool
*/
- public function cas( $casToken, $key, $value, $exptime = 0 ) {
+ protected function cas( $casToken, $key, $value, $exptime = 0 ) {
return wincache_ucache_cas( $key, $casToken, serialize( $value ) );
}
* @return bool
* @throws MWException
*/
- public function cas( $casToken, $key, $value, $exptime = 0 ) {
+ protected function cas( $casToken, $key, $value, $exptime = 0 ) {
// Can't find any documentation on xcache cas
throw new MWException( "CAS is not implemented in " . __CLASS__ );
}
Hooks::run( 'ArticleEditUpdates', array( &$this, &$editInfo, $options['changed'] ) );
if ( Hooks::run( 'ArticleEditUpdatesDeleteFromRecentchanges', array( &$this ) ) ) {
- if ( 0 == mt_rand( 0, 99 ) ) {
- // Flush old entries from the `recentchanges` table; we do this on
- // random requests so as to avoid an increase in writes for no good reason
- RecentChange::purgeExpiredChanges();
- }
+ // Flush old entries from the `recentchanges` table
+ JobQueueGroup::singleton()->push( RecentChangesUpdateJob::newPurgeJob() );
}
if ( !$this->exists() ) {
}
protected function extractResourceLoaderModules( $dir, array $info ) {
+ $defaultPaths = isset( $info['ResourceFileModulePaths'] )
+ ? $info['ResourceFileModulePaths']
+ : false;
+ if ( isset( $defaultPaths['localBasePath'] ) ) {
+ $defaultPaths['localBasePath'] = "$dir/{$defaultPaths['localBasePath']}";
+ }
+
if ( isset( $info['ResourceModules'] ) ) {
foreach ( $info['ResourceModules'] as $name => $data ) {
if ( isset( $data['localBasePath'] ) ) {
$data['localBasePath'] = "$dir/{$data['localBasePath']}";
}
+ if ( $defaultPaths ) {
+ $data += $defaultPaths;
+ }
$this->globals['wgResourceModules'][$name] = $data;
}
}
foreach ( $this->queued as $path => $mtime ) {
$json = file_get_contents( $path );
$info = json_decode( $json, /* $assoc = */ true );
+ if ( !is_array( $info ) ) {
+ throw new Exception( "$path is not a valid JSON file." );
+ }
$autoload = $this->processAutoLoader( dirname( $path ), $info );
// Set up the autoloader now so custom processors will work
$GLOBALS['wgAutoloadClasses'] += $autoload;
return array();
}
}
-
- /**
- * @param string $filename absolute path to the JSON file
- * @param int $mtime modified time of the file
- * @return array
- */
- protected function loadInfoFromFile( $filename, $mtime ) {
- $key = wfMemcKey( 'registry', md5( $filename ) );
- $cached = $this->cache->get( $key );
- if ( isset( $cached['mtime'] ) && $cached['mtime'] === $mtime ) {
- return $cached['info'];
- }
-
- $contents = file_get_contents( $filename );
- $json = json_decode( $contents, /* $assoc = */ true );
- if ( is_array( $json ) ) {
- $this->cache->set( $key, array( 'mtime' => $mtime, 'info' => $json ) );
- } else {
- // Don't throw an error here, but don't cache it either.
- // @todo log somewhere?
- $json = array();
- }
-
- return $json;
- }
}
/** @var bool */
protected static $debugMode = null;
+ /** @var array */
+ private static $lessVars = null;
+
/**
* Module name/ResourceLoaderModule object pairs
* @var array
* @return array Map of variable names to string CSS values.
*/
public static function getLessVars( Config $config ) {
- $lessVars = $config->get( 'ResourceLoaderLESSVars' );
- // Sort by key to ensure consistent hashing for cache lookups.
- ksort( $lessVars );
- return $lessVars;
+ if ( !self::$lessVars ) {
+ $lessVars = $config->get( 'ResourceLoaderLESSVars' );
+ Hooks::run( 'ResourceLoaderGetLessVars', array( &$lessVars ) );
+ // Sort by key to ensure consistent hashing for cache lookups.
+ ksort( $lessVars );
+ self::$lessVars = $lessVars;
+ }
+ return self::$lessVars;
}
}
"tags-active-yes": "Так",
"tags-active-no": "Не",
"tags-source-extension": "Вызначаецца пашырэньнем",
+ "tags-source-manual": "Ставіцца ўручную ўдзельнікамі і робатамі",
"tags-edit": "рэдагаваць",
"tags-hitcount": "$1 {{PLURAL:$1|зьмена|зьмены|зьменаў}}",
"comparepages": "Параўнаньне старонак",
"watchlistfor2": "په $1 $2",
"watchnologin": "داخل نه بوته ئیت",
"addwatch": "افاضه کورتین بئ واچلیستا",
+ "removewatch": "پاک کورتین شه واچلیستا",
"watch": "دیستین",
"watchthispage": "دیستین ای تاکدیمی",
"unwatch": "اوشتارین تین دیستینی",
"protect-cantedit": "شما ئه نه توانیت ای تاکدیمی قُلپئ وضیعتا تغیر بدهیت، چون که شما آیی ایڈیٹ ئی اجازه ئا نداریت.",
"protect-othertime": "دیگه وخت:",
"protect-othertime-op": "دیگه وخت",
+ "protect-existing-expiry": "موجودین انقضای وخت: $2، $3",
+ "protect-existing-expiry-infinity": "موجودین انقضای وخت: بینهایت",
+ "protect-otherreason": "دیگرین دلیل/اضافی:",
+ "protect-otherreason-op": "دیگرین دلیل",
+ "restriction-type": "دسترسی:",
+ "restriction-level": "محدودیت ئی سطح:",
"minimum-size": "حداقل اندازه",
"maximum-size": "حداکثر اندازه:",
"pagesize": "(بایٹ)",
"restriction-upload": "بُرز کورتین",
"restriction-level-sysop": "کاملآ قُلپ بوته",
"restriction-level-autoconfirmed": "نیمه گ قُلپ بوته",
+ "restriction-level-all": "هر سطحی",
+ "undelete": "پاک بوته ئین تاکدیمانی دیستین",
+ "undeletepage": "پاک بوته ئین تاکدیمانی دیستین و پدا جۆڑ کورتین",
+ "undelete-fieldset-title": "نخسه ئانی پدا جۆڑ کورتین",
"undeletebtn": "احیا",
"undeletelink": "نمایش/احیا",
"undeleteviewlink": "دیستین",
"undeletecomment": "دلیل:",
"undeletedrevisions": "$1 ئی نخسه احیا {{PLURAL:$1|بوت}}",
+ "undeletedfiles": "$1 ئی فایل پدا جۆڑ {{PLURAL:$1|بوت|بوتنت}}.",
+ "cannotundelete": "پدا جۆڑ کورتین ناکام ات:\n$1",
+ "undelete-search-title": "گشتین په پاک بوته ئین تاکدیمان",
+ "undelete-search-box": "گشتین په پاک بوته ئین تاکدیمان",
+ "undelete-search-prefix": "نشان داتین تاکدیمانی شرو شه:",
"undelete-search-submit": "گشتین",
+ "undelete-error": "خطا تاکدیم غیر قابل پاک کورتین اینت",
+ "undelete-error-short": "خطا پدا جۆڑ کورتین بئ فایل: $1",
+ "undelete-error-long": "بی پدا جۆڑ کورتین ئی وختا خطا رخ دات:\n\n$1",
+ "undelete-show-file-confirm": "آیا شما مطمئن وێت که لوٹیت یک پاک بوته ئین نخسه شه فایل \"<nowiki>$1</nowiki>\" مورخ $2 سائت $3 ئا بگیندیت؟",
"undelete-show-file-submit": "هان",
+ "namespace": "نامی فضا:",
+ "invert": "انتخاب سرچپی بیئت",
"blanknamespace": "(بُنیادی)",
+ "contribsub2": "په {{GENDER:$3|$1}} ($2)",
+ "nocontribs": "هیچ تغیری گۆ ای مشخصات ئان ودێ نه بوت",
"uctop": "(انونین نخسه)",
"month": "بی ای ماه ئی تا (و دیمتیر شه آیی):",
"year": "بی ای سال ئی تا (و دیمتیر شه آیی):",
"sp-contributions-newbies": "فقط نوکین مشارکتان نشان داته بیئنت",
"sp-contributions-newbies-sub": "په نوک کاران",
+ "sp-contributions-uploads": "بُرز بوته هان",
"sp-contributions-logs": "سیاههها",
"sp-contributions-talk": "گپ",
"sp-contributions-userrights": "کار گیروکی اختیارانی مدیریت",
"whatlinkshere-title": "تاکدیمان که گو «$1» لینک دارنت",
"whatlinkshere-page": "تاکدیم:",
"isredirect": "تاکدیمی تغییرمسیر داتین",
+ "istemplate": "تراگنجانشهان",
"isimage": "فایل لینک",
"whatlinkshere-prev": "{{PLURAL:$1|دیمئ|$1 دیمئ مورد}}",
"whatlinkshere-next": "{{PLURAL:$1|پدئ|$1 پدئ مورد}}",
"whatlinkshere-links": "→ لینک",
"whatlinkshere-hideredirs": "$1 تغییرمسیر",
+ "whatlinkshere-hidetrans": "$1 تراگنجانشهان",
"whatlinkshere-hidelinks": "$1 لینک",
"whatlinkshere-hideimages": "$1 فایلی لینکان",
"whatlinkshere-filters": "فیلتر ئان",
"unblock": "کار زوروکئ انبلاک یا پاچ کورتین",
"blockip": "{{GENDER:$1|کار زورکئ}} بستین",
"blockip-legend": "کار زوروکئ بلاک کورتین",
+ "ipbexpiry": "الاسی وخت:",
"ipbreason": "دلیل:",
+ "ipbsubmit": "ای کار زوروک بسته بیئت",
+ "ipbother": "دیگه وخت:",
+ "ipboptions": "۲ سائت:2 hours,۱ روچ:1 day,۳ روچ:3 days,۱ هپتگ:1 week,۲ هپتگ:2 weeks,۱ ماه:1 month,۳ ماه:3 months,۶ ماه:6 months,۱ سال:1 year,بیپایان:infinite",
"ipb-confirm": "بستینئ تائید کورتین",
"badipaddress": "آیپی نامجازین ادرس",
"blockipsuccesssub": "بستین گو کامیابیا انجام بوت",
+ "ipb-unblock-addr": " $1 پاچ کورتین",
+ "ipb-blocklist-contribs": "مشارکتان په {{GENDER:$1|$1}}",
+ "unblockip": "کار زوروکئ انبلاک یا پاچ کورتین",
+ "ipusubmit": "ای بلاک ئی پاک کورتین",
+ "unblocked": "[[User:$1|$1]] ئی دسترسی پدا پئال بوت",
+ "unblocked-range": "$1 پاچ بوت",
"blocklist": "بلاک بوته ئین کار زوروکان",
"ipblocklist": "بلاک بوته ئین کار زوروکان",
"ipblocklist-legend": "گشتین په بلاک بوته ئین کار زوروکا",
+ "blocklist-timestamp": "وختی برچسپ",
"blocklist-target": "هدف",
+ "blocklist-expiry": "الاسی وخت",
+ "blocklist-params": "بلاک ئی پارامیتران",
"blocklist-reason": "دلیل",
"ipblocklist-submit": "گشتین",
+ "ipblocklist-localblock": "محلین دسترسی ئی بستین",
+ "ipblocklist-otherblocks": "دیگرین {{PLURAL:$1|بستینهان|بستینهان}}",
+ "infiniteblock": "بیپایان",
+ "expiringblock": "بئ $1 سائت $2 ئا الاس ئه بێت",
+ "anononlyblock": "فقط زیان نامین کار زوروکان",
+ "emailblock": "ایمیل بسته بوته",
+ "blocklist-nousertalk": "وتئ گپ ئی تاکدیما نتوان ایڈیٹ کورت",
+ "blocklink": "بلاک یا بستین",
+ "unblocklink": "پاچ یا انبلاک بێت",
+ "contribslink": "مشارکت ئان",
"emaillink": "ایمیلی دیم داتین",
"blocklogpage": "کورمئ بستین",
"unblocklogentry": "$1 ئا پاچ کورت",
"block-log-flags-noemail": "ایمیل بسته بوته",
"block-log-flags-nousertalk": "وتئ گپ ئی تاکدیما نتوان ایڈیٹ کورت",
"block-log-flags-hiddenname": "چیهرین کار زوروکئ نام",
+ "lockedbyandtime": "(بواسطه $1 ئا بئ $2 سائت $3)",
"move-page": "انتقال $1",
"move-page-legend": "تاکدیمی انتقال",
"movearticle": "تاکدیمی انتقال:",
"movepagebtn": "تاکدیمی انتقال",
+ "movesubpage": "{{PLURAL:$1|گۆنڈدیم|گۆنڈدیم هان}}",
+ "movenosubpage": "ای تاکدیم هیچ گۆنڈدیم ئی نداریت.",
"movereason": "دلیل:",
"revertmove": "بیرگردینتین",
"delete_and_move": "پاک کورتین یا جابیجا",
"delete_and_move_confirm": "هان،تاکدیم پاک بیئت",
+ "delete_and_move_reason": "پاک کورتین «[[$1]]» جابجایی امکانا",
"export": "ڈن کورتین تاکدیمانئ",
"exportall": "ڈن کورتین موچین تاکدیمانئ",
"exportcuronly": "فقط انونین نخسه شامل بیئت، نه موچین تاریخچه",
"export-templates": "شامل بوتین تراشوانانئ",
"allmessagesname": "نام",
"allmessages-filter-legend": "فیلتر",
+ "allmessages-filter-unmodified": "تغیر نه کورته",
"allmessages-filter-all": "موچ",
+ "allmessages-filter-modified": "تغیر نه کورته",
"allmessages-language": "زبان:",
"allmessages-filter-submit": "برا",
"allmessages-filter-translate": "ترجمه",
"thumbnail-more": "ٹُوه کورتین",
"filemissing": "فایل وجود نداریت",
+ "import": "تاکدیمانێ بێ تێ کورتین",
+ "import-interwiki-sourcewiki": "ویکی زێ منشا:",
"import-interwiki-sourcepage": "تاکدیمئ منشا:",
"import-interwiki-templates": "موچین تراشوانانا شامل بیئت",
+ "import-interwiki-submit": "بێ تێ کورتین",
"import-upload-filename": "فایلئ نام:",
"import-comment": "کومنیت:",
"import-revision-count": "$1 {{PLURAL:$1|نخسه|نخسه}}",
"tooltip-n-mainpage-description": "بُنیاد ئین تاکدیمی دیستین",
"tooltip-n-portal": "بی پروژه ئی موریدا٬ آنچه که توانیت انجام دهیت و ای که چی چیزی ئا شه گوجا ودی بکنیت",
"tooltip-t-upload": "فایلی بُرز کورتین",
+ "tooltip-ca-nstab-main": "تاکدیمێ محتویاتێ دیستین",
+ "tooltip-ca-nstab-user": "کارزوروکین تاکدیمی دیستین",
+ "tooltip-ca-nstab-media": "میدیایی تاکدیمێ دیستین",
"tooltip-ca-nstab-special": "ای یک خاصین تاکدیمی است٬ شما ئه توانیت که ای تاکدیما ایڈیٹ بکنیت",
"tooltip-ca-nstab-project": "پروژه ئی تاکدیمی دیستین",
"tooltip-ca-nstab-image": "دیستین فایلی تاکدیمی",
"pageinfo-protect-cascading-yes": "هان",
"pageinfo-category-pages": "تاکدیمانی نمبر",
"patrol-log-page": "گشتئ سیاه چال",
+ "previousdiff": "→دیمتیرین ئی فرق",
+ "nextdiff": "نۆکتیرین ئی فرق ←",
"widthheightpage": "$1×$2، $3 {{PLURAL:$3|تاکدیم|تاکدیم}}",
"file-info": "فایلئ اندازه گ: $1، نوع MIME $2",
"file-info-size": "<span dir=\"ltr\">$1 × $2</span> پیکسل، فایلئ اندازه گ: $3، نوع MIME فایلئ: $4",
"file-info-size-pages": "<span style=\"direction:ltr\">$1 × $2</span> نقطه، فایلئ حجم: $3، نوع MIME فایل: $4، $5 تاکدیم",
+ "show-big-image-other": "دیگرین {{PLURAL:$2|کیفیت|کیفیتهان}}: $1.",
+ "show-big-image-size": "<span dir=\"ltr\">$1 × $2</span> پیکسل",
+ "file-info-gif-looped": "چرخشدار",
+ "file-info-gif-frames": "$1 {{PLURAL:$1|قاب|قاب}}",
+ "file-info-png-looped": "چرخشدار",
+ "file-info-png-repeat": "$1 {{PLURAL:$1|وار|وار}} پخش بوت",
+ "file-info-png-frames": "$1 {{PLURAL:$1|قاب|قاب}}",
+ "newimages-legend": "فیلتر",
+ "noimages": "چیزی په دیستینا نه اینت.",
"ilsubmit": "گشتین",
"bydate": "شه تاریخی رُوگا",
+ "sp-newimages-showfrom": "نشانداتین نۆکین اکسانی شه $2، $1 بئ بعد",
+ "seconds": "{{PLURAL:$1|$1ثانیه| $1 ثانیه}}",
+ "minutes": "{{PLURAL:$1|دقیقه|دقیقه}}",
+ "hours": "{{PLURAL:$1|سائت|سائت}}",
+ "days": "{{PLURAL:$1|روچ|روچ}}",
+ "weeks": "{{PLURAL:$1|$1 هپتگ|$1 هپتگ ئان}}",
+ "months": "{{PLURAL:$1|$1 ماه|}}",
+ "years": "{{PLURAL:$1|$1 سال|$1 سال ئان}}",
+ "ago": "$1دیما",
"just-now": "همی انون",
"hours-ago": "$1 سائت دیما",
"minutes-ago": "$1 دقیقه دیما",
"saturday-at": "بی شنبی $1",
"sunday-at": "یکشنبی $1",
"yesterday-at": "زئ بی $1",
+ "metadata": "فرادیتا",
"exif-imagewidth": "گوور، عرض",
"exif-imagelength": "تَچکي",
"exif-photometricinterpretation": "ٹیک ئانی ترکیب",
"exif-orientation": "نیمگ ، جهت",
+ "exif-ycbcrpositioning": "Y و C موقیعتان",
"exif-imagedescription": "اکسئ ئنوان",
"exif-artist": "اکس گيروک/هنرمند",
+ "exif-pixelydimension": "اکسئ گۆر",
+ "exif-pixelxdimension": "اکسئ بُرزی",
+ "exif-usercomment": "کار زوروکئ توضیحات",
+ "exif-fnumber": "اف ئی نمبر",
"exif-exposureprogram": "نوردهئ پروگرام",
"exif-spectralsensitivity": "طیفئ ئین حساسیت",
"exif-isospeedratings": "ایزو ئی سرعت ئی درجه بندی",
"exif-flashenergy": "پلاش ئی قدرت",
"exif-subjectlocation": "سوژه ئی مکان",
"exif-filesource": "فایلئ منشا",
+ "exif-contrast": "کنتراست",
+ "exif-gpsareainformation": "جیپیاس ئی ناحیه ئی نام",
+ "exif-gpsdatestamp": "جی پی اس ئی تاریخ",
+ "exif-worldregiondest": "جهانئ منطقه نشان داته بوته",
+ "exif-countrydest": "مُلک نشان داته بوته",
+ "exif-countrycodedest": "مُلکی کود نشان داته بوته",
+ "exif-provinceorstatedest": "ولایت یا ایالت نشان داته بوته",
+ "exif-citydest": "شار نشان داته بوته",
+ "exif-sublocationdest": "شاری یک بخش نشان داته بوته",
+ "exif-objectname": "گۆنڈین ئینوان",
+ "exif-headline": "ئنوان",
"exif-source": "منشا",
"exif-writer": "نویسوک",
"exif-languagecode": "زبان",
"exif-iimsupplementalcategory": "تکمیلین تهر ئان",
"exif-datetimereleased": "منتشر بوته بی",
"exif-label": "برچسب",
+ "exif-copyrighted-true": "کاپی رایت بوت",
+ "exif-unknowndate": "نامئلومین تاریخ",
+ "exif-orientation-1": "نورمال",
+ "exif-orientation-3": "۱۸۰ درجه چرخ وارته",
"exif-exposureprogram-1": "دستی",
"exif-exposureprogram-2": "عادی ئین پروگرام",
"exif-exposureprogram-4": "شاتر ئی اولویت",
"namespacesall": "موچ",
"monthsall": "موچ",
"confirmemail": "ایمیل ادرسی تائید کورتین",
+ "recreate": "پدا جۆڑ کورتین",
"confirm_purge_button": "قبول داشتین",
"confirm-watch-button": "قبول داشتین",
"confirm-unwatch-button": "قبول داشتین",
"tags-title": "برچسپ هان",
"tags-tag": "برچسپئ نام",
"tags-active-header": "پئال؟",
+ "tags-actions-header": "عملکردهان",
"tags-active-yes": "هان",
"tags-active-no": "نه",
"tags-edit": "ایڈیٹ",
+ "tags-delete": "پاک کورتین",
+ "tags-activate": "پئال کورتین",
+ "tags-deactivate": "غیرپئال کورتین",
"tags-hitcount": "$1 {{PLURAL:$1|ٹگل|ٹگل}}",
+ "tags-create-tag-name": "برچسپئ نام:",
+ "tags-create-reason": "دلیل:",
+ "tags-create-submit": "جوڑ\t کورتین",
+ "tags-activate-reason": "دلیل:",
+ "tags-activate-submit": "پئال کورتین",
+ "tags-deactivate-reason": "دلیل:",
+ "tags-deactivate-submit": "غیرپئال کورتین",
"comparepages": "تاکدیمانئ مقایسه",
"compare-page1": "تاکدیم ۱",
"compare-page2": "تاکدیم ۲",
"htmlform-cloner-delete": "پاک کورتین",
"htmlform-cloner-required": "حداقل ضرورت انداره گ.",
"logentry-delete-delete": "$1 ، $3 تاکدیما {{GENDER:$2|پاک کورت}}",
+ "logentry-delete-restore": "$1 ، $3 ئی تاکدیما {{GENDER:$2|پدا جۆڑ کورت}}",
"logentry-delete-event": "$1 پیدایی {{PLURAL:$5|یک مورد سیاه چال|$5 مورد سیاه چال}} ئا بئ $3 {{GENDER:$2|تا تغیر دات}}: $4",
"logentry-delete-revision": "$1 پیدایی {{PLURAL:$5|یک نخسه|$5 نخسه}} تاکدیم $3 ئا {{GENDER:$2|تغییر دات}}: $4",
+ "revdelete-content-hid": "محتوائانه چیهر کورت",
+ "revdelete-uname-hid": "چیهرین کار زوروکئ نام",
+ "revdelete-content-unhid": "محتوائانه سر درا کورت",
+ "revdelete-uname-unhid": "کار زوروکئ ناما سر درا کورت",
+ "revdelete-restricted": "مدیر ئانه محدود کورت",
+ "revdelete-unrestricted": "مدیرئانی محدودیت ئا پروشت",
+ "logentry-merge-merge": "$1 $3 را بئ $4 {{GENDER:$2| ادغام کورت}} (نخسه تا $5)",
+ "logentry-move-move": "$1، $3 ئی تاکدیما بئ $4 {{GENDER:$2|جابجا کورت}}",
+ "logentry-newusers-newusers": "$1 ئی کار زوروکئ حساب {{GENDER:$2|جۆڑ بوت}}",
+ "logentry-newusers-create": "$1 ئی کار زوروکئ حساب {{GENDER:$2|جۆڑ بوت}}",
+ "logentry-newusers-create2": "$3 ئی کار زوروکئ حساب شه $1 ئی نیمگا {{GENDER:$2|جۆڑ بوت}}",
+ "logentry-newusers-byemail": "$3 ئی کار زوروکئ حساب شه $1 ئی نیمگا {{GENDER:$2|جۆڑ بوت}} و چیهرگال یا پاسورد گو ایمیلا دیم داته بوت",
+ "logentry-newusers-autocreate": "$1 ئی حساب بئ اتوماتیکین رکما {{GENDER:$2|جۆڑ بوت}}",
+ "logentry-rights-rights": "$1 ، $3 ئی عضویتا شه $4 ئی گروپا بئ $5 {{GENDER:$2|تغییر دات}}",
+ "logentry-rights-rights-legacy": "$1 عضویتئ گروپا بئ $3 ئا {{GENDER:$2|تغییر دات}}",
+ "logentry-rights-autopromote": "$1 بئ اوتوماتیکین رکما وتر شه $4 بئ $5 {{GENDER:$2|ارتقاء دات}}",
+ "logentry-upload-upload": "$1 $3 ئا {{GENDER:$2|بُرز کورت}}",
+ "logentry-upload-overwrite": "$1 نوکین نخسه ئی شه $3 ئا {{GENDER:$2|بُرز کورت}}",
+ "logentry-upload-revert": "$1 {{GENDER:$2|بُرز کورت}} $3 ئا",
"rightsnone": "(هیچ)",
"revdelete-summary": "ایڈیتی خاصه",
"feedback-subject": "ئنوان:",
"feedback-cancel": "کنسیل",
"feedback-close": "کار بوت",
"searchsuggest-search": "گشتین",
+ "api-error-unclassified": "یک نا زانتین خطائی رخ دات.",
+ "api-error-unknown-code": "نازانتین خطای: \" $1 \"",
"duration-seconds": "$1 ثانیه",
"duration-minutes": "$1 دقیقه",
"duration-hours": "$1 سائت",
"duration-decades": "$1 دههگ",
"duration-centuries": "$1 قرن",
"duration-millennia": "{{PLURAL:$1|هزار سال |$1 هزار سال}}",
+ "limitreport-walltime": "واقئین مصرفئ مدت",
+ "limitreport-walltime-value": "$1 {{PLURAL:$1|ثانیه|ثانیه}}",
+ "limitreport-postexpandincludesize-value": "$1/$2 {{PLURAL:$2|بایٹ|بایٹ}}",
+ "limitreport-templateargumentsize": "ارگومان ئی تراشوانی اندازگ",
+ "limitreport-templateargumentsize-value": "$1/$2 {{PLURAL:$2|بایٹ|بایٹ}}",
+ "limitreport-expansiondepth": "گیشتیرین پراخی جُهلی",
+ "expandtemplates": "تراشوانی اکسپاند کورتین",
+ "expand_templates_title": "موضوع ئی ئنوان، په {{FULLPAGENAME}} ئا و غیره:",
+ "expand_templates_input": "ورودین متن:",
"expand_templates_output": "نتیجه",
"expand_templates_xml_output": "خروجی XML",
+ "expand_templates_html_output": "اچتیامال حامگین خروجی",
"expand_templates_ok": "قبول داشتین",
+ "expand_templates_remove_comments": "ملاحظاتانئ پاک کورتین",
+ "expand_templates_remove_nowiki": "خنثی کورتین <nowiki> تگ هانی بئ نتیجه ئی",
+ "expand_templates_generate_xml": "XML تجزیه ئی درختی نشان داتین",
+ "expand_templates_generate_rawhtml": "حامیگین اچ تی ام ال ئی نشان داتین",
+ "expand_templates_preview": "دیم دیست",
"pagelang-name": "تاکدیم",
"pagelang-language": "زبان",
"pagelang-use-default": "استفاده کورتین شه پیش فرض ئین زبانا",
"mediastatistics-header-text": "متنی",
"mediastatistics-header-executable": "اجرایی",
"json-error-unknown": "مشکلی گو جیسن ات. خطا: $1",
+ "json-error-state-mismatch": "جن سن جووان نه اینت یا ناقض اینت",
"json-error-syntax": "نحوی ئین خطا",
"json-error-inf-or-nan": "INF یا NAN ئی مقادیر یک یا گیشتیر بی مقداری که کدگذاری ئا بیئنت",
"json-error-unsupported-type": "یک اندازه که نتوانت کد گذاری بیئت داته بوته"
"exif-bitspersample": "yew parçe de biti",
"exif-compression": "Planê kompresyoni",
"exif-photometricinterpretation": "Compozisyonê pixeli",
- "exif-orientation": "Oriyentasyon",
+ "exif-orientation": "Berhetkerdış",
"exif-samplesperpixel": "teneyê parçeyi",
"exif-planarconfiguration": "Rezeyê datayi",
"exif-ycbcrsubsampling": "Subsampleyi ebatê Y heta C",
"tog-numberheadings": "Керташкашта аланза таьрахьа хотта",
"tog-showtoolbar": "Г|алатнийcдара г|ирсагартакх хьахьокха (JavaScript)",
"tog-editondblclick": "Шозза д|ато|амцa oаг|ув хувца (JavaScript)",
- "tog-editsectiononrightclick": "Ð\94екÑ\8aам Ñ\85Ñ\83вÑ\86а кеÑ\80Ñ\82мÑ\83гÓ\80а аÑ\8cÑ\82Ñ\82а Ñ\86лиÑ\86ака Ñ\8f (JavaScript)",
+ "tog-editsectiononrightclick": "РалÑ\81декÑ\8aаÑ\80аÑ\88 Ñ\85Ñ\83вÑ\86а даÑ\85каÑ\86а аÑ\8cÑ\82Ñ\82а д|аÑ\82о|амÑ\86а кеÑ\80Ñ\82аÑ\88ка Ñ\82|а (JavaScript)",
"tog-watchcreations": "Tеркама хьат|аяздар т|а аз яь оаг|онаши чуяьккха паьлаши т|атоха",
"tog-watchdefault": "Tеркама хьат|аяздар т|а аз хийца оаг|онаши паьлаша кустяздараши т|атоха",
"tog-watchmoves": "Tеркама хьат|аяздар т|а аз ц|ихийца оагӀонаши паьлаши т|атоха",
"tog-showhiddencats": "Къайла катагаш гойта",
"underline-always": "Даиман",
"underline-never": "Ц|аккха",
- "underline-default": "МазабӀарглокхарий оттамаш хайрамбе",
+ "underline-default": "Мазаб|арглокхарa оттамаш хайраде",
"editfont-style": "Нийсдара меттига чу йола зарба б|армат:",
+ "editfont-monospace": "Башхалон зарба",
"sunday": "К|иранди",
"monday": "Оршот",
"tuesday": "Шинара",
"oct": "Тов.",
"nov": "Лайч.",
"dec": "Чант.",
+ "january-date": "Нажгамсхой $1",
+ "february-date": "Саькур $1",
+ "march-date": "Муттхьол $1",
+ "april-date": "Тушоли $1",
+ "may-date": "Бекарг $1",
+ "june-date": "Аьтинг $1",
+ "july-date": "К|имарс $1",
+ "august-date": "Мангал $1",
+ "september-date": "Моажол $1",
+ "october-date": "Тов $1",
+ "november-date": "Лайчил $1",
+ "december-date": "Чантар $1",
"pagecategories": "{{PLURAL:$1|1=Катаг|Катагаш}}",
"category_header": "\"$1\" Катага чура оаг|онаш",
"subcategories": "Чуракатагаш",
"category-empty": "''Укх катага чу цхьаккха оаг|онаш е паьлаш яц.''",
"hidden-categories": "{{PLURAL:$1|1=Къайла катаг|Къайла катагаш}}",
"hidden-category-category": "Къайла катагаш",
- "category-subcat-count": "{{PLURAL:$2|Ð\99ола каÑ\82аг Ñ\82Ó\80еÑ\85Ñ\8cаÑ\80а бÑ\83Ñ\85каÑ\82аг Ñ\87Ñ\83лоаÑ\86а.|{{PLURAL:$1|1=$1 бÑ\83Ñ\85каÑ\82аг Ñ\85Ñ\8cаÑ\85Ñ\8cекÑ\85а Ñ\8f|$1 бÑ\83Ñ\85каÑ\82агаÑ\88 Ñ\85Ñ\8cаÑ\85Ñ\8cекÑ\85а Ñ\8f}} $2 йолаÑ\87аÑ\80а.}}",
- "category-subcat-count-limited": "Укх катагий {{PLURAL:$1|1=$1 кӀалкатаг|$1 кӀалкатагаш}}.",
- "category-article-count": "{{PLURAL:$2|Ð\99ола Ñ\86аÑ\82ег Ñ\86Ñ\85Ñ\8cа оагÓ\80Ñ\83в маÑ\80а Ñ\87Ñ\83лоаÑ\86аÑ\86.|{{PLURAL:$1|1=$1 оагÓ\80Ñ\83в Ñ\85Ñ\8cаÑ\85екÑ\85а Ñ\8f|$1 оагÓ\80Ñ\83внаÑ\88 Ñ\85Ñ\8cаÑ\85екÑ\85а Ñ\8f}} Ñ\83кÑ\85 Ñ\86аÑ\82ега $2 йолаÑ\87аÑ\80ах.}}",
- "category-article-count-limited": "Укх катагач {{PLURAL:$1|1=$1 оагӀув|$1 оагӀувнаш}}.",
- "category-file-count": "{{PLURAL:$2|Укх цатего ца паьла мара чулоацац.|{{PLURAL:$1|1=$1 паьла хьахьекха я|$1 паьлаш хьахьекха я}} укх цатегий $2 долачаьрахь.}}",
- "category-file-count-limited": "Укх катагач {{PLURAL:$1|1=$1 паьл|$1 паьлаш}}.",
+ "category-subcat-count": "{{PLURAL:$2|УкÑ\85 каÑ\82агa Ñ\82|еÑ\85Ñ\8cаÑ\80а бÑ\83Ñ\85каÑ\82аг Ñ\87Ñ\83лоаÑ\86.|{{PLURAL:$1|1=$1 бÑ\83Ñ\85каÑ\82аг Ñ\85Ñ\8cаÑ\85Ñ\8cекÑ\85а Ñ\8f|$1 бÑ\83Ñ\85каÑ\82агаÑ\88 Ñ\85Ñ\8cаÑ\85Ñ\8cекÑ\85а Ñ\8f}} $2 йолаÑ\87аÑ\80ex.}}",
+ "category-subcat-count-limited": "Укх катагa чу {{PLURAL:$1|1=$1 к|алкатаг|$1 к|алкатагаш}}.",
+ "category-article-count": "{{PLURAL:$2|УкÑ\85 каÑ\82ага Ñ\86Ñ\85Ñ\8cа оаг|Ñ\83в маÑ\80а Ñ\87Ñ\83лоаÑ\86аÑ\86.|{{PLURAL:$1|1=$1 оаг|Ñ\83в Ñ\85Ñ\8cаÑ\85екÑ\85а Ñ\8f|$1 оаг|oнаÑ\88 Ñ\85Ñ\8cаÑ\85екÑ\85а Ñ\8f}} Ñ\83кÑ\85 каÑ\82ага $2 йолаÑ\87аÑ\80eх.}}",
+ "category-article-count-limited": "Укх катага чу {{PLURAL:$1|1=$1 оаг|ув|$1 оаг|oнаш}}.",
+ "category-file-count": "{{PLURAL:$2|Укх катаг чу цхьа лурдар мара дац.|{{PLURAL:$1|1=$1 лурдар хьахьекха я|$1 лурдараш хьахьекха я}} укх катагa $2 долачаьрeх.}}",
+ "category-file-count-limited": "Укх катага чу {{PLURAL:$1|1=$1 лурдар|$1 лурдараш}}.",
"listingcontinuesabbrev": "д|ахо",
"index-category": "Д|ахьожама оаг|онаш",
"noindex-category": "Д|ахьожаманза оаг|онаш",
"newwindow": "(кердача коре)",
"cancel": "Эшац",
"moredotdotdot": "Д|ахо",
- "mypage": "Са оагӀув",
+ "morenotlisted": "Ер |ояздар хьалдиззанз да.",
+ "mypage": "Oаг|ув",
"mytalk": "Дувцам",
"anontalk": "Укх IP-меттига дувцам",
"navigation": "Наькъатохкарг",
"actions": "Х|амдараш",
"namespaces": "Ц|ерий аренаш",
"variants": "Дешкепаш",
+ "navigation-heading": "Наькъагойтара хоржаг|ирс",
"errorpagetitle": "Г|алат",
"returnto": "цу $1 оаг|он т|а юхаг|о",
"tagline": "Кечал укхазара я {{SITENAME}}",
"permalink": "Даиман латташ йола хьожадерг",
"print": "Кепатохар",
"view": "Б|аргтассам",
+ "view-foreign": "Мазаоаг|он чу $1 хьажа",
"edit": "Хувца",
"create": "Хьаде",
"editthispage": "Ер оаг|ув хувца",
"create-this-page": "Ep oаг|ув хьае",
"delete": "Д|аяккха",
"deletethispage": "Ер оаг|ув д|аяьккха",
+ "undeletethispage": "Ер оаг|ув д|аяккханз йита",
"undelete_short": "Меттаоттае {{PLURAL:$1|1=хувцам|$1 хувцамаш}}",
- "viewdeleted_short": "БӀаргтасса {{PLURAL:$1|1=дӀадаьккха хувцам тӀа|$1 дӀадаьккха хувцамаш тӀа}}",
+ "viewdeleted_short": "Б|аргтасса {{PLURAL:$1|1=д|адаьккха хувцам|$1 д|адаьккха хувцамаш}}",
"protect": "Лораде",
"protect_change": "хувца",
"protectthispage": "Лорае ер оаг|ув",
"otherlanguages": "Кхыча меттаех",
"redirectedfrom": "($1 тӀера хьадейта да)",
"redirectpagesub": "ДӀа-хьа дайта оагӀув",
+ "redirectto": "Д|ахьожаде укх т|а:",
"lastmodifiedat": "Укх оагӀув тӀехьара хувцам: $2, $1.",
- "viewcount": "Укх оагӀув тӀа бӀаргтасса хиннад {{PLURAL:$1|1=цхьазза|$1 шозза}}.",
+ "viewcount": "Укх оаг|oн т|а б|аргтассаб {{PLURAL:$1|цхьааца\n|$1 times}}. {{PLURAL:$1|1=цхьазза|$1за}}.",
"protectedpage": "Лорама оагӀув",
"jumpto": "Укхаза дехьа гӀо:",
"jumptonavigation": "никътохкарг",
"pool-timeout": "ЧIегатохара сабаран ха чакхаяьннай",
"pool-queuefull": "Хаттарий цӀа хьалдизад",
"pool-errorunknown": "Довзаш доаца гӀалат",
+ "poolcounter-usage-error": "Лелдара г|алат: $1",
"aboutsite": "Лоацам {{SITENAME}}",
"aboutpage": "Project:Лоацам",
"copyright": "$1 чулоацамаца тIакхоачаш да.",
"hidetoc": "къайладаккха",
"collapsible-collapse": "чудерзаде",
"collapsible-expand": "хьадоаржаде",
+ "confirmable-yes": "X|аа",
+ "confirmable-no": "A",
"thisisdeleted": "$1 бӀаргтасса е юхаметтаоттаде?",
"viewdeleted": "$1 бӀаргтасса?",
- "restorelink": "{{PLURAL:$1|1=дӀаяьккха хувцам|$1 дӀаяьккха хувцамаш}}",
+ "restorelink": "{{PLURAL:$1|1=д|адaьккха хувцам|$1 д|адaьккха хувцамаш}}",
"feedlinks": "Цу тайпара:",
"site-rss-feed": "$1 RSS мугӀ",
"site-atom-feed": "$1 Atom мугӀ",
"nosuchaction": "Цу тайпара дулархIам бац",
"nosuchspecialpage": "Изза мо гӀон оагӀув яц",
"error": "ГӀалат",
+ "databaseerror-query": "Дехар: $1",
+ "databaseerror-function": "Белхма|ан: $1",
+ "databaseerror-error": "Г|алат: $1",
"missing-article": "Кораде дезаш хинна оагӀувни яздам корадаьдац «$1» $2.\n\nИз мо гӀалат нийсалуш хула, саг тишъенна Ӏинкаца, д|адаьккха дола оагӀувни хувца искара тӀа чувала гӀертача.\n\nНаггахь санна из иштта децe, шоана гӀорса Ӏалаш деча гӀалат кораяь хила мега.\nДехар да, [[Special:ListUsers/sysop|мазакулгалхочоа]] хоам бе, URL хьахьокхаш.",
"missingarticle-rev": "(бӀаргоагӀув № $1)",
+ "missingarticle-diff": "(башх: $1, $2)",
"internalerror": "Чура гӀалат",
"internalerror_info": "Чура гӀалат: $1",
"cannotdelete-title": "ОагIув дIаяккха йиш яц \"$1\"",
"badtitletext": "Дехаш дола оагӀувни цӀи, нийса яц, яьсса я е меттаюкъара е массаюкъара цӀи харцахь я. ЦӀера юкъе мегаш доаца харакъаш нийсаденна хила мегаш да.",
"viewsource": "БIаргтассам",
"actionthrottled": "Сихален овзамал",
- "protectedpagetext": "Ð¥Ñ\83вÑ\86аман белÑ\85аÑ\88 долаÑ\88 еÑ\80 оагIÑ\83в кÑ\8aайла Ñ\8f.",
+ "protectedpagetext": "Ð\95Ñ\80 оаг|Ñ\83в кÑ\8aайла Ñ\8f Ñ\85Ñ\83вÑ\86амаÑ\88 деÑ\80гдоаÑ\86аÑ\88 е кÑ\85Ñ\8b дола Ñ\85|амдаÑ\80аÑ\88.",
"virus-unknownscanner": "довзашдоаца мазаундохьалург:",
+ "welcomeuser": "Маьрша доаг|алд, $1!",
"yourname": "Дакъалаьцархочунна цӀи:",
"yourpassword": "КъайладIоагӀа:",
"yourpasswordagain": "КъайладIоагӀа юха Ӏоязаде:",
"userlogin-resetlink": "Чувала/яла цӀии дIоагӀаи дийцаденнадий?",
"createaccountmail": "КъайладIоагIа д-хоамнец хьадайта",
"createaccountreason": "Бахьан:",
+ "createacct-reason": "Бахьан",
"badretype": "Оаша яьккха дIоагIий цIераш шоайл таралуш яц.",
"loginerror": "Дакъалаьцархочун цIи нийса яц",
- "mailmypassword": "Керда къайладIоагӀа хьаэца",
+ "mailmypassword": "Керда къайлад|оаг|а эца",
"mailerror": "Хоам дIабохьийташ гIалат даьннад: $1",
"emailconfirmlink": "Доаржален хоамни хьожадорг дIачIоагIаде",
"loginlanguagelabel": "Мотт: $1",
"rev-delundel": "хьахьокха/къайлаяьккха",
"rev-showdeleted": "хьахьокха",
"revdelete-show-file-submit": "XӀаа",
- "revdelete-radio-set": "XӀаа",
- "revdelete-radio-unset": "A",
+ "revdelete-radio-set": "Къайла",
+ "revdelete-radio-unset": "Гуш йола",
"revdelete-log": "Бахьан",
"revdel-restore": "Кустгойтам хувца",
"pagehist": "ОагӀува искар",
"search-section": " (дакъа $1)",
"search-suggest": "Iа лохар из хила мега: $1",
"search-interwiki-caption": "Гаргалон хьахьоадайтамаш",
- "search-interwiki-default": "$1 Ñ\82оламÑ\87аш:",
+ "search-interwiki-default": "$1 Ñ\85Ñ\8cаÑ\85иннаÑ\80аш:",
"search-interwiki-more": "(кха)",
"search-relatedarticle": "шоайл дола",
"searchrelated": "гаргара",
"timezoneregion-europe": "Аьроп",
"timezoneregion-indian": "ХIинда форд",
"timezoneregion-pacific": "Тийна форд",
- "prefs-searchoptions": "ТоÑ\85кама оÑ\82Ñ\82амаÑ\88",
+ "prefs-searchoptions": "Ð\9bаÑ\85аÑ\80",
"prefs-files": "Паьлаш",
"youremail": "Д-хоамни:",
- "username": "Дакъалаьцархочунна цIи:",
+ "username": "{{GENDER:$1|Доакъошхочун ц|и}}:",
"yourrealname": "Шун цIи:",
"yourlanguage": "Мотт:",
- "gender-male": "МаIа",
- "gender-female": "Ð\9aÑ\85ал",
+ "gender-male": "Массаоаг|онаш нийсaеш ва из",
+ "gender-female": "Ð\9cаÑ\81Ñ\81аоаг|онаÑ\88 нийÑ\81аеÑ\88 Ñ\8f из",
"email": "Д-хоамни",
"prefs-help-email": "Д-хоамни моттиг ала эшаш дац, амма новкъа даца, наггахь санна къайладIоагIа шоана дийцалой, цу тIа хьатIадайтаргда.",
"prefs-help-email-others": "Кхыбола дакъалаьцархоша шоаца бувзам я йийшхургья шун оагIува тIа гIолла, д-хоамни хьаела ца езаш.",
"whatlinkshere-hidelinks": "$1 Iинкаш",
"whatlinkshere-hideimages": "$1 суртIинкаш",
"whatlinkshere-filters": "ЦIенъераш",
- "blockip": "Ð\94акÑ\8aалаÑ\8cÑ\86аÑ\80Ñ\85оÑ\87Ñ\83нна Ñ\87Iега бола",
+ "blockip": "УкÑ\85 {{GENDER:$1|доакÑ\8aоÑ\88Ñ\85оÑ\87оа}} Ñ\87|ега бола",
"ipboptions": "2 сахьат:2 hours,1 ди:1 day,3 ди:3 days,1 кIира:1 week,2 кIира:2 weeks,1 бутт:1 month,3 бутт:3 months,6 бутт:6 months,1 шу:1 year,сиха ца луш:infinite",
"ipblocklist": "ЧIега бела дакъалаьцархой",
"blocklink": "чIегa тоха",
"Zylbath",
"לערי ריינהארט",
"아라",
- "Kolega2357"
+ "Kolega2357",
+ "Servien"
]
},
"tog-underline": "Verwies ünnerstrieken",
"login-abort-generic": "Dien Anmellen harr keen Spood. Dat is afbroken worrn.",
"loginlanguagelabel": "Spraak: $1",
"suspicious-userlogout": "Dien Anfraag, di aftomellen, worr aflehnt, wieldat se vermoodlich vun en Browser oder Cache-Proxy sennt worrn is, de nich mehr funkschoneert.",
+ "pt-userlogout": "Afmellen",
"php-mail-error-unknown": "Unbekennten Fehler in PHP sien mail()-Funkschoon",
"user-mail-no-addy": "Versöch en E-Mail ahn E-Mail-Adress to sennen.",
"changepassword": "Passwoort ännern",
"delete-toobig": "Disse Siet hett en temlich lange Versionsgeschicht vun mehr as {{PLURAL:$1|ene Version|$1 Versionen}}. Dat Wegsmieten kann de Datenbank vun {{SITENAME}} för längere Tied utlasten un den Bedriev vun dat Wiki stöörn.",
"delete-warning-toobig": "Disse Siet hett en temlich lange Versionsgeschicht vun mehr as {{PLURAL:$1|ene Version|$1 Versionen}}. Dat Wegsmieten kann de Datenbank vun {{SITENAME}} för längere Tied utlasten un den Bedriev vun dat Wiki stöörn.",
"rollback": "Trüchnahm vun de Ännern",
- "rollback_short": "Trüchnehmen",
"rollbacklink": "Trüchnehmen",
"rollbackfailed": "Trüchnahm hett kenen Spood",
"cantrollback": "De Ännern kann nich trüchnahmen warrn; de letzte Autor is de eenzige.",
"watchlisttools-edit": "Oppasslist ankieken un ännern",
"watchlisttools-raw": "Oppasslist as Textlist ännern",
"signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|Diskusschoon]])",
- "unknown_extension_tag": "Unbekannt Extension-Tag „$1“",
"duplicate-defaultsort": "Wohrschau: De DEFAULTSORTKEY „$2“ överschrifft den vörher bruukten Slötel „$1“.",
"version": "Version",
"version-extensions": "Installeerte Extensions",
"specialpages-group-wiki": "Systemdaten un Warktüüch",
"specialpages-group-redirects": "Redirect-Spezialsieden",
"specialpages-group-spam": "Spam-Warktüüch",
+ "specialpages-group-developer": "Developer-Warktüüch",
"blankpage": "Leddige Sied",
"intentionallyblankpage": "Disse Sied is mit Afsicht leddig.",
"external_image_whitelist": " #Disse Reeg nich ännern<pre>\n#Ünnen köönt Delen vun reguläre Utdrück (de Deel twischen de //) angeven warrn.\n#De warrt mit de URLs vun Biller ut externe Borns vergleken\n#En positiv Vergliek föhrt dorto, dat dat Bild wiest warrt, ans warrt dat Bild blot as Lenk wiest\n#Regen, de mit en # anfangt, warrt as Kommentar behannelt\n#De List maakt keen Ünnerscheed bi grote un lütte Bookstaven\n\n#Delen vun reguläre Utdrück na disse Reeg indragen. Disse Reeg nich ännern</pre>",
"prefs-personal": "Profil dl'utent",
"prefs-rc": "Ùltime modìfiche",
"prefs-watchlist": "Ròba che as ten sot euj",
+ "prefs-editwatchlist": "Modifiché la lista ëd lòn ch'as ten sot-euj",
+ "prefs-editwatchlist-label": "Modifiché dle vos ëd la lista ëd lòn ch'as ten sot-euj:",
+ "prefs-editwatchlist-edit": "Vëdde e gavé dle vos ëd la lista ëd lòn ch'as ten sot-euj",
+ "prefs-editwatchlist-raw": "Modìfica lesta ëd la lista ëd lòn ch'as ten sot-euj",
+ "prefs-editwatchlist-clear": "Dësvujdé la lista ëd lòn ch'as ten sot-euj",
"prefs-watchlist-days": "Vàire dì che a veul ës-ciairé an soa lista ëd lòn che as ten sot euj:",
"prefs-watchlist-days-max": "Al pì $1 {{PLURAL:$1|di|di}}",
"prefs-watchlist-edits": "Vàire modìfiche che a veul ës-ciairé con le funsion avansà:",
"right-override-export-depth": "Esporté le pàgine ancludend le pàgine colegà fin-a a na profondeur ëd 5",
"right-sendemail": "Mandé un mëssagi an pòsta eletrònica a j'àutri utent",
"right-passwordreset": "Vëdde ij mëssagi ëd pòsta eletrònica ëd riampostassion dle ciav",
+ "right-managechangetags": "Creé e dëscancelé dle [[Special:Tags|tichëtte]] da la base ëd dàit",
"newuserlogpage": "Registr dla creassion dj'utent",
"newuserlogpagetext": "Sossì a l'é un registr andova ch'as marco le creassion dj'utent.",
"rightslog": "Argistr dij drit ëd j'utent",
"action-viewmyprivateinfo": "vëdde soe anformassion përsonaj",
"action-editmyprivateinfo": "modifiché soe anformassion përsonaj",
"action-editcontentmodel": "modifiché ël model ëd contnù ëd na pàgina",
+ "action-managechangetags": "creé e dëscancelé dle tichëtte da la base ëd dàit",
"nchanges": "$1 {{PLURAL:$1|modìfica|modìfiche}}",
"enhancedrc-since-last-visit": "$1 {{PLURAL:$1|da l'ùltima visita}}",
"enhancedrc-history": "stòria",
"tags-tag": "Nòm ëd la tichëtta",
"tags-display-header": "Aparensa ant la lista dle modìfiche",
"tags-description-header": "Descrission completa dël significà",
+ "tags-source-header": "Sorgiss",
"tags-active-header": "Ativ?",
"tags-hitcount-header": "Modìfiche con tichëtta",
"tags-active-yes": "Bò",
"Marcos dias de oliveira",
"He7d3r",
"PauloEduardo",
- "Webysther"
+ "Webysther",
+ "Fasouzafreitas"
]
},
"tog-underline": "Sublinhar links:",
"prefs-editwatchlist-label": "Editar entradas na sua lista de páginas vigiadas:",
"prefs-editwatchlist-edit": "Visualizar e remover títulos da sua lista de páginas vigiadas",
"prefs-editwatchlist-raw": "Edição crua da lista de páginas vigiadas",
+ "prefs-editwatchlist-clear": "Limpar sua lista de páginas vigiadas",
"prefs-watchlist-days": "Dias a mostrar na lista de páginas vigiadas:",
"prefs-watchlist-days-max": "Máximo $1 {{PLURAL:$1|dia|dias}}",
"prefs-watchlist-edits": "Número de edições mostradas na lista de páginas vigiadas expandida:",
"right-override-export-depth": "Exportar páginas incluindo páginas ligadas até uma profundidade de 5",
"right-sendemail": "Enviar email a outros usuários",
"right-passwordreset": "Ver todos os e-mails de reposição de senhas",
+ "right-managechangetags": "Criar e apagar [[Special:Tags|tags]] na base de dados",
"newuserlogpage": "Registro de criação de usuários",
"newuserlogpagetext": "Este é um registro de novas contas de usuário",
"rightslog": "Registro de privilégios de usuário",
"action-viewmyprivateinfo": "veja suas informações privadas",
"action-editmyprivateinfo": "modifique suas informações privadas",
"action-editcontentmodel": "editar o conteudo do modelo de uma pagina",
+ "action-managechangetags": "Criar e apagar etiquetas da base de dados",
"nchanges": "$1 {{PLURAL:$1|alteração|alterações}}",
"enhancedrc-since-last-visit": "$1 {{PLURAL:$1|desde a última visita}}",
"enhancedrc-history": "histórico",
"tooltip-feed-atom": "Feed Atom desta página",
"tooltip-t-contributions": "Ver as contribuições deste usuário",
"tooltip-t-emailuser": "Enviar um e-mail a este usuário",
+ "tooltip-t-info": "Mais informações sobre esta página",
"tooltip-t-upload": "Enviar arquivos",
"tooltip-t-specialpages": "Lista de páginas especiais",
"tooltip-t-print": "Versão para impressão desta página",
"version-entrypoints": "URLs dos pontos de entrada",
"version-entrypoints-header-entrypoint": "Ponto de entrada",
"version-entrypoints-header-url": "URL",
+ "version-libraries": "Bibliotecas instaladas",
+ "version-libraries-library": "Biblioteca",
+ "version-libraries-version": "Versão",
"redirect": "Redirecionar por arquivo, usuário ou ID de revisão",
"redirect-legend": "Redirecionar para um arquivo ou página",
"redirect-summary": "Esta página especial redireciona a um arquivo (dado o nome do arquivo), a uma página (dado um ID de revisão ou ID da página) ou a uma página de usuário (dado o ID do usuário). Uso: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]], or [[{{#Special:Redirect}}/user/101]].",
"tags-tag": "Nome da etiqueta",
"tags-display-header": "Aparência nas listas de modificações",
"tags-description-header": "Descrição completa do significado",
+ "tags-source-header": "Fonte",
"tags-active-header": "Ativo?",
"tags-hitcount-header": "Modificações etiquetadas",
+ "tags-actions-header": "Ações",
"tags-active-yes": "Sim",
"tags-active-no": "Não",
+ "tags-source-extension": "Definida por uma extensão",
+ "tags-source-manual": "Aplicado manualmente pelos usuários e pelos robôs",
+ "tags-source-none": "Não mais em uso",
"tags-edit": "editar",
+ "tags-delete": "Apagar",
+ "tags-activate": "Ativar",
+ "tags-deactivate": "Desativar",
"tags-hitcount": "$1 {{PLURAL:$1|modificação|modificações}}",
+ "tags-manage-no-permission": "Você não possui permissão para gerenciar alterações de etiquetas",
+ "tags-create-heading": "Criar uma nova etiqueta",
+ "tags-create-explanation": "Por padrão, etiquetas recém-criadas serão disponibilizadas para usuários e robôs",
+ "tags-create-tag-name": "Nome de etiqueta",
+ "tags-create-reason": "Razão:",
+ "tags-create-submit": "Criar",
+ "tags-create-no-name": "Você deve especificar um nome de etiqueta",
+ "tags-create-invalid-chars": "Nomes de etiquetas não devem conter vírgulas (<code>,</code>) ou barras (<code>/</code>).",
+ "tags-create-invalid-title-chars": "Nomes de etiqueta não devem conter caracteres que não possam ser utilizados em títulos de páginas.",
+ "tags-create-already-exists": "A etiqueta \"$1\" já existe.",
+ "tags-create-warnings-below": "Você deseja continuar a criar a etiqueta?",
+ "tags-delete-title": "Apagar etiqueta",
+ "tags-delete-submit": "Apagar irreversivelmente esta etiqueta",
+ "tags-delete-not-allowed": "Etiquetas definidas por uma extensão não podem ser apagadas a menos que a extensão especificamente o permita.",
+ "tags-delete-not-found": "A etiqueta \"$1\" não existe.",
+ "tags-activate-title": "Ativar etiqueta",
+ "tags-activate-submit": "Ativar",
+ "tags-deactivate-title": "Desativar etiqueta",
+ "tags-deactivate-submit": "Desativar",
"comparepages": "Comparar páginas",
"compare-page1": "Página 1",
"compare-page2": "Página 2",
"compare-revision-not-exists": "A revisão que você especificou não existe.",
"dberr-problems": "Desculpe! Este sítio está passando por dificuldades técnicas.",
"dberr-again": "Experimente esperar alguns minutos e atualizar.",
- "dberr-info": "(Não foi possível contatar o servidor de base de dados: $1)",
+ "dberr-info": "(Não pode acessar a base de dados: $1)",
"dberr-info-hidden": "(Não foi possível contatar o banco de dados do servidor)",
"dberr-usegoogle": "Você pode tentar pesquisar no Google entretanto.",
"dberr-outofdate": "Note que os seus índices relativos ao nosso conteúdo podem estar desatualizados.",
"expand_templates_generate_xml": "Mostrar árvore de análise (parse) do XML",
"expand_templates_generate_rawhtml": "Mostrar HTML puro",
"expand_templates_preview": "Pré-visualização",
+ "pagelanguage": "Seletor de idioma de página",
"pagelang-name": "Página",
"pagelang-language": "Idioma",
"pagelang-use-default": "Idioma padrão de uso",
"action-viewmyprivateinfo": "просмотр вашей частной информации",
"action-editmyprivateinfo": "редактирование вашей частной информации",
"action-editcontentmodel": "редактирование контентной модели страницы",
+ "action-managechangetags": "создание и удаление меток из базы данных",
"nchanges": "$1 {{PLURAL:$1|изменение|изменения|изменений}}",
"enhancedrc-since-last-visit": "$1 {{PLURAL:$1|с последнего посещения}}",
"enhancedrc-history": "история",
"tags-active-yes": "Да",
"tags-active-no": "Нет",
"tags-source-extension": "Определяется расширением",
+ "tags-source-none": "Больше не используется",
"tags-edit": "править",
"tags-delete": "удалить",
"tags-activate": "активировать",
"tags-deactivate": "отключить",
"tags-hitcount": "$1 {{PLURAL:$1|изменение|изменения|изменений}}",
+ "tags-create-heading": "Создать новую метку",
"tags-create-tag-name": "Название метки:",
"tags-create-reason": "Причина:",
"tags-create-submit": "Создать",
"tags-create-no-name": "Вы должны указать имя метки.",
+ "tags-create-already-exists": "Метка «$1» уже существует.",
+ "tags-delete-title": "Удалить метку",
"tags-delete-reason": "Причина:",
+ "tags-activate-title": "Активировать метку",
+ "tags-activate-question": "Вы собираетесь активировать метку «$1».",
"tags-activate-reason": "Причина:",
+ "tags-activate-not-allowed": "Невозможно активировать метку «$1».",
+ "tags-activate-not-found": "Метка «$1» не существует.",
"tags-activate-submit": "Активировать",
+ "tags-deactivate-title": "Отключить метку",
"tags-deactivate-reason": "Причина:",
"tags-deactivate-submit": "Отключить",
"comparepages": "Сравнение страниц",
"logentry-upload-upload": "$1 загрузил{{GENDER:$2||а}} $3",
"logentry-upload-overwrite": "$1 загрузил{{GENDER:$2||а}} новую версию $3",
"logentry-upload-revert": "$1 загрузил{{GENDER:$2||а}} $3",
+ "log-name-managetags": "Журнал управления метками",
+ "logentry-managetags-create": "$1 создал{{GENDER:$2||а}} метку «$4»",
"rightsnone": "(нет)",
"revdelete-summary": "описание изменений",
"feedback-bugornote": "Если вы готовы подробно описать техническую проблему, пожалуйста, [$1 сообщите об ошибке].\nВ противном случае вы можете использовать данную простую форму. Ваш комментарий будет добавлен на страницу «[$3 $2]» вместе с вашим именем участника и используемым браузером.",
"prefs-personal": "Профил",
"prefs-rc": "Скорашње измене",
"prefs-watchlist": "Списак надгледања",
+ "prefs-editwatchlist": "Уређивање списка надгледања",
+ "prefs-editwatchlist-label": "Уређивање списка:",
+ "prefs-editwatchlist-edit": "Уреди списак",
+ "prefs-editwatchlist-raw": "Уреди сиров списак",
+ "prefs-editwatchlist-clear": "Испразни списак",
"prefs-watchlist-days": "Број дана у списку надгледања:",
"prefs-watchlist-days-max": "Највише $1 {{PLURAL:$1|дан|дана|дана}}",
"prefs-watchlist-edits": "Највећи број измена у проширеном списку надгледања:",
"watchlistedit-clear-legend": "Испразни списак надгледања",
"watchlistedit-clear-explain": "Сви наслови ће бити уклоњени из вашег списка надгледања.",
"watchlistedit-clear-titles": "Наслови:",
- "watchlistedit-clear-submit": "Испразни списак надгледања (Ово је трајно!)",
+ "watchlistedit-clear-submit": "Испразни списак надгледања (Ово је неповратно!)",
"watchlistedit-clear-done": "Ваш списак надгледања је испражњен.",
"watchlistedit-clear-removed": "{{PLURAL:$1|1 наслов је уклоњен|$1 наслова су уклоњена|$1 наслова је уклоњено}}:",
"watchlistedit-too-many": "Има превише страница за приказ овде.",
"watchlistedit-clear-legend": "Isprazni spisak nadgledanja",
"watchlistedit-clear-explain": "Svi naslovi će biti uklonjeni iz vašeg spiska nadgledanja.",
"watchlistedit-clear-titles": "Naslovi:",
- "watchlistedit-clear-submit": "Isprazni spisak nadgledanja (Ovo je trajno!)",
+ "watchlistedit-clear-submit": "Isprazni spisak nadgledanja (Ovo je nepovratno!)",
"watchlistedit-clear-done": "Vaš spisak nadgledanja je ispražnjen.",
"watchlistedit-clear-removed": "{{PLURAL:$1|1 naslov je uklonjen|$1 naslova su uklonjena|$1 naslova je uklonjeno}}:",
"watchlistedit-too-many": "Ima previše stranica za prikaz ovde.",
"action-viewmyprivateinfo": "visa din privata information",
"action-editmyprivateinfo": "redigera din privata information",
"action-editcontentmodel": "ändra innehållsmodellen för en sida",
+ "action-managechangetags": "skapa och radera taggar från databasen",
"nchanges": "$1 {{PLURAL:$1|ändring|ändringar}}",
"enhancedrc-since-last-visit": "$1 {{PLURAL:$1|sedan senaste besöket}}",
"enhancedrc-history": "historik",
"tags-activate": "aktivera",
"tags-deactivate": "inaktivera",
"tags-hitcount": "$1 {{PLURAL:$1|ändring|ändringar}}",
- "tags-manage-no-permission": "Du har inte behörighet att hantera förändrings taggar.",
+ "tags-manage-no-permission": "Du har inte behörighet att hantera förändringstaggar.",
"tags-create-heading": "Skapa en ny tag",
- "tags-create-explanation": "Som standard, nyskapade taggar kommer att bli tillgängliga för användning av användare och bots.",
+ "tags-create-explanation": "Som standard, kommer nyskapade taggar att bli tillgängliga för användning av användare och botar.",
"tags-create-tag-name": "Taggnamn:",
"tags-create-reason": "Anledning:",
"tags-create-submit": "Skapa",
"tags-create-no-name": "Du måste ange ett taggnamn.",
- "tags-create-invalid-chars": "Taggnamn får inte innehålla kommatecken ( <code>,</code> ) eller framåt snedstreck ( <code>/</code> ).",
+ "tags-create-invalid-chars": "Taggnamn får inte innehålla kommatecken (<code>,</code>) eller snedstreck (<code>/</code>).",
"tags-create-invalid-title-chars": "Taggnamn får inte innehålla tecken som inte kan användas i sidtitlar.",
"tags-create-already-exists": "Taggen \"$1\" finns redan.",
"tags-create-warnings-above": "Följande {{PLURAL:$2|varning |varningar}} stöttes på när du försöker skapa etiketten \" $1 \":",
"tags-create-warnings-below": "Vill du fortsätta att skapa taggen?",
"tags-delete-title": "Radera tagg",
"tags-delete-explanation-initial": "Du är på väg att ta bort taggen \"$1\" från databasen.",
- "tags-delete-explanation-warning": "Denna åtgärd är <strong>oåterkalleligt</strong> och <strong>kan inte ångras</strong>, inte ens av databasadministratörer. Var säker på att detta är taggen du vill radera.",
+ "tags-delete-explanation-warning": "Denna åtgärd är <strong>oåterkallelig</strong> och <strong>kan inte ångras</strong>, inte ens av databasadministratörer. Var säker på att detta är den tagg du vill radera.",
+ "tags-delete-explanation-active": "<strong>Taggen\" $1 \" är fortfarande aktiv, och kommer att fortsätta att appliceras i framtiden.</strong> För att hindra detta, gå till den eller de platser där taggen är inställd att användas, och inaktivera den där.",
"tags-delete-reason": "Anledning:",
- "tags-delete-submit": "Oåterkalleligt radera denna tagg",
+ "tags-delete-submit": "Radera denna tagg oåterkalleligen",
"tags-delete-not-found": "Taggen \"$1\" finns inte.",
+ "tags-delete-too-many-uses": "Taggen \"$1\" appliceras på mer än $2 {{PLURAL:$2|version|versioner}}, vilket innebär att den inte kan raderas.",
"tags-delete-warnings-after-delete": "Taggen \"$1\" raderades, men följande {{PLURAL:$2|varning|varningar}} inträffade:",
"tags-activate-title": "Aktivera tagg",
"tags-activate-question": "Du är på väg att aktivera taggen \"$1\".",
"logentry-upload-upload": "$1 {{GENDER:$2|laddade upp}} $3",
"logentry-upload-overwrite": "$1 {{GENDER:$2|laddade upp}} en ny version av $3",
"logentry-upload-revert": "$1 {{GENDER:$2|laddade upp}} $3",
- "log-name-managetags": "Tagg hanterings logg",
+ "log-name-managetags": "Tagghanterings logg",
"logentry-managetags-create": "$1 {{GENDER:$2|skapade}} taggen \"$4\"",
- "logentry-managetags-delete": "$1 {{GENDER:$2|raderade}} taggen \"$4\" (borttagen från $5 {{PLURAL:$5|revisionen eller loggpost |revisionerna och/eller loggposterna}})",
- "logentry-managetags-activate": "$1 {{GENDER:$2|aktiverade}} taggen \"$4\" för användning av användare och bots.",
- "logentry-managetags-deactivate": "$1 {{GENDER:$2|inaktiverade}} taggen \"$4\" för användning av användare och bots.",
+ "logentry-managetags-delete": "$1 {{GENDER:$2|raderade}} taggen \"$4\" (borttagen från $5 {{PLURAL:$5|version eller loggpost|versioner och/eller loggposter}})",
+ "logentry-managetags-activate": "$1 {{GENDER:$2|aktiverade}} taggen \"$4\" för användning av användare och botar.",
+ "logentry-managetags-deactivate": "$1 {{GENDER:$2|inaktiverade}} taggen \"$4\" för användning av användare och botar.",
"rightsnone": "(inga)",
"revdelete-summary": "sammanfattning",
"feedback-bugornote": "Om du har möjlighet att ge en detaljerad teknisk beskrivning av felet kan du lämna en [$1 buggrapport]. \nAnvänd annars formuläret nedan. Din kommentar kommer att läggas till på sidan \"[$3 $2]\", tillsammans med ditt användarnamn.",
"tags-create-invalid-chars": "Tên thẻ không được chứa dấu phẩy (<code>,</code>) hoặc dấu gạch chéo lên (<code>/</code>).",
"tags-create-invalid-title-chars": "Tên thẻ không được chứa các ký tự mà không thể được sử dụng trong tiêu đề của trang .",
"tags-create-already-exists": "Các từ khóa \"$1\" đã tồn tại.",
+ "tags-create-warnings-above": "{{PLURAL:$2| Cảnh báo}} sau gặp phải khi cố gắng để tạo ra các thẻ \"$1\":",
"tags-create-warnings-below": "Bạn có muốn tiếp tục tạo thẻ này?",
"tags-delete-title": "Xóa thẻ",
"tags-delete-explanation-initial": "Bạn muốn xóa thẻ \"$1\" từ cơ sở dữ liệu.",
"tags-delete-explanation-in-use": "Nó sẽ được gỡ bỏ từ {{PLURAL:$2|$2 mục sửa đổi hoặc mục đăng nhập|tất cả $2 bản sửa đổi và/hoặc đăng nhập các mục}} mà nó hiện đang áp dụng.",
+ "tags-delete-explanation-warning": "Hành động này là <strong>không thể đảo ngược</strong> và <strong>không thể hoàn tác</strong>, ngay cả bởi người quản trị cơ sở dữ liệu. Hãy chắc chắn đây là thẻ mà bạn muốn xóa.",
"tags-delete-reason": "Lý do:",
"tags-delete-submit": "Không thể phục hồi xóa thẻ này",
"tags-delete-not-allowed": "Thẻ được định nghĩa bởi một mở rộng không thể bị xóa trừ khi mở rộng đặc biệt cho phép điều đó xảy ra.",
"tags-activate-title": "Kích hoạt thẻ",
"tags-activate-question": "Bạn sắp sửa kích hoạt thẻ \"$1\".",
"tags-activate-reason": "Lý do:",
+ "tags-activate-not-allowed": "Không thể kích hoạt thẻ \"$1\".",
+ "tags-activate-not-found": "Thẻ \"$1\" không tồn tại.",
"tags-activate-submit": "Kích hoạt",
"tags-deactivate-title": "Vô hiệu thẻ",
+ "tags-deactivate-question": "Bạn sắp sửa vô hiệu thẻ \"$1\".",
"tags-deactivate-reason": "Lý do:",
+ "tags-deactivate-not-allowed": "Không thể vô hiệu hóa thẻ \"$1\".",
+ "tags-deactivate-submit": "Vô hiệu",
"comparepages": "So sánh trang",
"compare-page1": "Trang 1",
"compare-page2": "Trang 2",
"logentry-upload-upload": "$1 {{GENDER:$2}}đã tải lên $3",
"logentry-upload-overwrite": "$1 {{GENDER:$2}}đã tải lên một phiên bản mới của $3",
"logentry-upload-revert": "$1 {{GENDER:$2}}đã tải lên $3",
+ "log-name-managetags": "Danh sách quản lý thẻ",
+ "logentry-managetags-create": "$1 {{GENDER:$2| đã tạo}} thẻ \"$4\"",
"logentry-managetags-activate": "$1 {{GENDER:$2|đã kích hoạt}} tag \"$4\" để sử dụng bởi người dùng và các bot",
+ "logentry-managetags-deactivate": "$1 {{GENDER:$2|đã vô hiệu}} thẻ \"$4\" để sử dụng bởi người dùng và các bot",
"rightsnone": "(không có)",
"revdelete-summary": "tóm lược sửa đổi",
"feedback-bugornote": "Nếu bạn đã sẵn sàng để miêu tả các chi tiết của một vấn đề kỹ thuật, xin vui lòng [$1 báo cáo lỗi].\nNếu không thì bạn có thể điền biểu mẫu đơn giản ở dưới. Lời ghi của bạn sẽ được đăng lên trang “[$3 $2]”, cùng với tên người dùng và trình duyệt của bạn.",
"noindex-category": "Келкгдшго халхс",
"about": "Бичлһн",
"article": "Зүүл",
- "newwindow": "(Ñ\88ин Ñ\82еÑ\80зд)",
- "cancel": "Уга кех",
- "moredotdotdot": "ЦааÑ\80анднь...",
+ "newwindow": "(Ñ\88ин Ñ\86оңÑ\85Ñ\82)",
+ "cancel": "Ð\91Ñ\83Ñ\86х",
+ "moredotdotdot": "Ð\9dань...",
"mypage": "Халх",
"mytalk": "Меткән",
- "anontalk": "IP хайгна күндллһн",
- "navigation": "Ð\9eÑ\80м медлһн",
+ "anontalk": "Эн IP хайгин меткән",
+ "navigation": "Ð\90йлл",
"and": " болн",
- "qbfind": "Ð¥Ó\99Ó\99лһн",
- "qbbrowse": "Ð\93Ò¯Ò¯Ò»Ó\99д Ñ\85әләх",
- "qbedit": "Чиклх",
- "qbpageoptions": "ТеÑ\80 халх",
- "qbmyoptions": "Тана халхс",
- "faq": "Юм би",
- "faqpage": "Project:Юм би",
- "actions": "Үүлд",
- "namespaces": "Ð\9dеÑ\80нÓ\99 Ñ\83Ñ\81",
- "variants": "СÑ\83ңһлÑ\82Ñ\81",
+ "qbfind": "Ð¥Ó\99Ó\99вÑ\80",
+ "qbbrowse": "Ð¥әләх",
+ "qbedit": "ЯÑ\81х",
+ "qbpageoptions": "Ðн халх",
+ "qbmyoptions": "Ð\9cини халхс",
+ "faq": "СÑ\83Ñ\80мһа Ñ\81Ñ\83Ñ\80вÑ\80",
+ "faqpage": "Project:СÑ\83Ñ\80мһа Ñ\81Ñ\83Ñ\80вÑ\80",
+ "actions": "Үүлдвр",
+ "namespaces": "Ð\9dеÑ\80нÓ\99 Ñ\82Ó©Ñ\80л",
+ "variants": "ХүвлвÑ\80",
"errorpagetitle": "Эндү",
- "returnto": "«$1» Ñ\82ал Ñ\85Ó\99Ñ\80Ò¯ иÑ\80х.",
- "tagline": "{{grammar:genitive|{{SITENAME}}}} гидг һазрас өггцн",
- "help": "ЦÓ\99Ó\99лһлһн",
- "search": "Ð¥Ó\99Ó\99лһн",
- "searchbutton": "Хәәлһн",
- "go": "Ор",
- "searcharticle": "Ор",
- "history": "тууҗ",
+ "returnto": "«$1» Ñ\82ал Ñ\85Ó\99Ñ\80Ò¯ одх.",
+ "tagline": "{{SITENAME}} талас",
+ "help": "Ð\94Ó©Ò£",
+ "search": "Ð¥Ó\99Ó\99вÑ\80",
+ "searchbutton": "Хәәх",
+ "go": "Одх",
+ "searcharticle": "Одх",
+ "history": "Халхин тууҗ",
"history_short": "Тууҗ",
- "updatedmarker": "мини шидрә орлһна хөөн шинрүлсн",
+ "updatedmarker": "мини отхн орсна хөөн шинрүлсн",
"printableversion": "Барин бәәдл",
"permalink": "Даңгин заалһ",
"print": "Барлх",
- "edit": "Чиклх",
+ "edit": "ЯÑ\81х",
"create": "Бүтәх",
- "editthispage": "Эн халхиг чиклх",
- "create-this-page": "Эн халхиг бүтәх",
- "delete": "Һарһх",
- "deletethispage": "Эн халхиг һарһх",
+ "editthispage": "Эн халх ясх",
+ "create-this-page": "Эн халх бүтәх",
+ "delete": "Әрлһх",
+ "deletethispage": "Эн халх әрлһх",
"undelete_short": "{{PLURAL:$1|Нег ясвр|$1 ясвр|ясвр}} босхх",
"protect": "Харсх",
"protect_change": "сольх",
- "protectthispage": "Эн халхиг харсх",
+ "protectthispage": "Эн халх харсх",
"unprotect": "Харслт сольх",
"unprotectthispage": "Эн халхин харслт сольх",
"newpage": "Шин халх",
- "talkpage": "ТеÑ\80 Ñ\85алÑ\85ин Ñ\82Ñ\83Ñ\81к келх",
+ "talkpage": "Ðн Ñ\85алÑ\85 меÑ\82клдх",
"talkpagelinktext": "Меткән",
- "specialpage": "Ð\9aөдлÑ\85нÓ\99 халх",
- "personaltools": "ÐвÑ\80Ó\99н зеÑ\80-зев",
- "articlepage": "Зүүл үзх",
+ "specialpage": "ШиÑ\88лң халх",
+ "personaltools": "ТÑ\83Ñ\81лң зевÑ\81г",
+ "articlepage": "Зүүл хәләх",
"talk": "Меткән",
"views": "Хәләврүд",
"toolbox": "Зевсг",
- "userpage": "Демнчна халх үзх",
- "projectpage": "Төсвин халх үзх",
- "imagepage": "Боомгин халх үзх",
- "mediawikipage": "Зәңгин халх үзх",
- "templatepage": "Кевләр халх үзх",
- "viewhelppage": "ЦÓ\99Ó\99лһлһиг Ñ\83зх",
- "categorypage": "Әәшлин халх үзх",
+ "userpage": "Демнәчнә халх хәләх",
+ "projectpage": "Төсвин халх хәләх",
+ "imagepage": "Боомгин халх хәләх",
+ "mediawikipage": "Зәңгин халх хәләх",
+ "templatepage": "Кевләрин халх хәләх",
+ "viewhelppage": "Ð\94өңгин Ñ\85алÑ\85 Ñ\85Ó\99лÓ\99х",
+ "categorypage": "Әәшлин халх хәләх",
"viewtalkpage": "Меткән халх узх",
- "otherlanguages": "Талдан келәр",
- "redirectedfrom": "($1 гидг һазрас авч одсмн)",
- "redirectpagesub": "Ð\90вÑ\87 оддг халх",
- "lastmodifiedat": "ТеÑ\80 Ñ\85алÑ\85 Ñ\8dн Ñ\86агÑ\82 Ñ\81үл Ñ\87иклÓ\99д болÒ\97: $2, $1.",
+ "otherlanguages": "Ð\91Ñ\83Ñ\81 келәр",
+ "redirectedfrom": "($1 талас туусн)",
+ "redirectpagesub": "ТÑ\83Ñ\83дг халх",
+ "lastmodifiedat": "Ðн Ñ\85алÑ\85ин оÑ\82Ñ\85н Ñ\81олÑ\8cÑ\81н: $2, $1.",
"viewcount": "Эн халхт {{PLURAL:$1|нег дәкҗ|$1 дәкҗ}} хандла.",
"protectedpage": "Харссн халх",
- "jumpto": "Ð\98Ñ\80х тал:",
- "jumptonavigation": "Һазр медлһн",
- "jumptosearch": "Ñ\85Ó\99Ó\99лһн",
- "view-pool-error": "Гемим тәвтн, ода серверүд хар-хату көдлмштә.\nДегд дала күн тер халх үзхәр бәәнә.\nБуйн болтха, бәәҗәһәд дәкәд арһ хәәтн.\n\n$1",
- "aboutsite": "{{SITENAME}} тускар",
- "aboutpage": "Project:ТодлÒ\97 бичлһн",
+ "jumpto": "Ð\9eдх тал:",
+ "jumptonavigation": "айлл",
+ "jumptosearch": "Ñ\85Ó\99Ó\99вÑ\80",
+ "view-pool-error": "Гем тәвтн, ода серверуд күндрҗәнә.\nДегд олн халх хәләх сурлт орв.\nБуйн болтха, күләһәд халхт хандх седвәр бәән давттн.\n\n$1",
+ "aboutsite": "{{SITENAME}} туск",
+ "aboutpage": "Project:Ð\91ичлһн",
"copyright": "Бус эс гихлә, дотр $1 зөвшәрләр орлһта.",
- "copyrightpage": "{{ns:project}}:Ð\91иÑ\87Ñ\81н күүнÓ\99 зөв",
- "currentevents": "Ода болсн йовдл",
- "currentevents-url": "Project:Ода болсн йовдл",
- "disclaimers": "Дааврас эс зөвшәрлһн",
- "disclaimerpage": "Project:Даарас эс зөвшәрлһн",
- "edithelp": "ЧикллһнÓ\99 дөң",
+ "copyrightpage": "{{ns:project}}:Ð\97окÑ\8aÑ\8fÑ\87ин зөв",
+ "currentevents": "Өдгәк йовдл",
+ "currentevents-url": "Project:Өдгәк йовдл",
+ "disclaimers": "Даавран буцлт",
+ "disclaimerpage": "Project:Даавран буцлт",
+ "edithelp": "ЯÑ\81вÑ\80ин дөң",
"mainpage": "Нүр халх",
"mainpage-description": "Нүр халх",
"policy-url": "Project:Бодлһн",
- "portal": "Ð\91Ò¯Ñ\80дÓ\99Ñ\86ин хург",
- "portal-url": "Project:Ð\91Ò¯Ñ\80дÓ\99Ñ\86ин хург",
- "privacy": "Нууцин бодлһн",
- "privacypage": "Project:Нууцин бодлһн",
- "badaccess": "Зөвәнә эндү",
- "badaccess-group0": "Та сурсн үүл кеҗ болшго.",
+ "portal": "Ð\9dииÑ\86Ó\99нÓ\99 хург",
+ "portal-url": "Project:Ð\9dииÑ\86Ó\99нÓ\99 хург",
+ "privacy": "Нуултын бодлһн",
+ "privacypage": "Project:Нуултын бодлһн",
+ "badaccess": "Зөвшәлин эндү",
+ "badaccess-group0": "Та сурсн үүлдврән күцәҗ болшгот.",
"badaccess-groups": "Сансн үүлдвр һанц эн {{PLURAL:$2|багин|багмудын}} демнәчнр күцәҗ чадна: $1",
"versionrequired": "MediaWiki'н $1 һарц кергтә",
- "versionrequiredtext": "Тер халх олзхар, MediaWiki'н $1 һарц кергтә.\n[[Special:Version|Һарца халх]] хәләтн.",
- "ok": "Тиим",
- "retrievedfrom": "\"$1\" гидг халхас йовулсн",
- "youhavenewmessages": "Та $1та бәәнәт ($2).",
- "youhavenewmessagesmulti": "Таньд $1 деер шин зәңг ирсн бәәнә.",
- "editsection": "чиклх",
- "editold": "чиклх",
- "viewsourceold": "ишиг үзх",
- "editlink": "чиклх",
- "viewsourcelink": "ишиг хәләх",
- "editsectionhint": "«$1» гидг хүвиг чиклх",
+ "versionrequiredtext": "Эн халх керглхәр, MediaWiki'н $1 һарц кергтә.\n[[Special:Version|Һарцин халх]] хәләтн.",
+ "ok": "Не",
+ "retrievedfrom": "\"$1\" халхас авсн",
+ "youhavenewmessages": "Танд $1 бәәнә ($2).",
+ "newmessageslinkplural": "шин зәңг",
+ "youhavenewmessagesmulti": "Танд $1 деер шин зәңг бәәнә.",
+ "editsection": "ясх",
+ "editold": "ясх",
+ "viewsourceold": "иш код хәләх",
+ "editlink": "ясх",
+ "viewsourcelink": "иш код хәләх",
+ "editsectionhint": "«$1» салвр ясх",
"toc": "Һарг",
"showtoc": "үзүлх",
- "hidetoc": "бÑ\83лÑ\82Ñ\83лх",
- "thisisdeleted": "$1 гүүһәд хәләхү аль хәрүлхү?",
- "viewdeleted": "$1 үзүлхү?",
+ "hidetoc": "нÑ\83Ñ\83х",
+ "thisisdeleted": "$1 хәләхий аль босххий?",
+ "viewdeleted": "$1 хәләхий?",
"restorelink": "{{PLURAL:$1|нег әрлһсн ясвр|$1 әрлһсн ясвр}}",
- "feedlinks": "ТеÑ\80 бÓ\99Ó\99длÑ\82Ó\99",
- "feed-invalid": "Ð\91Ñ\83Ñ\80Ñ\83 биÑ\87гдлһнÓ\99 Ñ\82өлÓ\99 Ñ\81үвин Ñ\8fнз.",
- "feed-unavailable": "СиндикаÑ\86ин Ñ\81үв оÑ\80лһÑ\82а биÑ\88",
- "site-rss-feed": "$1 — RSS-зәңг",
- "site-atom-feed": "$1 — Atom-зәңг",
- "page-rss-feed": "«$1» — RSS-зәнгллһн",
- "page-atom-feed": "«$1» â\80\94 Atom зÓ\99нгллһн",
- "red-link-title": "$1 (халх бәәшго)",
+ "feedlinks": "Ð\9aÒ¯Ñ\81м:",
+ "feed-invalid": "Ð\91Ñ\83Ñ\80Ñ\83 закÑ\8aÑ\8fлһÑ\85 Ñ\86Ñ\83вгин Ñ\82Ó©Ñ\80л.",
+ "feed-unavailable": "Ð\9dегдÑ\81н күÑ\81м оÑ\80лһго",
+ "site-rss-feed": "$1 — RSS күсм",
+ "site-atom-feed": "$1 — Atom күсм",
+ "page-rss-feed": "«$1» — RSS күсм",
+ "page-atom-feed": "«$1» â\80\94 Atom күÑ\81м",
+ "red-link-title": "$1 (халх уга)",
"nstab-main": "Зүүл",
- "nstab-user": "Демнч",
- "nstab-media": "Ð\90һаÑ\80ин халх",
- "nstab-special": "Ð\9aөдлÑ\85нÓ\99 халх",
+ "nstab-user": "Демнәч",
+ "nstab-media": "Ð\91оомгин халх",
+ "nstab-special": "ШиÑ\88лң халх",
"nstab-project": "Төслин халх",
"nstab-image": "Боомг",
"nstab-mediawiki": "Зәңг",
"nstab-template": "Кевләр",
- "nstab-help": "ЦÓ\99Ó\99лһлһн",
+ "nstab-help": "Ð\94Ó©Ò£",
"nstab-category": "Әәшл",
- "nosuchaction": "Иим үүл бәәшго",
- "nosuchactiontext": "URL'д биÑ\87Ñ\81н үүл бÑ\83Ñ\80Ñ\83 болÒ\97ана.\nТа URL биÑ\87Ó\99д Ñ\8dндү кеÒ\97 болвза алÑ\8c бÑ\83Ñ\80Ñ\83 заалһаÑ\81 даÑ\85Ò\97.\nÐ\94Ó\99кÓ\99д, Ñ\82еÑ\80 йовдл {{SITENAME}} Ñ\82Ó©Ñ\81лин Ñ\8dндү болвза.",
- "nosuchspecialpage": "Иим көдлхнә халх бәәшго",
- "nospecialpagetext": "<strong>Та сурсн көдлхнә халх бәәшго.</strong>\n\nЧик көдлхнә халхин буулһавр: [[Special:SpecialPages|{{int:specialpages}}]].",
+ "nosuchaction": "Иим үүлдвр уга",
+ "nosuchactiontext": "URL'д зааÑ\81н үүлдвÑ\80 бÑ\83Ñ\80Ñ\83.\nТа URL оÑ\80Ñ\83лад Ñ\8dндүÑ\80Ñ\81н алÑ\8c бÑ\83Ñ\80Ñ\83 заалһаÑ\80 одÑ\81н маһд.\nÐ\94Ó\99кÓ\99д, Ñ\8dн {{SITENAME}} Ñ\82Ó©Ñ\81лин Ñ\8dндү маһд.",
+ "nosuchspecialpage": "Иим шишлң халх уга",
+ "nospecialpagetext": "<strong>Тана сурсн шишлң халх уга.</strong>\n\nБәәдг шишлң халхин бүрткл: [[Special:SpecialPages|{{int:specialpages}}]].",
"error": "Эндү",
- "databaseerror": "Өггцнә базин эндү",
- "missing-article": "Өггцнә халһлд сурсн халхин бичг олв уга. Эн халх олх йоста: \"$1\" нертә $2.\n\nТер йовдл һарһсн халхна тууҗин өңгрсн заалһиг дахлһна арһ болад бәәнә.\n\nЭс гиҗ, тиим болх зөвтә, та заклһна теткүлин эндүһиг олв.\nБуйн болтха, URL заалһ бичәд, тер йовдлин туск [[Special:ListUsers/sysop|закрачд]] келтн.",
- "missingarticle-rev": "($1 тойгта халхна янз)",
- "missingarticle-diff": "(йилһән: $1, $2)",
- "internalerror": "Ð\94оÑ\82Ñ\80нÑ\8c эндү",
- "internalerror_info": "Ð\94оÑ\82Ñ\80нÑ\8c эндү: $1",
- "filerenameerror": "Боомгин нериг «$1»-с «$2» болһн сольҗ чаддго",
- "filedeleteerror": "«$1» боомг һарһҗ чаддго.",
- "unexpected": "Таалһта уга кемҗә: «$1» = «$2».",
- "badtitle": "Буру нернь",
- "badtitletext": "Сурсн нерн буру, хоосн, аль му бичсн келн хоорнд нертә. Тиим чигн биз, нерн зөв уга үзгтә.",
- "viewsource": "Ишиг хәләх",
+ "databaseerror": "То-дигин саңгин эндү",
+ "missing-article": "То-дигин саңт сурсн «$1» халхин $2 олх йоста бичг олсн уга.\n\nТиим заңта йовдл әрлһсн халхна тууҗин хуучрсн заалһар одх седвәрәр һардг авъяста.\n\nКемр эн учр биш, та көтлврин теткүлин эндү олсн бәәдлтәт.\nБуйн болтха, эн URL зааҗ, эн йовдлын туск [[Special:ListUsers/sysop|закрачт]] келтн.",
+ "missingarticle-rev": "(№ $1 һарц)",
+ "missingarticle-diff": "(Ð\99илһән: $1, $2)",
+ "internalerror": "Ð\94оÑ\82Ñ\80к эндү",
+ "internalerror_info": "Ð\94оÑ\82Ñ\80к эндү: $1",
+ "filerenameerror": "Боомгин нер «$1» талас «$2» болһҗ болмҗго",
+ "filedeleteerror": "«$1» боомг әрлһҗ болмҗго.",
+ "unexpected": "Таарго кемҗән: «$1» = «$2».",
+ "badtitle": "Болшго нернь",
+ "badtitletext": "Сурсн халхин нерн буру, хоосн, аль буру бичсн келн хоорндк аль вики хоорндк нерн. Нернд болшго темдгүд керглсн маһд.",
+ "viewsource": "Иш код хәләх",
"actionthrottled": "Хурдна заг",
- "ns-specialprotected": "ШиÑ\88лң Ñ\85алÑ\85 Ñ\87иклÑ\81н бÓ\99Ó\99Ñ\85 болшго.",
- "virus-unknownscanner": "медгдго антивирус:",
+ "ns-specialprotected": "ШиÑ\88лң Ñ\85алÑ\85 Ñ\8fÑ\81Ñ\87 болшго.",
+ "virus-unknownscanner": "медснго антивирус:",
"logouttext": "'''Та һарад бәәнәт.'''\n\nТа {{SITENAME}} гидг ормиг нертә уга олзлҗ чаднат, аль та <span class='plainlinks'>[$1 дәкәд орҗ]</span> цацу аль талдан нертә чаднат.\nЗәрм халхс цааранднь та ода чигн орсн мет үзүлҗ чаддг тускар темдглтн (та хәләчин санлиг цеврлтл).",
- "yourname": "Демнчна нернь:",
+ "yourname": "Демнәчнә нерн:",
"yourpassword": "Нууц үг:",
- "yourpasswordagain": "Нууц үгиг давтн:",
+ "yourpasswordagain": "Нууц үг давтн:",
"remembermypassword": "Намаг эн тоолдврд тодлх ($1 {{PLURAL:$1|1=өдрт|өдрмүдт}} икәр биш)",
"yourdomainname": "Тана домен:",
"login": "Орлһн",
- "nav-login-createaccount": "ХаÑ\80Ò»Ñ\85 / биÑ\87гдлһн кех",
- "userlogin": "Орх аль бичгдлһиг бүтәх",
- "userloginnocreate": "ХаÑ\80Ò»х",
+ "nav-login-createaccount": "Ð\9eÑ\80Ñ\85/бүÑ\80Ñ\82кгдх",
+ "userlogin": "Орх/бүрткгдх",
+ "userloginnocreate": "Ð\9eÑ\80х",
"logout": "Һарх",
"userlogout": "Һарх",
- "notloggedin": "Та орв биш",
- "nologin": "Бичгдлһта уга? '''$1'''.",
- "nologinlink": "Ð\91иÑ\87гдлһиг бүтәх",
- "createaccount": "Ð\91иÑ\87гдлһиг бүтәх",
- "gotaccount": "Бичгдлһтә? '''$1'''.",
- "gotaccountlink": "ХаÑ\80Ò»тн",
+ "notloggedin": "Та орсн уга",
+ "nologin": "Бичгдлһгот? '''$1'''.",
+ "nologinlink": "Ð\91иÑ\87гдлһн бүтәх",
+ "createaccount": "Ð\91иÑ\87гдлһн бүтәх",
+ "gotaccount": "Бичгдлһтәт? '''$1'''.",
+ "gotaccountlink": "Ð\9eÑ\80тн",
"createaccountmail": "электрона улаһар",
"userexists": "Эн нер олзлдг юмн.\nБуйн болтха, талдан нернь автн.",
"loginerror": "Орлһна эндү",
- "createaccounterror": "Ð\91иÑ\87гдлһиг бүÑ\82Ó\99Ñ\85 болÑ\88го: $1",
- "noname": "Та зөвÑ\82Ó\99 демнÑ\87на неÑ\80нÑ\8c биÑ\87в уга.",
+ "createaccounterror": "Ð\91иÑ\87гдлһн бүÑ\82Ó\99Ò\97 болмÒ\97го: $1",
+ "noname": "Та зөвÑ\88Ó\99Ñ\81н демнÓ\99Ñ\87нÓ\99 неÑ\80 зааÑ\81н уга.",
"loginsuccesstitle": "Йовудта орлһн",
- "loginsuccess": "''' Тадн ода «$1» нертә {{SITENAME}} гидг нерәдлһтә төсвд бәәнәт.'''",
- "nosuchuser": "«$1» гидг неÑ\80Ó\99длһÑ\82Ó\99 демнÑ\87 бÓ\99Ó\99Ñ\88го.\nÐ\94емнÑ\87на неÑ\80Ñ\82 баһ болн ик үзгүд Ó\99дл биÑ\88 болна.\n«<nowiki>$1</nowiki>» гидг неÑ\80Ó\99длһÑ\82Ó\99 демнÑ\87 бÓ\99Ó\99Ñ\88го.\nÐ\91иÑ\87лһиг Ñ\88Ò¯Ò¯Ñ\82н алÑ\8c [[Special:UserLogin/signup|бигÑ\87длһиг бүтәтн]].",
- "nosuchusershort": "«$1» гидг неÑ\80Ó\99длһÑ\82Ó\99 демнÑ\87 бÓ\99Ó\99Ñ\88го.\nÐ\91иÑ\87лһиг шүүтн.",
- "nouserspecified": "Та демнчна нернь бичх йостав.",
- "login-userblocked": "ТеÑ\80 демнÑ\87 бүÑ\81лÑ\81н, Ñ\85аÑ\80һад оÑ\80Ò\97 болÑ\88го бÓ\99Ó\99нÓ\99.",
- "wrongpassword": "Та буру нууц үг бичв.\nДәкәд арһ хәәтн.",
- "wrongpasswordempty": "Та хоосн нууц үгиг бичв.\nДәкәд арһ хәәтн.",
+ "loginsuccess": "<strong>Та ода «$1» нертә {{SITENAME}} төсвд бәәнәт.</strong>",
+ "nosuchuser": "«$1» неÑ\80Ñ\82Ó\99 демнÓ\99Ñ\87 Ñ\83га.\nÐ\94емнÓ\99Ñ\87нÓ\99 неÑ\80нд баһ болн Ñ\82ом үзгүд Ó\99дл биÑ\88.\nЧик биÑ\87Ñ\81н Ñ\88Ò¯Ò¯Ñ\82н алÑ\8c [[Special:UserLogin/signup|бигÑ\87длһн бүтәтн]].",
+ "nosuchusershort": "«$1» неÑ\80Ñ\82Ó\99 демнÓ\99Ñ\87 Ñ\83га.\nЧик биÑ\87Ñ\81н шүүтн.",
+ "nouserspecified": "Та демнәчнә нер заах йоста.",
+ "login-userblocked": "Ðн демнÓ\99Ñ\87 бүÑ\81лÑ\81н, оÑ\80Ò\97 болÑ\88го.",
+ "wrongpassword": "Та буру нууц үг бичвт.\nДәкәд сөртн.",
+ "wrongpasswordempty": "Та хоосн нууц үг бичвт.\nДәкәд сөртн.",
"passwordtooshort": "Нууц үг баһар биш $1 {{PLURAL:$1|үзгтә|үзгүдта|үзгүдта}} бәәх йоста.",
- "password-name-match": "Нууц үг денмнчна нертә әдл биш бәәх йоста.",
+ "password-name-match": "Нууц үг денмнәчнә нерәс бус бәәх йоста.",
"mailmypassword": "Шин нууц үгиг E-mail бичгәр йовулҗ",
"emailauthenticated": "Тана e-mail хайг $2 өдрт, $3 цагт батлсн.",
- "accountcreated": "Бичгдллһн бүтәв.",
+ "accountcreated": "Бичгдлһн бүтәв.",
"loginlanguagelabel": "Келн: $1",
- "changepassword": "Нууц үгиг сольҗ",
- "resetpass_header": "Бичгдллһнә нууц үгиг сольх",
+ "changepassword": "Нууц үг сольлһн",
+ "resetpass_header": "Бичгдлһнә нууц үг сольх",
"oldpassword": "Көгшн нууц үг:",
"newpassword": "Шин нууц үг:",
- "retypenew": "Шин нууц үгиг дәкәд бичтн:",
+ "retypenew": "Шин нууц үг дәкәд бичтн:",
"changepassword-success": "Тана нууц үгиг йовудта сольв! Та ода орнат...",
- "resetpass-submit-loggedin": "Нууц үгиг сольх",
- "resetpass-submit-cancel": "Уга кех",
+ "resetpass-submit-loggedin": "Нууц үг сольх",
+ "resetpass-submit-cancel": "Ð\91Ñ\83Ñ\86х",
"bold_sample": "Тарһн бичг",
"bold_tip": "Тарһн бичг",
"italic_sample": "Өкәсн бичг",
"italic_tip": "Өкәсн бичг",
- "link_sample": "Ð\97аалһна нерн",
- "link_tip": "Өвр заалһ",
- "extlink_sample": "http://www.example.com заалһна неÑ\80нÑ\8c",
- "extlink_tip": "Һаза заалһ (http:// гидг эклц бичә мартн)",
- "headline_sample": "Толһа нерн",
- "headline_tip": "Дү толһа нерн",
- "nowiki_sample": "Энд темдглһтә уга бичгиг бичтн",
- "nowiki_tip": "Ð\91ики Ñ\82емдглһиг басх",
- "image_tip": "Ð\9eÑ\80Ñ\86Ñ\83лсн боомг",
- "media_tip": "Боомгин заалһ",
- "sig_tip": "Тана тәвсн һар цагин темдгтә",
- "hr_tip": "Кевтдг татасн (дундин бәәдлтә олзлтн)",
- "summary": "Учр-утх:",
- "subject": "Төр/нерәдлһн:",
- "minoredit": "Ð\91аһ Ñ\87икллһн",
- "watchthis": "ШинÒ\97лх",
- "savearticle": "Хадһлх",
+ "link_sample": "Ð\97аалһин нерн",
+ "link_tip": "Дотрк заалһ",
+ "extlink_sample": "http://www.example.com заалһин һаÑ\80Ñ\86г",
+ "extlink_tip": "Һазак заалһ (http:// эклвр бичә мартн)",
+ "headline_sample": "Һарцгин бичәсн",
+ "headline_tip": "Дү һарцг",
+ "nowiki_sample": "Энд кевлүлх кергго бичәсн орултн",
+ "nowiki_tip": "Ð\92ики кевлүллÑ\82 басх",
+ "image_tip": "Углсн боомг",
+ "media_tip": "Боомгур заалһ",
+ "sig_tip": "Тана тәвсн һар цаг хойр",
+ "hr_tip": "Кевтә татасн (нигтәр биш керглтн)",
+ "summary": "Үндсн:",
+ "subject": "Төр/һарцг:",
+ "minoredit": "Ð\91аһ Ñ\8fÑ\81вÑ\80",
+ "watchthis": "Ðн Ñ\85алÑ\85 овÑ\80х",
+ "savearticle": "ХалÑ\85 Ñ\85адһлÑ\85",
"preview": "Хәләвр",
"showpreview": "Хәләвр",
- "showdiff": "Ð\99илһән",
+ "showdiff": "Ð\9aеÑ\81н йилһән",
"anoneditwarning": "'''Урдаснь зәңг:''' та орв биш.\nТадна IP хайг эн халхна чикллһнә сеткүлд бичҗ авх.",
- "summary-preview": "Эн учр-утхта болх:",
- "subject-preview": "ТеÑ\80 һаÑ\80Ñ\87иг болх:",
- "blockedtitle": "Демнч бүслгдәд бәәнә.",
- "loginreqlink": "харһх",
- "accmailtitle": "Ð\9dÑ\83Ñ\83Ñ\86 үгÑ\82Ó\99 биÑ\87г йовÑ\83лла.",
+ "summary-preview": "Эн үндсн болх:",
+ "subject-preview": "Ðн һаÑ\80Ñ\86г болх:",
+ "blockedtitle": "Демнәч бүслгдсн",
+ "loginreqlink": "орх",
+ "accmailtitle": "Ð\9dÑ\83Ñ\83Ñ\86 үгÑ\82Ó\99 биÑ\87г йовÑ\83лгдв",
"newarticle": "(Шин)",
- "newarticletext": "Та заалһиг даÑ\85ад бÓ\99Ó\99дг Ñ\83га Ñ\85алÑ\85д иÑ\80в.\nТеÑ\80үг бүÑ\82Ó\99Ò\97 болÑ\85ла, доÑ\80аһаÑ\80 Ñ\82еÑ\80зд биÑ\87Ñ\82н (дÓ\99кÓ\99д өггÑ\86нÓ\99 Ñ\82өлÓ\99 [$1 Ñ\82Ó\99Ó\99лвÑ\80] Ñ\85Ó\99лÓ\99Ñ\82н).\nТа Ñ\8dн һазÑ\80Ñ\82 Ñ\8dндүһÓ\99Ñ\80 бÓ\99Ó\99Ñ\85лÓ\99, '''Ð¥Ó\99Ñ\80Ò¯''' даÑ\80Ñ\86иг дартн.",
- "noarticletext": "Эн халх хоосн. Та [[Special:Search/{{PAGENAME}}|эн нернә сананд орулһна хәәх]] , <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} бүртклин бичгт хәәх], аль '''[{{fullurl:{{FULLPAGENAME}}|action=edit}} бүтәх]'''</span>.",
+ "newarticletext": "Та заалһаÑ\80 одаÑ\85н Ñ\83га Ñ\85алÑ\85Ñ\82 одв.\nҮүниг бүÑ\82Ó\99Ñ\85Ó\99Ñ\80, доÑ\80к Ñ\86оңÑ\85Ñ\82 биÑ\87Ó\99Ñ\81н оÑ\80Ñ\83лÑ\82н (Ñ\82одÑ\80Ñ\85а [$1 дөңгин Ñ\85алÑ\85] Ñ\85Ó\99лÓ\99Ñ\82н).\nТа Ñ\8dнд Ñ\8dндүһÓ\99Ñ\80 одлÑ\85лаÑ\82, Ñ\85аÑ\80агÑ\87ин '''Ð¥Ó\99Ñ\80Ò¯''' Ñ\82овÑ\80Ñ\83н дартн.",
+ "noarticletext": "Эн халх одахн хоосн. Та [[Special:Search/{{PAGENAME}}|эн нернә дурдлһн хәәҗ]] , <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} ирлцәтә седкүлин бичгдл хәәҗ], аль '''[{{fullurl:{{FULLPAGENAME}}|action=edit}} иим нертә халх бүтәҗ]''' чаднат</span>.",
"clearyourcache": "'''Оньган өгтн:''' Кесн сольлһн үзхәр, тана хәләлгчин кеш цеврүлтн: '''Mozilla / Firefox''': ''Ctrl+Shift+R'', '''IE:''' ''Ctrl+F5'', '''Safari''': ''Cmd+Shift+R'', '''Konqueror''': ''F5'', '''Opera''': ''Tools→Preferences'' менүһәс.",
"usercssyoucanpreview": "'''Селвг:''' тана шин CSS боомг шүүҗ хадһлар, «{{int:showpreview}}» товч олзлтн.",
"userjsyoucanpreview": "'''Селвг:''' тана шин JS боомг шүүҗ хадһлар, «{{int:showpreview}}» товч олзлтн.",
"previewnote": "'''Эн мел хәләвр бәәдг тускар тодлтн.'''\nТана сольлһн ода чигн хадһлсн уга!",
"previewconflict": "Тер хәләвр деегүрк чикллһнә теегт бәәдг бичг хадлһҗ бичсн мет үзүлнә.",
"session_fail_preview": "'''Гемим тәвтн, сервер тана сольлһта даңдад болв. Юнгад гихлә, тана харһлһна медүллһн геев.\nБуйн болтха, дәкәд арһ хәәтн.\nТер эндү давтхла, [[Special:UserLogout|һартн]] тегәд бас харһтн.'''",
- "editing": "Чикллһн: $1",
- "editingsection": "«$1» гидг халхна чикллһн (хүв)",
- "editconflict": "ЧикллһнÓ\99 керүл: $1",
- "yourtext": "Тана бичсн",
+ "editing": "ЯÑ\81вÑ\80: $1",
+ "editingsection": "«$1» ясвр (салвр)",
+ "editconflict": "ЯÑ\81вÑ\80ин керүл: $1",
+ "yourtext": "Тана бичәсн",
"yourdiff": "Йилһән",
"copyrightwarning": "Буйн болтха, цуг өгүллһн {{SITENAME}} төлә $2 гидг закаһар кесн, тоолсн бәәдг тускар тодлтн (Дәкәд өггцд төлә $1 хәләтн). Та тана бичсн чилклсн аль делгрңсн бәәҗ седхлә биш, эн ормд бичә бичтн.<br /> Дәкәд та маднд эн эврәнь бичсн, күмн әмтнә хазас аль цацу сул медснәс бәәдг үгән өгнәт. '''Зөвән авхла уга, харссн бичсн күүнә көдлмш бичә тәвтн!'''",
"copyrightwarning2": "Буйн болтха, цуг өгүллһн {{SITENAME}} төлә чиклсн аль һарһсн бәәдг чадта тускар тодлтн. Та тана бичсн чилклсн аль делгрңсн бәәҗ седхлә биш, эн ормд бичә бичтн.<br /> Дәкәд та маднд эн эврәнь бичсн, күмн әмтнә хазас аль цацу сул медснәс бәәдг үгән өгнәт ($1 хәләтн). '''Зөвән авхла уга, харссн бичсн күүнә көдлмш бичә тәвтн!'''",
"template-semiprotected": "(зәрм харссн)",
"hiddencategories": "Эн халх тер $1 {{PLURAL:$1|бултулсн әәшләс|бултулсн әәшлүдәс|бултулсн әәшлүдәс}}:",
"permissionserrorstext-withaction": "Та $2 кеҗ болшго. Юнгад гихлә, эн {{PLURAL:$1|1=учрар|учрар}}:",
- "edit-conflict": "ЧикллһнÓ\99 керүл.",
+ "edit-conflict": "ЯÑ\81вÑ\80ин керүл.",
"parser-template-loop-warning": "Зуран бүтү нүдлв: [[$1]]",
- "viewpagelogs": "ТеÑ\80 Ñ\85алÑ\85на Ñ\81еÑ\82күлдүд үзүлх",
- "currentrev-asof": "Ода болсн янз ($1)",
- "revisionasof": "Тер цагин янз: $1",
- "previousrevision": "← Урдк янз",
- "nextrevision": "Дарук янз →",
- "currentrevisionlink": "Ð\9eда болÑ\81н Ñ\8fнз",
+ "viewpagelogs": "Ðн Ñ\85алÑ\85ин Ñ\81едкүлүд Ñ\85Ó\99лÓ\99х",
+ "currentrev-asof": "$1 һарц",
+ "revisionasof": "$1 һарц",
+ "previousrevision": "← Урдк һарц",
+ "nextrevision": "Дарук һарц →",
+ "currentrevisionlink": "Ð\91Ó\99Ó\99гÑ\87 һаÑ\80Ñ\86",
"cur": "ода",
"next": "дарук",
"last": "урдк",
"page_first": "түрүн",
- "page_last": "кенз",
- "histlegend": "Тәәлвр: (ода) — одачн янзас йилһән; (урдк) — урдк янзас йилһән; '''б''' — баһ сольлһн",
- "history-fieldset-title": "Тууҗиг хәләх",
+ "page_last": "оÑ\82Ñ\85н",
+ "histlegend": "Һарцин суңһвр: дүңнх сансн халхин һарцс суңһад, Enter аль дорк товрун дартн.<br />\nТәәлвр: <strong>({{int:cur}})</strong> — отхн һарцас йилһән, <strong>({{int:last}})</strong> — урдк һарцас йилһән, <strong>{{int:minoreditletter}}</strong> — баһ ясвр.",
+ "history-fieldset-title": "Тууҗ хәләх",
"histfirst": "маш хуучн",
- "histlast": "маш отхн",
+ "histlast": "маш шин",
"historyempty": "(хоосн)",
- "rev-delundel": "үзүлÑ\85/бÑ\83лÑ\82Ñ\83лх",
+ "rev-delundel": "үзүлÑ\85/нÑ\83Ñ\83х",
"rev-showdeleted": "үзүлх",
"revdelete-show-file-submit": "Тиим",
"revdelete-radio-set": "Нуугдсн",
"revdelete-radio-unset": "Үзгдмл",
- "revdel-restore": "Үзгдллһиг сольх",
- "pagehist": "ХалÑ\85на тууҗ",
- "revdelete-otherreason": "Талдан/дÓ\99кÓ\99д учр:",
+ "revdel-restore": "үзгдмл сольх",
+ "pagehist": "ХалÑ\85ин тууҗ",
+ "revdelete-otherreason": "Ð\91Ñ\83Ñ\81/немгÑ\87 учр:",
"mergehistory-reason": "Учр:",
"revertmerge": "Хувах",
- "history-title": "«$1» — ясврин тууҗ",
+ "history-title": "«$1» ясврин тууҗ",
"lineno": "$1 мөр:",
"compareselectedversions": "Суңһсн янзс әдлцүлх",
- "editundo": "уга кех",
- "searchresults": "Ð¥Ó\99Ó\99лһнÓ\99 аÑ\88Ñ\83д",
- "searchresults-title": "Хәәлһнә ашуд \"$1\" төлә",
- "notextmatches": "Әдл бичг халхд уга",
+ "editundo": "буцх",
+ "searchresults": "Ð¥Ó\99Ó\99вÑ\80ин аÑ\88",
+ "searchresults-title": "«$1» хәәврин аш",
+ "notextmatches": "Халхсин бичәснд ирлцән уга",
"prevn": "урдк {{PLURAL:$1|$1}}",
"nextn": "дарук {{PLURAL:$1|$1}}",
- "viewprevnext": "Ð\93Ò¯Ò¯Ò»Ó\99д Ñ\85әләх ($1 {{int:pipe-separator}} $2) ($3)",
+ "viewprevnext": "Ð¥әләх ($1 {{int:pipe-separator}} $2) ($3)",
"searchprofile-articles": "Зүүлс",
"searchprofile-images": "Үзгдл-соңсвр",
- "searchprofile-everything": "ЦÑ\83һаÑ\80",
- "searchprofile-articles-tooltip": "$1 гидг зүүлд хәәх",
+ "searchprofile-everything": "Хамг",
+ "searchprofile-articles-tooltip": "$1 доÑ\82Ñ\80 хәәх",
"searchprofile-images-tooltip": "Боомг хәәх",
"search-result-size": "$1 ({{PLURAL:$2|$2 үг|$2 үг|$2 үг}})",
- "search-redirect": "(авч одлһн $1)",
- "search-section": "($1 хүв)",
- "search-suggest": "Та эниг таанат: $1 ?",
- "search-interwiki-caption": "Садта проектмуд",
+ "search-redirect": "(туудг $1)",
+ "search-section": "(«$1» салвр)",
+ "search-suggest": "Тана таасн маһд: $1",
+ "search-interwiki-caption": "Садта төслүд",
"search-interwiki-default": "$1 талас аш:",
- "search-interwiki-more": "(дÓ\99кÓ\99д)",
- "searchall": "Ñ\86Ñ\83г",
- "powersearch-legend": "Ð\9aÒ¯Ñ\87н Ñ\85Ó\99Ó\99лһн",
- "powersearch-ns": "Ðн неÑ\80нÓ\99 Ñ\83 доÑ\82Ñ\80ан хәәх:",
+ "search-interwiki-more": "(нанÑ\8c)",
+ "searchall": "Ñ\85амг",
+ "powersearch-legend": "Ð\9dÓ\99Ñ\80н Ñ\85Ó\99Ó\99вÑ\80",
+ "powersearch-ns": "Ðн неÑ\80нÓ\99 Ñ\82Ó©Ñ\80лд хәәх:",
"powersearch-togglenone": "Уга",
- "preferences": "Ð\94Ñ\83Ñ\80ллһн",
+ "preferences": "Ð\9aөгмүд",
"mypreferences": "Көгмүд",
- "prefs-edits": "ЧикллһнÓ\99 то:",
- "prefs-skin": "Ð¥Ñ\83вÑ\86нÑ\8c",
+ "prefs-edits": "ЯÑ\81вÑ\80ин то:",
+ "prefs-skin": "Ð\91Ó\99Ó\99дл",
"skin-preview": "Хәләвр",
- "datedefault": "Келхлә уга",
- "prefs-personal": "Демнчна көгүд",
- "prefs-rc": "ШидÑ\80Ó\99 Ñ\81олÑ\8cлһн",
- "prefs-watchlist": "ШинÒ\97ллһнÓ\99 Ñ\81еÑ\82күл",
+ "datedefault": "Көг уга",
+ "prefs-personal": "Демнәчна то-диг",
+ "prefs-rc": "Ð\9eÑ\82Ñ\85н Ñ\8fÑ\81вÑ\80",
+ "prefs-watchlist": "Ð\9eвÑ\80Ñ\85ин Ñ\81едкүл",
"prefs-watchlist-days": "Шинҗллһнә седкүлд үзүлсн ик гисн өдрин то:",
"prefs-watchlist-days-max": "$1 {{PLURAL:$1|өдрәс|өдрәс}} удан биш",
- "prefs-misc": "Талдан",
- "prefs-resetpass": "Нууц угиг сольҗ",
- "prefs-email": "E-mail'ын көгүд",
+ "prefs-misc": "Ð\91Ñ\83Ñ\81",
+ "prefs-resetpass": "Нууц үг сольх",
+ "prefs-email": "E-mail'ин көгмүд",
"prefs-rendering": "Һазад бәәдл",
"saveprefs": "Хадһлх",
"restoreprefs": "Хамг таарсн көг босхх (цуг салвр)",
- "prefs-editing": "Чикллһн",
+ "prefs-editing": "ЯÑ\81вÑ\80",
"rows": "Мөрд:",
"columns": "Бахд:",
"savedprefs": "Тана көгүдиг хадһлв.",
- "timezonelegend": "ЧаÑ\81ин бүс:",
- "localtime": "Бәәрн һазра цаг:",
+ "timezonelegend": "Цагин бүс:",
+ "localtime": "Бәәрн цаг:",
"timezoneuseserverdefault": "Серверин көг керглх",
- "timezoneuseoffset": "Талдан (көндллһн заатн)",
+ "timezoneuseoffset": "Ð\91Ñ\83Ñ\81 (көндллһн заатн)",
"servertime": "Серверин цаг:",
- "guesstimezone": "Хәләлгчәс авх",
- "timezoneregion-africa": "Априк",
+ "guesstimezone": "Харагчасавх",
+ "timezoneregion-africa": "Африк",
"timezoneregion-america": "Америк",
"timezoneregion-antarctica": "Антарктик",
"timezoneregion-arctic": "Арктик",
"timezoneregion-atlantic": "Атлантин дала",
"timezoneregion-australia": "Австрал",
"timezoneregion-europe": "Европ",
- "timezoneregion-indian": "ÐнеÑ\82екгин дала",
+ "timezoneregion-indian": "Ðндкг дала",
"timezoneregion-pacific": "Номһн дала",
"allowemail": "Талдан демнчнрәс ирсн e-mail бичг зөвшәрх",
"prefs-searchoptions": "Хәәвр",
"prefs-namespaces": "Нернә ус",
"prefs-custom-css": "Онц CSS",
"prefs-custom-js": "Онц JS",
- "prefs-emailconfirm-label": "E-mail баÑ\82лһн:",
+ "prefs-emailconfirm-label": "E-mail лавллÑ\82:",
"youremail": "E-mail хайг:",
"username": "Демнәчнә нерн:",
"prefs-memberingroups": "{{PLURAL:$1|Багин|Багмудын}} гешүн:",
"prefs-help-email": "E-mail хайг та эврә дурар бичнәт. Бичхлә, тадн шин түлкүр үгиг бичгәр йовулсн өгҗ чаднат (мартхла).",
"prefs-info": "Һол медә",
"prefs-i18n": "Олн орни бәәлһн",
- "prefs-signature": "Тәвсн һаран",
+ "prefs-signature": "Тәвсн һар",
"prefs-advancedediting": "Йирңкә көг",
- "prefs-advancedrc": "Ð\94Ó\99кÓ\99д көгүд",
- "prefs-advancedrendering": "Ð\94Ó\99кÓ\99д көгүд",
- "prefs-advancedsearchoptions": "Ð\94Ó\99кÓ\99д көгүд",
- "prefs-advancedwatchlist": "Ð\94Ó\99кÓ\99д көгүд",
+ "prefs-advancedrc": "Ð\9dÓ\99Ñ\80н көг",
+ "prefs-advancedrendering": "Ð\9dÓ\99Ñ\80н көг",
+ "prefs-advancedsearchoptions": "Ð\9dÓ\99Ñ\80н көг",
+ "prefs-advancedwatchlist": "Ð\9dÓ\99Ñ\80н көг",
"prefs-diffs": "Йилһәс",
"userrights-reason": "Учр:",
"group": "Баг:",
- "group-user": "Демнчнр",
+ "group-user": "Демнәчнр",
"group-autoconfirmed": "Эврә батлсн демнчнр",
"group-bot": "Көдлврүд",
"group-sysop": "Закрачуд",
"group-sysop-member": "закрач",
"group-bureaucrat-member": "нойнч",
"grouppage-user": "{{ns:project}}:Демнч",
- "grouppage-autoconfirmed": "{{ns:project}}:ÐвÑ\80Ó\99 баÑ\82лсн демнчнр",
- "grouppage-bot": "{{ns:project}}:Ð\9aөдлвÑ\80үд",
+ "grouppage-autoconfirmed": "{{ns:project}}:ÐвÑ\80Ó\99 лавлсн демнчнр",
+ "grouppage-bot": "{{ns:project}}:Ð\9aөдлгÑ\87үд",
"grouppage-sysop": "{{ns:project}}:Закрачуд",
"grouppage-bureaucrat": "{{ns:project}}:Нойнчуд",
- "newuserlogpage": "Бичгдлһнә сеткүл",
- "rightslog": "Демнчна зөвәнә сеткүл",
- "action-edit": "эн халхиг чиклх",
+ "newuserlogpage": "Бичгдлһнә седкүл",
+ "rightslog": "Демнәчна зөвин седкүл",
+ "action-edit": "эн халх ясх",
"nchanges": "$1 ясвр",
- "recentchanges": "ШидÑ\80Ó\99 Ñ\81олÑ\8cлһн",
- "recentchanges-legend": "ШидÑ\80Ó\99 Ñ\81олÑ\8cлһна көгүд",
- "recentchanges-summary": "Эн цагин дараһар бичсн шидрә сольлһн",
- "recentchanges-feed-description": "Ðн зÓ\99ңгллһд Ñ\88идÑ\80Ó\99 Ñ\85үвÑ\80һд Ñ\88инÒ\97лх.",
- "recentchanges-label-newpage": "ТеÑ\80 үүлÓ\99р шин халх бүтәв",
- "recentchanges-label-minor": "Ðн баһ Ñ\87инÑ\80Ñ\82Ó\99 Ñ\81олÑ\8cлһн",
- "recentchanges-label-bot": "Ðн Ñ\81олÑ\8cлһн көдлвÑ\80 (Ñ\80обоÑ\82) кеÑ\85в",
+ "recentchanges": "Ð\9eÑ\82Ñ\85н Ñ\8fÑ\81вÑ\80",
+ "recentchanges-legend": "Ð\9eÑ\82Ñ\85н Ñ\8fÑ\81вÑ\80ин көг",
+ "recentchanges-summary": "Энд цагин дараһар бичсн отхн ясвр.",
+ "recentchanges-feed-description": "Ðн күÑ\81мд викин оÑ\82Ñ\85н Ñ\8fÑ\81вÑ\80 овÑ\80х.",
+ "recentchanges-label-newpage": "Ðн Ñ\8fÑ\81вÑ\80ар шин халх бүтәв",
+ "recentchanges-label-minor": "Ðн баһ Ñ\87инÑ\80Ñ\82Ó\99 Ñ\8fÑ\81вÑ\80",
+ "recentchanges-label-bot": "Ðн Ñ\8fÑ\81вÑ\80 көдлгÑ\87 (боÑ\82) кев",
"recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} ([[Special:NewPages|list of new pages]] чигн хәләтн)",
- "rclistfrom": "ТеÑ\80 Ñ\86агаÑ\81 авн Ñ\81олÑ\8cлһн үзүлх: $3 $2.",
- "rcshowhideminor": "баһ Ñ\87икллһиг $1",
- "rcshowhidebots": "көдлвÑ\80үдиг $1",
+ "rclistfrom": "Ðн Ñ\86агаÑ\81 авн Ñ\8fÑ\81вÑ\80 үзүлх: $3 $2.",
+ "rcshowhideminor": "баһ Ñ\8fÑ\81вÑ\80 $1",
+ "rcshowhidebots": "көдлгÑ\87үд $1",
"rcshowhideliu": "$1 бүрткгдсн демнәч",
- "rcshowhideanons": "нер уга демнчнриг $1",
- "rcshowhidemine": "мини Ñ\87икллһиг $1",
- "rclinks": "Ð\9aенз $1 Ñ\81олÑ\8cлһн, кенз $2 өдрмүдт үзүлх<br />$3",
+ "rcshowhideanons": "нерго демнәчнр $1",
+ "rcshowhidemine": "мини Ñ\8fÑ\81вÑ\80 $1",
+ "rclinks": "Ð\9eÑ\82Ñ\85н $1 Ñ\8fÑ\81вÑ\80, Ñ\81үл $2 өдрмүдт үзүлх<br />$3",
"diff": "йилһ",
"hist": "тууҗ",
- "hide": "бÑ\83лÑ\82Ñ\83лх",
- "show": "Ò¯зүлх",
+ "hide": "Ð\9dÑ\83Ñ\83х",
+ "show": "Ò®зүлх",
"minoreditletter": "б",
"newpageletter": "Ш",
"boteditletter": "к",
- "newsectionsummary": "/* $1 */ Шин Ñ\85үв",
+ "newsectionsummary": "/* $1 */ Шин Ñ\81алвÑ\80",
"rc-enhanced-expand": "Нәрн учр үзүлх",
- "rc-enhanced-hide": "Тодрхасиг бултулх",
- "recentchangeslinked": "Садн Ñ\87икллһн",
- "recentchangeslinked-feed": "Садта чикллһн",
- "recentchangeslinked-toolbox": "Садта чикллһн",
- "recentchangeslinked-title": "$1 садта сольлһн",
+ "rc-enhanced-hide": "Тодрха нуух",
+ "recentchangeslinked": "Садн Ñ\8fÑ\81вÑ\80",
+ "recentchangeslinked-feed": "Садн ясвр",
+ "recentchangeslinked-toolbox": "Садн ясвр",
+ "recentchangeslinked-title": "$1 садн ясвр",
"recentchangeslinked-summary": "Эн тер халх заалдг халхсин (аль тер янзин халхсин) шидрә сольлһн.\nТана [[Special:Watchlist|шинҗллһнә сеткүлин]] халхс '''тарһн''' бичәтә.",
- "recentchangeslinked-page": "ХалÑ\85на неÑ\80нÑ\8c:",
- "recentchangeslinked-to": "Зөрүһәр, эн халхд заалдг халхсин хүврлһиг үзүлх",
- "upload": "Боомгиг тәвх",
- "uploadbtn": "Боомгиг тәвх",
+ "recentchangeslinked-page": "ХалÑ\85ин неÑ\80н:",
+ "recentchangeslinked-to": "Зөрүһәр, эн халхт заалдг халхсин ясвр үзүлх",
+ "upload": "Боомг тәвх",
+ "uploadbtn": "Боомг тәвх",
"uploadnologintext": "Боомг орулхар $1 кергтә.",
"uploaderror": "Тәвллһнә эндү",
"uploadlogpage": "Тәвллһнә сеткүл",
- "filename": "Ð\91оомгна нернь",
+ "filename": "Ð\91оомгин нернь",
"filedesc": "Учр-утх",
"fileuploadsummary": "Учр-утх:",
"savefile": "Хадһлх",
"filehist-datetime": "Өдр/цаг",
"filehist-thumb": "Зураллһн",
"filehist-thumbtext": "$1 янзин зураллһн",
- "filehist-user": "Демнч",
+ "filehist-user": "Демнәч",
"filehist-dimensions": "Юмна кир",
"filehist-comment": "Аҗгллһн",
"imagelinks": "Боомг керглән",
"statistics-users": "Бичгдлһтә [[Special:ListUsers|демнчнр]]",
"statistics-users-active": "Үүлтә демнчнр",
"statistics-users-active-desc": "Сүл $1 өдрт үүлдвр кесн демнчәнр",
- "brokenredirects-edit": "Ñ\87иклх",
+ "brokenredirects-edit": "Ñ\8fÑ\81х",
"brokenredirects-delete": "һарһх",
"nbytes": "$1 байт",
"nmembers": "$1 гешүн",
"prefs-rc": "近期變更",
"prefs-watchlist": "監視清單",
"prefs-editwatchlist": "編輯監視清單",
+ "prefs-editwatchlist-label": "編輯在您監視清單上的項目:",
+ "prefs-editwatchlist-edit": "檢視並移除在您監視清單上的標題",
+ "prefs-editwatchlist-raw": "編輯原始監視清單",
"prefs-editwatchlist-clear": "清除您的監視清單",
"prefs-watchlist-days": "監視清單中顯示的天數:",
"prefs-watchlist-days-max": "最多 $1 {{PLURAL:$1|天}}",
"right-override-export-depth": "匯出頁面包含連結內容,深度上限為 5 層",
"right-sendemail": "傳送電子郵件給其他使用者",
"right-passwordreset": "檢視重設密碼電子郵件",
+ "right-managechangetags": "建立並自資料庫移除[[Special:Tags|標籤]]",
"newuserlogpage": "建立使用者日誌",
"newuserlogpagetext": "此為建立使用者的日誌。",
"rightslog": "使用者權限日誌",
"action-viewmyprivateinfo": "檢視您的個人資訊",
"action-editmyprivateinfo": "編輯您的個人資訊",
"action-editcontentmodel": "編輯頁面的內容模型",
+ "action-managechangetags": "建立並自資料庫移除標籤",
"nchanges": "$1 次變更",
"enhancedrc-since-last-visit": "自上次訪問已有 $1",
"enhancedrc-history": "歷史",
"tags-tag": "標籤名稱",
"tags-display-header": "在變更日誌中顯示的方式",
"tags-description-header": "意義完整的說明",
+ "tags-source-header": "來源",
"tags-active-header": "開啟?",
"tags-hitcount-header": "已標記的變更",
+ "tags-actions-header": "操作",
"tags-active-yes": "是",
"tags-active-no": "否",
+ "tags-source-extension": "由擴充套件定義",
+ "tags-source-manual": "由使用者與機器人手動套用",
+ "tags-source-none": "不再使用",
"tags-edit": "編輯",
+ "tags-delete": "刪除",
+ "tags-activate": "啟動",
+ "tags-deactivate": "停用",
"tags-hitcount": "$1 次變更",
+ "tags-manage-no-permission": "您沒有權限管理變更標籤。",
+ "tags-create-heading": "建立新標籤",
+ "tags-create-explanation": "在預設情況下,新建立的標籤可被使用者及機器人使用。",
+ "tags-create-tag-name": "標籤名稱:",
+ "tags-create-reason": "原因:",
+ "tags-create-submit": "建立",
+ "tags-create-no-name": "您必須指定一個標籤名稱。",
+ "tags-create-invalid-chars": "標籤名稱不可包含逗號 (<code>,</code>) 或斜線 (<code>/</code>)。",
+ "tags-create-already-exists": "標籤 \"$1\" 已存在。",
+ "tags-create-warnings-below": "您是否要繼續建立標籤?",
+ "tags-delete-title": "刪除標籤",
+ "tags-delete-explanation-initial": "您正要從資料庫刪除標籤 \"$1\"。",
+ "tags-delete-reason": "原因:",
+ "tags-delete-submit": "無法取消刪除此標籤",
+ "tags-delete-not-found": "標籤 \"$1\" 不存在。",
+ "tags-activate-title": "啟動標籤",
+ "tags-activate-question": "您正要啟動標籤 \"$1\"。",
+ "tags-activate-reason": "原因:",
+ "tags-activate-not-allowed": "無法啟動標籤 \"$1\"。",
+ "tags-activate-not-found": "標籤 \"$1\" 不存在。",
+ "tags-activate-submit": "啟動",
+ "tags-deactivate-title": "停用標籤",
+ "tags-deactivate-question": "您正要停用標籤 \"$1\"。",
+ "tags-deactivate-reason": "原因:",
+ "tags-deactivate-not-allowed": "無法停用標籤 \"$1\"。",
+ "tags-deactivate-submit": "停用",
"comparepages": "比較頁面",
"compare-page1": "第 1 頁",
"compare-page2": "第 2 頁",
"logentry-upload-upload": "$1 {{GENDER:$2|已上傳}} $3",
"logentry-upload-overwrite": "$1 {{GENDER:$2|上傳了}}新版本的 $3",
"logentry-upload-revert": "$1 {{GENDER:$2|已上傳}} $3",
+ "log-name-managetags": "標籤管理日誌",
+ "log-description-managetags": "此頁面列出與[[Special:Tags|標籤]]相關的管理工作項目。 在日誌中僅包含由管理員手動所做的操作;被 Wiki 軟體所建立或刪除的標籤項目,不會記錄在此日誌中",
"rightsnone": "(無)",
"revdelete-summary": "編輯摘要",
"feedback-bugornote": "如果您準備要詳細描述一個技術問題,請至 [$1 回報問題]。\n或您可以使用以下的簡易表單回報問題,您的使用者名稱與評論將被新增到頁面 \"[$3 $2]\"。",
}
if ( $isatty && function_exists( 'readline' ) ) {
- return readline( $prompt );
+ $resp = readline( $prompt );
+ if ( $resp === null ) {
+ // Workaround for https://github.com/facebook/hhvm/issues/4776
+ return false;
+ } else {
+ return $resp;
+ }
} else {
if ( $isatty ) {
$st = self::readlineEmulation( $prompt );
}
protected function handleResourceModules( $realName, $value ) {
+ $defaults = array();
+ $remote = $this->hasOption( 'skin' ) ? 'remoteSkinPath' : 'remoteExtPath';
foreach ( $value as $name => $data ) {
if ( isset( $data['localBasePath'] ) ) {
$data['localBasePath'] = $this->stripPath( $data['localBasePath'], $this->dir );
+ if ( !$defaults ) {
+ $defaults['localBasePath'] = $data['localBasePath'];
+ unset( $data['localBasePath'] );
+ if ( isset( $data[$remote] ) ) {
+ $defaults[$remote] = $data[$remote];
+ unset( $data[$remote] );
+ }
+ } else {
+ if ( $data['localBasePath'] === $defaults['localBasePath'] ) {
+ unset( $data['localBasePath'] );
+ }
+ if ( isset( $data[$remote] ) && isset( $defaults[$remote] )
+ && $data[$remote] === $defaults[$remote]
+ ) {
+ unset( $data[$remote] );
+ }
+ }
}
+
+
$this->json[$realName][$name] = $data;
}
+ if ( $defaults ) {
+ $this->json['ResourceFileModulePaths'] = $defaults;
+ }
}
}
"test": "grunt test"
},
"devDependencies": {
- "grunt": "0.4.2",
+ "grunt": "0.4.5",
"grunt-banana-checker": "0.2.0",
- "grunt-contrib-jshint": "0.10.0",
+ "grunt-contrib-jshint": "0.11.0",
"grunt-contrib-watch": "0.6.1",
- "grunt-jscs": "0.8.1",
+ "grunt-jscs": "1.5.0",
"grunt-jsonlint": "1.0.4",
- "grunt-karma": "0.9.0",
+ "grunt-karma": "0.10.1",
"karma": "0.12.31",
"karma-chrome-launcher": "0.1.7",
- "karma-firefox-launcher": "0.1.3",
+ "karma-firefox-launcher": "0.1.4",
"karma-qunit": "0.1.4",
- "qunitjs": "1.15.0"
+ "qunitjs": "1.17.1"
}
}
<!DOCTYPE html>
<html>
<head>
- <meta charset="UTF-8">
+ <meta charset="UTF-8" />
<title>Profiling data</title>
<style>
/* noc.wikimedia.org/base.css */
?>
</tbody>
</table>
-<hr>
+<hr />
<p>Total time: <code><?php printf( '%5.02f', profile_point::$totaltime ); ?></code></p>
<p>Total memory: <code><?php printf( '%5.02f', profile_point::$totalmemory / 1024 ); ?></code></p>
'targets' => array( 'desktop', 'mobile' ),
),
'jquery.qunit' => array(
- 'scripts' => 'resources/lib/jquery/jquery.qunit.js',
- 'styles' => 'resources/lib/jquery/jquery.qunit.css',
+ 'scripts' => 'resources/lib/qunitjs/qunit.js',
+ 'styles' => 'resources/lib/qunitjs/qunit.css',
'position' => 'top',
'targets' => array( 'desktop', 'mobile' ),
),
/* MediaWiki */
'mediawiki' => array(
- 'scripts' => 'resources/src/mediawiki/mediawiki.js',
+ 'scripts' => array(
+ 'resources/src/mediawiki/mediawiki.js',
+ 'resources/src/mediawiki/mediawiki.startUp.js',
+ ),
'debugScripts' => 'resources/src/mediawiki/mediawiki.log.js',
'raw' => true,
'targets' => array( 'desktop', 'mobile' ),
+++ /dev/null
-/*!
- * QUnit 1.16.0
- * http://qunitjs.com/
- *
- * Copyright 2006, 2014 jQuery Foundation and other contributors
- * Released under the MIT license
- * http://jquery.org/license
- *
- * Date: 2014-12-03T16:32Z
- */
-
-/** Font Family and Sizes */
-
-#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult {
- font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
-}
-
-#qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
-#qunit-tests { font-size: smaller; }
-
-
-/** Resets */
-
-#qunit-tests, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter {
- margin: 0;
- padding: 0;
-}
-
-
-/** Header */
-
-#qunit-header {
- padding: 0.5em 0 0.5em 1em;
-
- color: #8699A4;
- background-color: #0D3349;
-
- font-size: 1.5em;
- line-height: 1em;
- font-weight: 400;
-
- border-radius: 5px 5px 0 0;
-}
-
-#qunit-header a {
- text-decoration: none;
- color: #C2CCD1;
-}
-
-#qunit-header a:hover,
-#qunit-header a:focus {
- color: #FFF;
-}
-
-#qunit-testrunner-toolbar label {
- display: inline-block;
- padding: 0 0.5em 0 0.1em;
-}
-
-#qunit-banner {
- height: 5px;
-}
-
-#qunit-testrunner-toolbar {
- padding: 0.5em 1em 0.5em 1em;
- color: #5E740B;
- background-color: #EEE;
- overflow: hidden;
-}
-
-#qunit-userAgent {
- padding: 0.5em 1em 0.5em 1em;
- background-color: #2B81AF;
- color: #FFF;
- text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
-}
-
-#qunit-modulefilter-container {
- float: right;
-}
-
-/** Tests: Pass/Fail */
-
-#qunit-tests {
- list-style-position: inside;
-}
-
-#qunit-tests li {
- padding: 0.4em 1em 0.4em 1em;
- border-bottom: 1px solid #FFF;
- list-style-position: inside;
-}
-
-#qunit-tests > li {
- display: none;
-}
-
-#qunit-tests li.pass, #qunit-tests li.running, #qunit-tests li.fail {
- display: list-item;
-}
-
-#qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running {
- display: none;
-}
-
-#qunit-tests li strong {
- cursor: pointer;
-}
-
-#qunit-tests li.skipped strong {
- cursor: default;
-}
-
-#qunit-tests li a {
- padding: 0.5em;
- color: #C2CCD1;
- text-decoration: none;
-}
-#qunit-tests li a:hover,
-#qunit-tests li a:focus {
- color: #000;
-}
-
-#qunit-tests li .runtime {
- float: right;
- font-size: smaller;
-}
-
-.qunit-assert-list {
- margin-top: 0.5em;
- padding: 0.5em;
-
- background-color: #FFF;
-
- border-radius: 5px;
-}
-
-.qunit-collapsed {
- display: none;
-}
-
-#qunit-tests table {
- border-collapse: collapse;
- margin-top: 0.2em;
-}
-
-#qunit-tests th {
- text-align: right;
- vertical-align: top;
- padding: 0 0.5em 0 0;
-}
-
-#qunit-tests td {
- vertical-align: top;
-}
-
-#qunit-tests pre {
- margin: 0;
- white-space: pre-wrap;
- word-wrap: break-word;
-}
-
-#qunit-tests del {
- background-color: #E0F2BE;
- color: #374E0C;
- text-decoration: none;
-}
-
-#qunit-tests ins {
- background-color: #FFCACA;
- color: #500;
- text-decoration: none;
-}
-
-/*** Test Counts */
-
-#qunit-tests b.counts { color: #000; }
-#qunit-tests b.passed { color: #5E740B; }
-#qunit-tests b.failed { color: #710909; }
-
-#qunit-tests li li {
- padding: 5px;
- background-color: #FFF;
- border-bottom: none;
- list-style-position: inside;
-}
-
-/*** Passing Styles */
-
-#qunit-tests li li.pass {
- color: #3C510C;
- background-color: #FFF;
- border-left: 10px solid #C6E746;
-}
-
-#qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; }
-#qunit-tests .pass .test-name { color: #366097; }
-
-#qunit-tests .pass .test-actual,
-#qunit-tests .pass .test-expected { color: #999; }
-
-#qunit-banner.qunit-pass { background-color: #C6E746; }
-
-/*** Failing Styles */
-
-#qunit-tests li li.fail {
- color: #710909;
- background-color: #FFF;
- border-left: 10px solid #EE5757;
- white-space: pre;
-}
-
-#qunit-tests > li:last-child {
- border-radius: 0 0 5px 5px;
-}
-
-#qunit-tests .fail { color: #000; background-color: #EE5757; }
-#qunit-tests .fail .test-name,
-#qunit-tests .fail .module-name { color: #000; }
-
-#qunit-tests .fail .test-actual { color: #EE5757; }
-#qunit-tests .fail .test-expected { color: #008000; }
-
-#qunit-banner.qunit-fail { background-color: #EE5757; }
-
-/*** Skipped tests */
-
-#qunit-tests .skipped {
- background-color: #EBECE9;
-}
-
-#qunit-tests .qunit-skipped-label {
- background-color: #F4FF77;
- display: inline-block;
- font-style: normal;
- color: #366097;
- line-height: 1.8em;
- padding: 0 0.5em;
- margin: -0.4em 0.4em -0.4em 0;
-}
-
-/** Result */
-
-#qunit-testresult {
- padding: 0.5em 1em 0.5em 1em;
-
- color: #2B81AF;
- background-color: #D2E0E6;
-
- border-bottom: 1px solid #FFF;
-}
-#qunit-testresult .module-name {
- font-weight: 700;
-}
-
-/** Fixture */
-
-#qunit-fixture {
- position: absolute;
- top: -10000px;
- left: -10000px;
- width: 1000px;
- height: 1000px;
-}
+++ /dev/null
-/*!
- * QUnit 1.16.0
- * http://qunitjs.com/
- *
- * Copyright 2006, 2014 jQuery Foundation and other contributors
- * Released under the MIT license
- * http://jquery.org/license
- *
- * Date: 2014-12-03T16:32Z
- */
-
-(function( window ) {
-
-var QUnit,
- config,
- onErrorFnPrev,
- loggingCallbacks = {},
- fileName = ( sourceFromStacktrace( 0 ) || "" ).replace( /(:\d+)+\)?/, "" ).replace( /.+\//, "" ),
- toString = Object.prototype.toString,
- hasOwn = Object.prototype.hasOwnProperty,
- // Keep a local reference to Date (GH-283)
- Date = window.Date,
- now = Date.now || function() {
- return new Date().getTime();
- },
- globalStartCalled = false,
- runStarted = false,
- setTimeout = window.setTimeout,
- clearTimeout = window.clearTimeout,
- defined = {
- document: window.document !== undefined,
- setTimeout: window.setTimeout !== undefined,
- sessionStorage: (function() {
- var x = "qunit-test-string";
- try {
- sessionStorage.setItem( x, x );
- sessionStorage.removeItem( x );
- return true;
- } catch ( e ) {
- return false;
- }
- }())
- },
- /**
- * Provides a normalized error string, correcting an issue
- * with IE 7 (and prior) where Error.prototype.toString is
- * not properly implemented
- *
- * Based on http://es5.github.com/#x15.11.4.4
- *
- * @param {String|Error} error
- * @return {String} error message
- */
- errorString = function( error ) {
- var name, message,
- errorString = error.toString();
- if ( errorString.substring( 0, 7 ) === "[object" ) {
- name = error.name ? error.name.toString() : "Error";
- message = error.message ? error.message.toString() : "";
- if ( name && message ) {
- return name + ": " + message;
- } else if ( name ) {
- return name;
- } else if ( message ) {
- return message;
- } else {
- return "Error";
- }
- } else {
- return errorString;
- }
- },
- /**
- * Makes a clone of an object using only Array or Object as base,
- * and copies over the own enumerable properties.
- *
- * @param {Object} obj
- * @return {Object} New object with only the own properties (recursively).
- */
- objectValues = function( obj ) {
- var key, val,
- vals = QUnit.is( "array", obj ) ? [] : {};
- for ( key in obj ) {
- if ( hasOwn.call( obj, key ) ) {
- val = obj[ key ];
- vals[ key ] = val === Object( val ) ? objectValues( val ) : val;
- }
- }
- return vals;
- };
-
-QUnit = {};
-
-/**
- * Config object: Maintain internal state
- * Later exposed as QUnit.config
- * `config` initialized at top of scope
- */
-config = {
- // The queue of tests to run
- queue: [],
-
- // block until document ready
- blocking: true,
-
- // when enabled, show only failing tests
- // gets persisted through sessionStorage and can be changed in UI via checkbox
- hidepassed: false,
-
- // by default, run previously failed tests first
- // very useful in combination with "Hide passed tests" checked
- reorder: true,
-
- // by default, modify document.title when suite is done
- altertitle: true,
-
- // by default, scroll to top of the page when suite is done
- scrolltop: true,
-
- // when enabled, all tests must call expect()
- requireExpects: false,
-
- // add checkboxes that are persisted in the query-string
- // when enabled, the id is set to `true` as a `QUnit.config` property
- urlConfig: [
- {
- id: "hidepassed",
- label: "Hide passed tests",
- tooltip: "Only show tests and assertions that fail. Stored as query-strings."
- },
- {
- id: "noglobals",
- label: "Check for Globals",
- tooltip: "Enabling this will test if any test introduces new properties on the " +
- "`window` object. Stored as query-strings."
- },
- {
- id: "notrycatch",
- label: "No try-catch",
- tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging " +
- "exceptions in IE reasonable. Stored as query-strings."
- }
- ],
-
- // Set of all modules.
- modules: [],
-
- // The first unnamed module
- currentModule: {
- name: "",
- tests: []
- },
-
- callbacks: {}
-};
-
-// Push a loose unnamed module to the modules collection
-config.modules.push( config.currentModule );
-
-// Initialize more QUnit.config and QUnit.urlParams
-(function() {
- var i, current,
- location = window.location || { search: "", protocol: "file:" },
- params = location.search.slice( 1 ).split( "&" ),
- length = params.length,
- urlParams = {};
-
- if ( params[ 0 ] ) {
- for ( i = 0; i < length; i++ ) {
- current = params[ i ].split( "=" );
- current[ 0 ] = decodeURIComponent( current[ 0 ] );
-
- // allow just a key to turn on a flag, e.g., test.html?noglobals
- current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true;
- if ( urlParams[ current[ 0 ] ] ) {
- urlParams[ current[ 0 ] ] = [].concat( urlParams[ current[ 0 ] ], current[ 1 ] );
- } else {
- urlParams[ current[ 0 ] ] = current[ 1 ];
- }
- }
- }
-
- QUnit.urlParams = urlParams;
-
- // String search anywhere in moduleName+testName
- config.filter = urlParams.filter;
-
- config.testId = [];
- if ( urlParams.testId ) {
-
- // Ensure that urlParams.testId is an array
- urlParams.testId = [].concat( urlParams.testId );
- for ( i = 0; i < urlParams.testId.length; i++ ) {
- config.testId.push( urlParams.testId[ i ] );
- }
- }
-
- // Figure out if we're running the tests from a server or not
- QUnit.isLocal = location.protocol === "file:";
-}());
-
-// Root QUnit object.
-// `QUnit` initialized at top of scope
-extend( QUnit, {
-
- // call on start of module test to prepend name to all tests
- module: function( name, testEnvironment ) {
- var currentModule = {
- name: name,
- testEnvironment: testEnvironment,
- tests: []
- };
-
- // DEPRECATED: handles setup/teardown functions,
- // beforeEach and afterEach should be used instead
- if ( testEnvironment && testEnvironment.setup ) {
- testEnvironment.beforeEach = testEnvironment.setup;
- delete testEnvironment.setup;
- }
- if ( testEnvironment && testEnvironment.teardown ) {
- testEnvironment.afterEach = testEnvironment.teardown;
- delete testEnvironment.teardown;
- }
-
- config.modules.push( currentModule );
- config.currentModule = currentModule;
- },
-
- // DEPRECATED: QUnit.asyncTest() will be removed in QUnit 2.0.
- asyncTest: function( testName, expected, callback ) {
- if ( arguments.length === 2 ) {
- callback = expected;
- expected = null;
- }
-
- QUnit.test( testName, expected, callback, true );
- },
-
- test: function( testName, expected, callback, async ) {
- var test;
-
- if ( arguments.length === 2 ) {
- callback = expected;
- expected = null;
- }
-
- test = new Test({
- testName: testName,
- expected: expected,
- async: async,
- callback: callback
- });
-
- test.queue();
- },
-
- skip: function( testName ) {
- var test = new Test({
- testName: testName,
- skip: true
- });
-
- test.queue();
- },
-
- // DEPRECATED: The functionality of QUnit.start() will be altered in QUnit 2.0.
- // In QUnit 2.0, invoking it will ONLY affect the `QUnit.config.autostart` blocking behavior.
- start: function( count ) {
- var globalStartAlreadyCalled = globalStartCalled;
-
- if ( !config.current ) {
- globalStartCalled = true;
-
- if ( runStarted ) {
- throw new Error( "Called start() outside of a test context while already started" );
- } else if ( globalStartAlreadyCalled || count > 1 ) {
- throw new Error( "Called start() outside of a test context too many times" );
- } else if ( config.autostart ) {
- throw new Error( "Called start() outside of a test context when " +
- "QUnit.config.autostart was true" );
- } else if ( !config.pageLoaded ) {
-
- // The page isn't completely loaded yet, so bail out and let `QUnit.load` handle it
- config.autostart = true;
- return;
- }
- } else {
-
- // If a test is running, adjust its semaphore
- config.current.semaphore -= count || 1;
-
- // Don't start until equal number of stop-calls
- if ( config.current.semaphore > 0 ) {
- return;
- }
-
- // throw an Error if start is called more often than stop
- if ( config.current.semaphore < 0 ) {
- config.current.semaphore = 0;
-
- QUnit.pushFailure(
- "Called start() while already started (test's semaphore was 0 already)",
- sourceFromStacktrace( 2 )
- );
- return;
- }
- }
-
- resumeProcessing();
- },
-
- // DEPRECATED: QUnit.stop() will be removed in QUnit 2.0.
- stop: function( count ) {
-
- // If there isn't a test running, don't allow QUnit.stop() to be called
- if ( !config.current ) {
- throw new Error( "Called stop() outside of a test context" );
- }
-
- // If a test is running, adjust its semaphore
- config.current.semaphore += count || 1;
-
- pauseProcessing();
- },
-
- config: config,
-
- // Safe object type checking
- is: function( type, obj ) {
- return QUnit.objectType( obj ) === type;
- },
-
- objectType: function( obj ) {
- if ( typeof obj === "undefined" ) {
- return "undefined";
- }
-
- // Consider: typeof null === object
- if ( obj === null ) {
- return "null";
- }
-
- var match = toString.call( obj ).match( /^\[object\s(.*)\]$/ ),
- type = match && match[ 1 ] || "";
-
- switch ( type ) {
- case "Number":
- if ( isNaN( obj ) ) {
- return "nan";
- }
- return "number";
- case "String":
- case "Boolean":
- case "Array":
- case "Date":
- case "RegExp":
- case "Function":
- return type.toLowerCase();
- }
- if ( typeof obj === "object" ) {
- return "object";
- }
- return undefined;
- },
-
- url: function( params ) {
- params = extend( extend( {}, QUnit.urlParams ), params );
- var key,
- querystring = "?";
-
- for ( key in params ) {
- if ( hasOwn.call( params, key ) ) {
- querystring += encodeURIComponent( key );
- if ( params[ key ] !== true ) {
- querystring += "=" + encodeURIComponent( params[ key ] );
- }
- querystring += "&";
- }
- }
- return location.protocol + "//" + location.host +
- location.pathname + querystring.slice( 0, -1 );
- },
-
- extend: extend,
-
- load: function() {
- config.pageLoaded = true;
-
- // Initialize the configuration options
- extend( config, {
- stats: { all: 0, bad: 0 },
- moduleStats: { all: 0, bad: 0 },
- started: 0,
- updateRate: 1000,
- autostart: true,
- filter: ""
- }, true );
-
- config.blocking = false;
-
- if ( config.autostart ) {
- resumeProcessing();
- }
- }
-});
-
-// Register logging callbacks
-(function() {
- var i, l, key,
- callbacks = [ "begin", "done", "log", "testStart", "testDone",
- "moduleStart", "moduleDone" ];
-
- function registerLoggingCallback( key ) {
- var loggingCallback = function( callback ) {
- if ( QUnit.objectType( callback ) !== "function" ) {
- throw new Error(
- "QUnit logging methods require a callback function as their first parameters."
- );
- }
-
- config.callbacks[ key ].push( callback );
- };
-
- // DEPRECATED: This will be removed on QUnit 2.0.0+
- // Stores the registered functions allowing restoring
- // at verifyLoggingCallbacks() if modified
- loggingCallbacks[ key ] = loggingCallback;
-
- return loggingCallback;
- }
-
- for ( i = 0, l = callbacks.length; i < l; i++ ) {
- key = callbacks[ i ];
-
- // Initialize key collection of logging callback
- if ( QUnit.objectType( config.callbacks[ key ] ) === "undefined" ) {
- config.callbacks[ key ] = [];
- }
-
- QUnit[ key ] = registerLoggingCallback( key );
- }
-})();
-
-// `onErrorFnPrev` initialized at top of scope
-// Preserve other handlers
-onErrorFnPrev = window.onerror;
-
-// Cover uncaught exceptions
-// Returning true will suppress the default browser handler,
-// returning false will let it run.
-window.onerror = function( error, filePath, linerNr ) {
- var ret = false;
- if ( onErrorFnPrev ) {
- ret = onErrorFnPrev( error, filePath, linerNr );
- }
-
- // Treat return value as window.onerror itself does,
- // Only do our handling if not suppressed.
- if ( ret !== true ) {
- if ( QUnit.config.current ) {
- if ( QUnit.config.current.ignoreGlobalErrors ) {
- return true;
- }
- QUnit.pushFailure( error, filePath + ":" + linerNr );
- } else {
- QUnit.test( "global failure", extend(function() {
- QUnit.pushFailure( error, filePath + ":" + linerNr );
- }, { validTest: true } ) );
- }
- return false;
- }
-
- return ret;
-};
-
-function done() {
- var runtime, passed;
-
- config.autorun = true;
-
- // Log the last module results
- if ( config.previousModule ) {
- runLoggingCallbacks( "moduleDone", {
- name: config.previousModule.name,
- tests: config.previousModule.tests,
- failed: config.moduleStats.bad,
- passed: config.moduleStats.all - config.moduleStats.bad,
- total: config.moduleStats.all,
- runtime: now() - config.moduleStats.started
- });
- }
- delete config.previousModule;
-
- runtime = now() - config.started;
- passed = config.stats.all - config.stats.bad;
-
- runLoggingCallbacks( "done", {
- failed: config.stats.bad,
- passed: passed,
- total: config.stats.all,
- runtime: runtime
- });
-}
-
-// Doesn't support IE6 to IE9
-// See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack
-function extractStacktrace( e, offset ) {
- offset = offset === undefined ? 4 : offset;
-
- var stack, include, i;
-
- if ( e.stacktrace ) {
-
- // Opera 12.x
- return e.stacktrace.split( "\n" )[ offset + 3 ];
- } else if ( e.stack ) {
-
- // Firefox, Chrome, Safari 6+, IE10+, PhantomJS and Node
- stack = e.stack.split( "\n" );
- if ( /^error$/i.test( stack[ 0 ] ) ) {
- stack.shift();
- }
- if ( fileName ) {
- include = [];
- for ( i = offset; i < stack.length; i++ ) {
- if ( stack[ i ].indexOf( fileName ) !== -1 ) {
- break;
- }
- include.push( stack[ i ] );
- }
- if ( include.length ) {
- return include.join( "\n" );
- }
- }
- return stack[ offset ];
- } else if ( e.sourceURL ) {
-
- // Safari < 6
- // exclude useless self-reference for generated Error objects
- if ( /qunit.js$/.test( e.sourceURL ) ) {
- return;
- }
-
- // for actual exceptions, this is useful
- return e.sourceURL + ":" + e.line;
- }
-}
-
-function sourceFromStacktrace( offset ) {
- var e = new Error();
- if ( !e.stack ) {
- try {
- throw e;
- } catch ( err ) {
- // This should already be true in most browsers
- e = err;
- }
- }
- return extractStacktrace( e, offset );
-}
-
-function synchronize( callback, last ) {
- if ( QUnit.objectType( callback ) === "array" ) {
- while ( callback.length ) {
- synchronize( callback.shift() );
- }
- return;
- }
- config.queue.push( callback );
-
- if ( config.autorun && !config.blocking ) {
- process( last );
- }
-}
-
-function process( last ) {
- function next() {
- process( last );
- }
- var start = now();
- config.depth = config.depth ? config.depth + 1 : 1;
-
- while ( config.queue.length && !config.blocking ) {
- if ( !defined.setTimeout || config.updateRate <= 0 ||
- ( ( now() - start ) < config.updateRate ) ) {
- if ( config.current ) {
-
- // Reset async tracking for each phase of the Test lifecycle
- config.current.usedAsync = false;
- }
- config.queue.shift()();
- } else {
- setTimeout( next, 13 );
- break;
- }
- }
- config.depth--;
- if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) {
- done();
- }
-}
-
-function begin() {
- var i, l,
- modulesLog = [];
-
- // If the test run hasn't officially begun yet
- if ( !config.started ) {
-
- // Record the time of the test run's beginning
- config.started = now();
-
- verifyLoggingCallbacks();
-
- // Delete the loose unnamed module if unused.
- if ( config.modules[ 0 ].name === "" && config.modules[ 0 ].tests.length === 0 ) {
- config.modules.shift();
- }
-
- // Avoid unnecessary information by not logging modules' test environments
- for ( i = 0, l = config.modules.length; i < l; i++ ) {
- modulesLog.push({
- name: config.modules[ i ].name,
- tests: config.modules[ i ].tests
- });
- }
-
- // The test run is officially beginning now
- runLoggingCallbacks( "begin", {
- totalTests: Test.count,
- modules: modulesLog
- });
- }
-
- config.blocking = false;
- process( true );
-}
-
-function resumeProcessing() {
- runStarted = true;
-
- // A slight delay to allow this iteration of the event loop to finish (more assertions, etc.)
- if ( defined.setTimeout ) {
- setTimeout(function() {
- if ( config.current && config.current.semaphore > 0 ) {
- return;
- }
- if ( config.timeout ) {
- clearTimeout( config.timeout );
- }
-
- begin();
- }, 13 );
- } else {
- begin();
- }
-}
-
-function pauseProcessing() {
- config.blocking = true;
-
- if ( config.testTimeout && defined.setTimeout ) {
- clearTimeout( config.timeout );
- config.timeout = setTimeout(function() {
- if ( config.current ) {
- config.current.semaphore = 0;
- QUnit.pushFailure( "Test timed out", sourceFromStacktrace( 2 ) );
- } else {
- throw new Error( "Test timed out" );
- }
- resumeProcessing();
- }, config.testTimeout );
- }
-}
-
-function saveGlobal() {
- config.pollution = [];
-
- if ( config.noglobals ) {
- for ( var key in window ) {
- if ( hasOwn.call( window, key ) ) {
- // in Opera sometimes DOM element ids show up here, ignore them
- if ( /^qunit-test-output/.test( key ) ) {
- continue;
- }
- config.pollution.push( key );
- }
- }
- }
-}
-
-function checkPollution() {
- var newGlobals,
- deletedGlobals,
- old = config.pollution;
-
- saveGlobal();
-
- newGlobals = diff( config.pollution, old );
- if ( newGlobals.length > 0 ) {
- QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join( ", " ) );
- }
-
- deletedGlobals = diff( old, config.pollution );
- if ( deletedGlobals.length > 0 ) {
- QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join( ", " ) );
- }
-}
-
-// returns a new Array with the elements that are in a but not in b
-function diff( a, b ) {
- var i, j,
- result = a.slice();
-
- for ( i = 0; i < result.length; i++ ) {
- for ( j = 0; j < b.length; j++ ) {
- if ( result[ i ] === b[ j ] ) {
- result.splice( i, 1 );
- i--;
- break;
- }
- }
- }
- return result;
-}
-
-function extend( a, b, undefOnly ) {
- for ( var prop in b ) {
- if ( hasOwn.call( b, prop ) ) {
-
- // Avoid "Member not found" error in IE8 caused by messing with window.constructor
- if ( !( prop === "constructor" && a === window ) ) {
- if ( b[ prop ] === undefined ) {
- delete a[ prop ];
- } else if ( !( undefOnly && typeof a[ prop ] !== "undefined" ) ) {
- a[ prop ] = b[ prop ];
- }
- }
- }
- }
-
- return a;
-}
-
-function runLoggingCallbacks( key, args ) {
- var i, l, callbacks;
-
- callbacks = config.callbacks[ key ];
- for ( i = 0, l = callbacks.length; i < l; i++ ) {
- callbacks[ i ]( args );
- }
-}
-
-// DEPRECATED: This will be removed on 2.0.0+
-// This function verifies if the loggingCallbacks were modified by the user
-// If so, it will restore it, assign the given callback and print a console warning
-function verifyLoggingCallbacks() {
- var loggingCallback, userCallback;
-
- for ( loggingCallback in loggingCallbacks ) {
- if ( QUnit[ loggingCallback ] !== loggingCallbacks[ loggingCallback ] ) {
-
- userCallback = QUnit[ loggingCallback ];
-
- // Restore the callback function
- QUnit[ loggingCallback ] = loggingCallbacks[ loggingCallback ];
-
- // Assign the deprecated given callback
- QUnit[ loggingCallback ]( userCallback );
-
- if ( window.console && window.console.warn ) {
- window.console.warn(
- "QUnit." + loggingCallback + " was replaced with a new value.\n" +
- "Please, check out the documentation on how to apply logging callbacks.\n" +
- "Reference: http://api.qunitjs.com/category/callbacks/"
- );
- }
- }
- }
-}
-
-// from jquery.js
-function inArray( elem, array ) {
- if ( array.indexOf ) {
- return array.indexOf( elem );
- }
-
- for ( var i = 0, length = array.length; i < length; i++ ) {
- if ( array[ i ] === elem ) {
- return i;
- }
- }
-
- return -1;
-}
-
-function Test( settings ) {
- var i, l;
-
- ++Test.count;
-
- extend( this, settings );
- this.assertions = [];
- this.semaphore = 0;
- this.usedAsync = false;
- this.module = config.currentModule;
- this.stack = sourceFromStacktrace( 3 );
-
- // Register unique strings
- for ( i = 0, l = this.module.tests; i < l.length; i++ ) {
- if ( this.module.tests[ i ].name === this.testName ) {
- this.testName += " ";
- }
- }
-
- this.testId = generateHash( this.module.name, this.testName );
-
- this.module.tests.push({
- name: this.testName,
- testId: this.testId
- });
-
- if ( settings.skip ) {
-
- // Skipped tests will fully ignore any sent callback
- this.callback = function() {};
- this.async = false;
- this.expected = 0;
- } else {
- this.assert = new Assert( this );
- }
-}
-
-Test.count = 0;
-
-Test.prototype = {
- before: function() {
- if (
-
- // Emit moduleStart when we're switching from one module to another
- this.module !== config.previousModule ||
-
- // They could be equal (both undefined) but if the previousModule property doesn't
- // yet exist it means this is the first test in a suite that isn't wrapped in a
- // module, in which case we'll just emit a moduleStart event for 'undefined'.
- // Without this, reporters can get testStart before moduleStart which is a problem.
- !hasOwn.call( config, "previousModule" )
- ) {
- if ( hasOwn.call( config, "previousModule" ) ) {
- runLoggingCallbacks( "moduleDone", {
- name: config.previousModule.name,
- tests: config.previousModule.tests,
- failed: config.moduleStats.bad,
- passed: config.moduleStats.all - config.moduleStats.bad,
- total: config.moduleStats.all,
- runtime: now() - config.moduleStats.started
- });
- }
- config.previousModule = this.module;
- config.moduleStats = { all: 0, bad: 0, started: now() };
- runLoggingCallbacks( "moduleStart", {
- name: this.module.name,
- tests: this.module.tests
- });
- }
-
- config.current = this;
-
- this.testEnvironment = extend( {}, this.module.testEnvironment );
- delete this.testEnvironment.beforeEach;
- delete this.testEnvironment.afterEach;
-
- this.started = now();
- runLoggingCallbacks( "testStart", {
- name: this.testName,
- module: this.module.name,
- testId: this.testId
- });
-
- if ( !config.pollution ) {
- saveGlobal();
- }
- },
-
- run: function() {
- var promise;
-
- config.current = this;
-
- if ( this.async ) {
- QUnit.stop();
- }
-
- this.callbackStarted = now();
-
- if ( config.notrycatch ) {
- promise = this.callback.call( this.testEnvironment, this.assert );
- this.resolvePromise( promise );
- return;
- }
-
- try {
- promise = this.callback.call( this.testEnvironment, this.assert );
- this.resolvePromise( promise );
- } catch ( e ) {
- this.pushFailure( "Died on test #" + ( this.assertions.length + 1 ) + " " +
- this.stack + ": " + ( e.message || e ), extractStacktrace( e, 0 ) );
-
- // else next test will carry the responsibility
- saveGlobal();
-
- // Restart the tests if they're blocking
- if ( config.blocking ) {
- QUnit.start();
- }
- }
- },
-
- after: function() {
- checkPollution();
- },
-
- queueHook: function( hook, hookName ) {
- var promise,
- test = this;
- return function runHook() {
- config.current = test;
- if ( config.notrycatch ) {
- promise = hook.call( test.testEnvironment, test.assert );
- test.resolvePromise( promise, hookName );
- return;
- }
- try {
- promise = hook.call( test.testEnvironment, test.assert );
- test.resolvePromise( promise, hookName );
- } catch ( error ) {
- test.pushFailure( hookName + " failed on " + test.testName + ": " +
- ( error.message || error ), extractStacktrace( error, 0 ) );
- }
- };
- },
-
- // Currently only used for module level hooks, can be used to add global level ones
- hooks: function( handler ) {
- var hooks = [];
-
- // Hooks are ignored on skipped tests
- if ( this.skip ) {
- return hooks;
- }
-
- if ( this.module.testEnvironment &&
- QUnit.objectType( this.module.testEnvironment[ handler ] ) === "function" ) {
- hooks.push( this.queueHook( this.module.testEnvironment[ handler ], handler ) );
- }
-
- return hooks;
- },
-
- finish: function() {
- config.current = this;
- if ( config.requireExpects && this.expected === null ) {
- this.pushFailure( "Expected number of assertions to be defined, but expect() was " +
- "not called.", this.stack );
- } else if ( this.expected !== null && this.expected !== this.assertions.length ) {
- this.pushFailure( "Expected " + this.expected + " assertions, but " +
- this.assertions.length + " were run", this.stack );
- } else if ( this.expected === null && !this.assertions.length ) {
- this.pushFailure( "Expected at least one assertion, but none were run - call " +
- "expect(0) to accept zero assertions.", this.stack );
- }
-
- var i,
- bad = 0;
-
- this.runtime = now() - this.started;
- config.stats.all += this.assertions.length;
- config.moduleStats.all += this.assertions.length;
-
- for ( i = 0; i < this.assertions.length; i++ ) {
- if ( !this.assertions[ i ].result ) {
- bad++;
- config.stats.bad++;
- config.moduleStats.bad++;
- }
- }
-
- runLoggingCallbacks( "testDone", {
- name: this.testName,
- module: this.module.name,
- skipped: !!this.skip,
- failed: bad,
- passed: this.assertions.length - bad,
- total: this.assertions.length,
- runtime: this.runtime,
-
- // HTML Reporter use
- assertions: this.assertions,
- testId: this.testId,
-
- // DEPRECATED: this property will be removed in 2.0.0, use runtime instead
- duration: this.runtime
- });
-
- // QUnit.reset() is deprecated and will be replaced for a new
- // fixture reset function on QUnit 2.0/2.1.
- // It's still called here for backwards compatibility handling
- QUnit.reset();
-
- config.current = undefined;
- },
-
- queue: function() {
- var bad,
- test = this;
-
- if ( !this.valid() ) {
- return;
- }
-
- function run() {
-
- // each of these can by async
- synchronize([
- function() {
- test.before();
- },
-
- test.hooks( "beforeEach" ),
-
- function() {
- test.run();
- },
-
- test.hooks( "afterEach" ).reverse(),
-
- function() {
- test.after();
- },
- function() {
- test.finish();
- }
- ]);
- }
-
- // `bad` initialized at top of scope
- // defer when previous test run passed, if storage is available
- bad = QUnit.config.reorder && defined.sessionStorage &&
- +sessionStorage.getItem( "qunit-test-" + this.module.name + "-" + this.testName );
-
- if ( bad ) {
- run();
- } else {
- synchronize( run, true );
- }
- },
-
- push: function( result, actual, expected, message ) {
- var source,
- details = {
- module: this.module.name,
- name: this.testName,
- result: result,
- message: message,
- actual: actual,
- expected: expected,
- testId: this.testId,
- runtime: now() - this.started
- };
-
- if ( !result ) {
- source = sourceFromStacktrace();
-
- if ( source ) {
- details.source = source;
- }
- }
-
- runLoggingCallbacks( "log", details );
-
- this.assertions.push({
- result: !!result,
- message: message
- });
- },
-
- pushFailure: function( message, source, actual ) {
- if ( !this instanceof Test ) {
- throw new Error( "pushFailure() assertion outside test context, was " +
- sourceFromStacktrace( 2 ) );
- }
-
- var details = {
- module: this.module.name,
- name: this.testName,
- result: false,
- message: message || "error",
- actual: actual || null,
- testId: this.testId,
- runtime: now() - this.started
- };
-
- if ( source ) {
- details.source = source;
- }
-
- runLoggingCallbacks( "log", details );
-
- this.assertions.push({
- result: false,
- message: message
- });
- },
-
- resolvePromise: function( promise, phase ) {
- var then, message,
- test = this;
- if ( promise != null ) {
- then = promise.then;
- if ( QUnit.objectType( then ) === "function" ) {
- QUnit.stop();
- then.call(
- promise,
- QUnit.start,
- function( error ) {
- message = "Promise rejected " +
- ( !phase ? "during" : phase.replace( /Each$/, "" ) ) +
- " " + test.testName + ": " + ( error.message || error );
- test.pushFailure( message, extractStacktrace( error, 0 ) );
-
- // else next test will carry the responsibility
- saveGlobal();
-
- // Unblock
- QUnit.start();
- }
- );
- }
- }
- },
-
- valid: function() {
- var include,
- filter = config.filter && config.filter.toLowerCase(),
- module = QUnit.urlParams.module && QUnit.urlParams.module.toLowerCase(),
- fullName = ( this.module.name + ": " + this.testName ).toLowerCase();
-
- // Internally-generated tests are always valid
- if ( this.callback && this.callback.validTest ) {
- return true;
- }
-
- if ( config.testId.length > 0 && inArray( this.testId, config.testId ) < 0 ) {
- return false;
- }
-
- if ( module && ( !this.module.name || this.module.name.toLowerCase() !== module ) ) {
- return false;
- }
-
- if ( !filter ) {
- return true;
- }
-
- include = filter.charAt( 0 ) !== "!";
- if ( !include ) {
- filter = filter.slice( 1 );
- }
-
- // If the filter matches, we need to honour include
- if ( fullName.indexOf( filter ) !== -1 ) {
- return include;
- }
-
- // Otherwise, do the opposite
- return !include;
- }
-
-};
-
-// Resets the test setup. Useful for tests that modify the DOM.
-/*
-DEPRECATED: Use multiple tests instead of resetting inside a test.
-Use testStart or testDone for custom cleanup.
-This method will throw an error in 2.0, and will be removed in 2.1
-*/
-QUnit.reset = function() {
-
- // Return on non-browser environments
- // This is necessary to not break on node tests
- if ( typeof window === "undefined" ) {
- return;
- }
-
- var fixture = defined.document && document.getElementById &&
- document.getElementById( "qunit-fixture" );
-
- if ( fixture ) {
- fixture.innerHTML = config.fixture;
- }
-};
-
-QUnit.pushFailure = function() {
- if ( !QUnit.config.current ) {
- throw new Error( "pushFailure() assertion outside test context, in " +
- sourceFromStacktrace( 2 ) );
- }
-
- // Gets current test obj
- var currentTest = QUnit.config.current;
-
- return currentTest.pushFailure.apply( currentTest, arguments );
-};
-
-// Based on Java's String.hashCode, a simple but not
-// rigorously collision resistant hashing function
-function generateHash( module, testName ) {
- var hex,
- i = 0,
- hash = 0,
- str = module + "\x1C" + testName,
- len = str.length;
-
- for ( ; i < len; i++ ) {
- hash = ( ( hash << 5 ) - hash ) + str.charCodeAt( i );
- hash |= 0;
- }
-
- // Convert the possibly negative integer hash code into an 8 character hex string, which isn't
- // strictly necessary but increases user understanding that the id is a SHA-like hash
- hex = ( 0x100000000 + hash ).toString( 16 );
- if ( hex.length < 8 ) {
- hex = "0000000" + hex;
- }
-
- return hex.slice( -8 );
-}
-
-function Assert( testContext ) {
- this.test = testContext;
-}
-
-// Assert helpers
-QUnit.assert = Assert.prototype = {
-
- // Specify the number of expected assertions to guarantee that failed test
- // (no assertions are run at all) don't slip through.
- expect: function( asserts ) {
- if ( arguments.length === 1 ) {
- this.test.expected = asserts;
- } else {
- return this.test.expected;
- }
- },
-
- // Increment this Test's semaphore counter, then return a single-use function that
- // decrements that counter a maximum of once.
- async: function() {
- var test = this.test,
- popped = false;
-
- test.semaphore += 1;
- test.usedAsync = true;
- pauseProcessing();
-
- return function done() {
- if ( !popped ) {
- test.semaphore -= 1;
- popped = true;
- resumeProcessing();
- } else {
- test.pushFailure( "Called the callback returned from `assert.async` more than once",
- sourceFromStacktrace( 2 ) );
- }
- };
- },
-
- // Exports test.push() to the user API
- push: function( /* result, actual, expected, message */ ) {
- var assert = this,
- currentTest = ( assert instanceof Assert && assert.test ) || QUnit.config.current;
-
- // Backwards compatibility fix.
- // Allows the direct use of global exported assertions and QUnit.assert.*
- // Although, it's use is not recommended as it can leak assertions
- // to other tests from async tests, because we only get a reference to the current test,
- // not exactly the test where assertion were intended to be called.
- if ( !currentTest ) {
- throw new Error( "assertion outside test context, in " + sourceFromStacktrace( 2 ) );
- }
-
- if ( currentTest.usedAsync === true && currentTest.semaphore === 0 ) {
- currentTest.pushFailure( "Assertion after the final `assert.async` was resolved",
- sourceFromStacktrace( 2 ) );
-
- // Allow this assertion to continue running anyway...
- }
-
- if ( !( assert instanceof Assert ) ) {
- assert = currentTest.assert;
- }
- return assert.test.push.apply( assert.test, arguments );
- },
-
- /**
- * Asserts rough true-ish result.
- * @name ok
- * @function
- * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );
- */
- ok: function( result, message ) {
- message = message || ( result ? "okay" : "failed, expected argument to be truthy, was: " +
- QUnit.dump.parse( result ) );
- this.push( !!result, result, true, message );
- },
-
- /**
- * Assert that the first two arguments are equal, with an optional message.
- * Prints out both actual and expected values.
- * @name equal
- * @function
- * @example equal( format( "{0} bytes.", 2), "2 bytes.", "replaces {0} with next argument" );
- */
- equal: function( actual, expected, message ) {
- /*jshint eqeqeq:false */
- this.push( expected == actual, actual, expected, message );
- },
-
- /**
- * @name notEqual
- * @function
- */
- notEqual: function( actual, expected, message ) {
- /*jshint eqeqeq:false */
- this.push( expected != actual, actual, expected, message );
- },
-
- /**
- * @name propEqual
- * @function
- */
- propEqual: function( actual, expected, message ) {
- actual = objectValues( actual );
- expected = objectValues( expected );
- this.push( QUnit.equiv( actual, expected ), actual, expected, message );
- },
-
- /**
- * @name notPropEqual
- * @function
- */
- notPropEqual: function( actual, expected, message ) {
- actual = objectValues( actual );
- expected = objectValues( expected );
- this.push( !QUnit.equiv( actual, expected ), actual, expected, message );
- },
-
- /**
- * @name deepEqual
- * @function
- */
- deepEqual: function( actual, expected, message ) {
- this.push( QUnit.equiv( actual, expected ), actual, expected, message );
- },
-
- /**
- * @name notDeepEqual
- * @function
- */
- notDeepEqual: function( actual, expected, message ) {
- this.push( !QUnit.equiv( actual, expected ), actual, expected, message );
- },
-
- /**
- * @name strictEqual
- * @function
- */
- strictEqual: function( actual, expected, message ) {
- this.push( expected === actual, actual, expected, message );
- },
-
- /**
- * @name notStrictEqual
- * @function
- */
- notStrictEqual: function( actual, expected, message ) {
- this.push( expected !== actual, actual, expected, message );
- },
-
- "throws": function( block, expected, message ) {
- var actual, expectedType,
- expectedOutput = expected,
- ok = false;
-
- // 'expected' is optional unless doing string comparison
- if ( message == null && typeof expected === "string" ) {
- message = expected;
- expected = null;
- }
-
- this.test.ignoreGlobalErrors = true;
- try {
- block.call( this.test.testEnvironment );
- } catch (e) {
- actual = e;
- }
- this.test.ignoreGlobalErrors = false;
-
- if ( actual ) {
- expectedType = QUnit.objectType( expected );
-
- // we don't want to validate thrown error
- if ( !expected ) {
- ok = true;
- expectedOutput = null;
-
- // expected is a regexp
- } else if ( expectedType === "regexp" ) {
- ok = expected.test( errorString( actual ) );
-
- // expected is a string
- } else if ( expectedType === "string" ) {
- ok = expected === errorString( actual );
-
- // expected is a constructor, maybe an Error constructor
- } else if ( expectedType === "function" && actual instanceof expected ) {
- ok = true;
-
- // expected is an Error object
- } else if ( expectedType === "object" ) {
- ok = actual instanceof expected.constructor &&
- actual.name === expected.name &&
- actual.message === expected.message;
-
- // expected is a validation function which returns true if validation passed
- } else if ( expectedType === "function" && expected.call( {}, actual ) === true ) {
- expectedOutput = null;
- ok = true;
- }
-
- this.push( ok, actual, expectedOutput, message );
- } else {
- this.test.pushFailure( message, null, "No exception was thrown." );
- }
- }
-};
-
-// Provide an alternative to assert.throws(), for enviroments that consider throws a reserved word
-// Known to us are: Closure Compiler, Narwhal
-(function() {
- /*jshint sub:true */
- Assert.prototype.raises = Assert.prototype[ "throws" ];
-}());
-
-// Test for equality any JavaScript type.
-// Author: Philippe Rathé <prathe@gmail.com>
-QUnit.equiv = (function() {
-
- // Call the o related callback with the given arguments.
- function bindCallbacks( o, callbacks, args ) {
- var prop = QUnit.objectType( o );
- if ( prop ) {
- if ( QUnit.objectType( callbacks[ prop ] ) === "function" ) {
- return callbacks[ prop ].apply( callbacks, args );
- } else {
- return callbacks[ prop ]; // or undefined
- }
- }
- }
-
- // the real equiv function
- var innerEquiv,
-
- // stack to decide between skip/abort functions
- callers = [],
-
- // stack to avoiding loops from circular referencing
- parents = [],
- parentsB = [],
-
- getProto = Object.getPrototypeOf || function( obj ) {
- /* jshint camelcase: false, proto: true */
- return obj.__proto__;
- },
- callbacks = (function() {
-
- // for string, boolean, number and null
- function useStrictEquality( b, a ) {
-
- /*jshint eqeqeq:false */
- if ( b instanceof a.constructor || a instanceof b.constructor ) {
-
- // to catch short annotation VS 'new' annotation of a
- // declaration
- // e.g. var i = 1;
- // var j = new Number(1);
- return a == b;
- } else {
- return a === b;
- }
- }
-
- return {
- "string": useStrictEquality,
- "boolean": useStrictEquality,
- "number": useStrictEquality,
- "null": useStrictEquality,
- "undefined": useStrictEquality,
-
- "nan": function( b ) {
- return isNaN( b );
- },
-
- "date": function( b, a ) {
- return QUnit.objectType( b ) === "date" && a.valueOf() === b.valueOf();
- },
-
- "regexp": function( b, a ) {
- return QUnit.objectType( b ) === "regexp" &&
-
- // the regex itself
- a.source === b.source &&
-
- // and its modifiers
- a.global === b.global &&
-
- // (gmi) ...
- a.ignoreCase === b.ignoreCase &&
- a.multiline === b.multiline &&
- a.sticky === b.sticky;
- },
-
- // - skip when the property is a method of an instance (OOP)
- // - abort otherwise,
- // initial === would have catch identical references anyway
- "function": function() {
- var caller = callers[ callers.length - 1 ];
- return caller !== Object && typeof caller !== "undefined";
- },
-
- "array": function( b, a ) {
- var i, j, len, loop, aCircular, bCircular;
-
- // b could be an object literal here
- if ( QUnit.objectType( b ) !== "array" ) {
- return false;
- }
-
- len = a.length;
- if ( len !== b.length ) {
- // safe and faster
- return false;
- }
-
- // track reference to avoid circular references
- parents.push( a );
- parentsB.push( b );
- for ( i = 0; i < len; i++ ) {
- loop = false;
- for ( j = 0; j < parents.length; j++ ) {
- aCircular = parents[ j ] === a[ i ];
- bCircular = parentsB[ j ] === b[ i ];
- if ( aCircular || bCircular ) {
- if ( a[ i ] === b[ i ] || aCircular && bCircular ) {
- loop = true;
- } else {
- parents.pop();
- parentsB.pop();
- return false;
- }
- }
- }
- if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) {
- parents.pop();
- parentsB.pop();
- return false;
- }
- }
- parents.pop();
- parentsB.pop();
- return true;
- },
-
- "object": function( b, a ) {
-
- /*jshint forin:false */
- var i, j, loop, aCircular, bCircular,
- // Default to true
- eq = true,
- aProperties = [],
- bProperties = [];
-
- // comparing constructors is more strict than using
- // instanceof
- if ( a.constructor !== b.constructor ) {
-
- // Allow objects with no prototype to be equivalent to
- // objects with Object as their constructor.
- if ( !( ( getProto( a ) === null && getProto( b ) === Object.prototype ) ||
- ( getProto( b ) === null && getProto( a ) === Object.prototype ) ) ) {
- return false;
- }
- }
-
- // stack constructor before traversing properties
- callers.push( a.constructor );
-
- // track reference to avoid circular references
- parents.push( a );
- parentsB.push( b );
-
- // be strict: don't ensure hasOwnProperty and go deep
- for ( i in a ) {
- loop = false;
- for ( j = 0; j < parents.length; j++ ) {
- aCircular = parents[ j ] === a[ i ];
- bCircular = parentsB[ j ] === b[ i ];
- if ( aCircular || bCircular ) {
- if ( a[ i ] === b[ i ] || aCircular && bCircular ) {
- loop = true;
- } else {
- eq = false;
- break;
- }
- }
- }
- aProperties.push( i );
- if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) {
- eq = false;
- break;
- }
- }
-
- parents.pop();
- parentsB.pop();
- callers.pop(); // unstack, we are done
-
- for ( i in b ) {
- bProperties.push( i ); // collect b's properties
- }
-
- // Ensures identical properties name
- return eq && innerEquiv( aProperties.sort(), bProperties.sort() );
- }
- };
- }());
-
- innerEquiv = function() { // can take multiple arguments
- var args = [].slice.apply( arguments );
- if ( args.length < 2 ) {
- return true; // end transition
- }
-
- return ( (function( a, b ) {
- if ( a === b ) {
- return true; // catch the most you can
- } else if ( a === null || b === null || typeof a === "undefined" ||
- typeof b === "undefined" ||
- QUnit.objectType( a ) !== QUnit.objectType( b ) ) {
-
- // don't lose time with error prone cases
- return false;
- } else {
- return bindCallbacks( a, callbacks, [ b, a ] );
- }
-
- // apply transition with (1..n) arguments
- }( args[ 0 ], args[ 1 ] ) ) &&
- innerEquiv.apply( this, args.splice( 1, args.length - 1 ) ) );
- };
-
- return innerEquiv;
-}());
-
-// Based on jsDump by Ariel Flesler
-// http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html
-QUnit.dump = (function() {
- function quote( str ) {
- return "\"" + str.toString().replace( /"/g, "\\\"" ) + "\"";
- }
- function literal( o ) {
- return o + "";
- }
- function join( pre, arr, post ) {
- var s = dump.separator(),
- base = dump.indent(),
- inner = dump.indent( 1 );
- if ( arr.join ) {
- arr = arr.join( "," + s + inner );
- }
- if ( !arr ) {
- return pre + post;
- }
- return [ pre, inner + arr, base + post ].join( s );
- }
- function array( arr, stack ) {
- var i = arr.length,
- ret = new Array( i );
-
- if ( dump.maxDepth && dump.depth > dump.maxDepth ) {
- return "[object Array]";
- }
-
- this.up();
- while ( i-- ) {
- ret[ i ] = this.parse( arr[ i ], undefined, stack );
- }
- this.down();
- return join( "[", ret, "]" );
- }
-
- var reName = /^function (\w+)/,
- dump = {
-
- // objType is used mostly internally, you can fix a (custom) type in advance
- parse: function( obj, objType, stack ) {
- stack = stack || [];
- var res, parser, parserType,
- inStack = inArray( obj, stack );
-
- if ( inStack !== -1 ) {
- return "recursion(" + ( inStack - stack.length ) + ")";
- }
-
- objType = objType || this.typeOf( obj );
- parser = this.parsers[ objType ];
- parserType = typeof parser;
-
- if ( parserType === "function" ) {
- stack.push( obj );
- res = parser.call( this, obj, stack );
- stack.pop();
- return res;
- }
- return ( parserType === "string" ) ? parser : this.parsers.error;
- },
- typeOf: function( obj ) {
- var type;
- if ( obj === null ) {
- type = "null";
- } else if ( typeof obj === "undefined" ) {
- type = "undefined";
- } else if ( QUnit.is( "regexp", obj ) ) {
- type = "regexp";
- } else if ( QUnit.is( "date", obj ) ) {
- type = "date";
- } else if ( QUnit.is( "function", obj ) ) {
- type = "function";
- } else if ( obj.setInterval !== undefined &&
- obj.document !== undefined &&
- obj.nodeType === undefined ) {
- type = "window";
- } else if ( obj.nodeType === 9 ) {
- type = "document";
- } else if ( obj.nodeType ) {
- type = "node";
- } else if (
-
- // native arrays
- toString.call( obj ) === "[object Array]" ||
-
- // NodeList objects
- ( typeof obj.length === "number" && obj.item !== undefined &&
- ( obj.length ? obj.item( 0 ) === obj[ 0 ] : ( obj.item( 0 ) === null &&
- obj[ 0 ] === undefined ) ) )
- ) {
- type = "array";
- } else if ( obj.constructor === Error.prototype.constructor ) {
- type = "error";
- } else {
- type = typeof obj;
- }
- return type;
- },
- separator: function() {
- return this.multiline ? this.HTML ? "<br />" : "\n" : this.HTML ? " " : " ";
- },
- // extra can be a number, shortcut for increasing-calling-decreasing
- indent: function( extra ) {
- if ( !this.multiline ) {
- return "";
- }
- var chr = this.indentChar;
- if ( this.HTML ) {
- chr = chr.replace( /\t/g, " " ).replace( / /g, " " );
- }
- return new Array( this.depth + ( extra || 0 ) ).join( chr );
- },
- up: function( a ) {
- this.depth += a || 1;
- },
- down: function( a ) {
- this.depth -= a || 1;
- },
- setParser: function( name, parser ) {
- this.parsers[ name ] = parser;
- },
- // The next 3 are exposed so you can use them
- quote: quote,
- literal: literal,
- join: join,
- //
- depth: 1,
- maxDepth: 5,
-
- // This is the list of parsers, to modify them, use dump.setParser
- parsers: {
- window: "[Window]",
- document: "[Document]",
- error: function( error ) {
- return "Error(\"" + error.message + "\")";
- },
- unknown: "[Unknown]",
- "null": "null",
- "undefined": "undefined",
- "function": function( fn ) {
- var ret = "function",
-
- // functions never have name in IE
- name = "name" in fn ? fn.name : ( reName.exec( fn ) || [] )[ 1 ];
-
- if ( name ) {
- ret += " " + name;
- }
- ret += "( ";
-
- ret = [ ret, dump.parse( fn, "functionArgs" ), "){" ].join( "" );
- return join( ret, dump.parse( fn, "functionCode" ), "}" );
- },
- array: array,
- nodelist: array,
- "arguments": array,
- object: function( map, stack ) {
- var keys, key, val, i, nonEnumerableProperties,
- ret = [];
-
- if ( dump.maxDepth && dump.depth > dump.maxDepth ) {
- return "[object Object]";
- }
-
- dump.up();
- keys = [];
- for ( key in map ) {
- keys.push( key );
- }
-
- // Some properties are not always enumerable on Error objects.
- nonEnumerableProperties = [ "message", "name" ];
- for ( i in nonEnumerableProperties ) {
- key = nonEnumerableProperties[ i ];
- if ( key in map && !( key in keys ) ) {
- keys.push( key );
- }
- }
- keys.sort();
- for ( i = 0; i < keys.length; i++ ) {
- key = keys[ i ];
- val = map[ key ];
- ret.push( dump.parse( key, "key" ) + ": " +
- dump.parse( val, undefined, stack ) );
- }
- dump.down();
- return join( "{", ret, "}" );
- },
- node: function( node ) {
- var len, i, val,
- open = dump.HTML ? "<" : "<",
- close = dump.HTML ? ">" : ">",
- tag = node.nodeName.toLowerCase(),
- ret = open + tag,
- attrs = node.attributes;
-
- if ( attrs ) {
- for ( i = 0, len = attrs.length; i < len; i++ ) {
- val = attrs[ i ].nodeValue;
-
- // IE6 includes all attributes in .attributes, even ones not explicitly
- // set. Those have values like undefined, null, 0, false, "" or
- // "inherit".
- if ( val && val !== "inherit" ) {
- ret += " " + attrs[ i ].nodeName + "=" +
- dump.parse( val, "attribute" );
- }
- }
- }
- ret += close;
-
- // Show content of TextNode or CDATASection
- if ( node.nodeType === 3 || node.nodeType === 4 ) {
- ret += node.nodeValue;
- }
-
- return ret + open + "/" + tag + close;
- },
-
- // function calls it internally, it's the arguments part of the function
- functionArgs: function( fn ) {
- var args,
- l = fn.length;
-
- if ( !l ) {
- return "";
- }
-
- args = new Array( l );
- while ( l-- ) {
-
- // 97 is 'a'
- args[ l ] = String.fromCharCode( 97 + l );
- }
- return " " + args.join( ", " ) + " ";
- },
- // object calls it internally, the key part of an item in a map
- key: quote,
- // function calls it internally, it's the content of the function
- functionCode: "[code]",
- // node calls it internally, it's an html attribute value
- attribute: quote,
- string: quote,
- date: quote,
- regexp: literal,
- number: literal,
- "boolean": literal
- },
- // if true, entities are escaped ( <, >, \t, space and \n )
- HTML: false,
- // indentation unit
- indentChar: " ",
- // if true, items in a collection, are separated by a \n, else just a space.
- multiline: true
- };
-
- return dump;
-}());
-
-// back compat
-QUnit.jsDump = QUnit.dump;
-
-// For browser, export only select globals
-if ( typeof window !== "undefined" ) {
-
- // Deprecated
- // Extend assert methods to QUnit and Global scope through Backwards compatibility
- (function() {
- var i,
- assertions = Assert.prototype;
-
- function applyCurrent( current ) {
- return function() {
- var assert = new Assert( QUnit.config.current );
- current.apply( assert, arguments );
- };
- }
-
- for ( i in assertions ) {
- QUnit[ i ] = applyCurrent( assertions[ i ] );
- }
- })();
-
- (function() {
- var i, l,
- keys = [
- "test",
- "module",
- "expect",
- "asyncTest",
- "start",
- "stop",
- "ok",
- "equal",
- "notEqual",
- "propEqual",
- "notPropEqual",
- "deepEqual",
- "notDeepEqual",
- "strictEqual",
- "notStrictEqual",
- "throws"
- ];
-
- for ( i = 0, l = keys.length; i < l; i++ ) {
- window[ keys[ i ] ] = QUnit[ keys[ i ] ];
- }
- })();
-
- window.QUnit = QUnit;
-}
-
-// For nodejs
-if ( typeof module !== "undefined" && module.exports ) {
- module.exports = QUnit;
-}
-
-// For CommonJS with exports, but without module.exports, like Rhino
-if ( typeof exports !== "undefined" ) {
- exports.QUnit = QUnit;
-}
-
-// Get a reference to the global object, like window in browsers
-}( (function() {
- return this;
-})() ));
-
-/*istanbul ignore next */
-// jscs:disable maximumLineLength
-/*
- * Javascript Diff Algorithm
- * By John Resig (http://ejohn.org/)
- * Modified by Chu Alan "sprite"
- *
- * Released under the MIT license.
- *
- * More Info:
- * http://ejohn.org/projects/javascript-diff-algorithm/
- *
- * Usage: QUnit.diff(expected, actual)
- *
- * QUnit.diff( "the quick brown fox jumped over", "the quick fox jumps over" ) == "the quick <del>brown </del> fox <del>jumped </del><ins>jumps </ins> over"
- */
-QUnit.diff = (function() {
- var hasOwn = Object.prototype.hasOwnProperty;
-
- /*jshint eqeqeq:false, eqnull:true */
- function diff( o, n ) {
- var i,
- ns = {},
- os = {};
-
- for ( i = 0; i < n.length; i++ ) {
- if ( !hasOwn.call( ns, n[ i ] ) ) {
- ns[ n[ i ] ] = {
- rows: [],
- o: null
- };
- }
- ns[ n[ i ] ].rows.push( i );
- }
-
- for ( i = 0; i < o.length; i++ ) {
- if ( !hasOwn.call( os, o[ i ] ) ) {
- os[ o[ i ] ] = {
- rows: [],
- n: null
- };
- }
- os[ o[ i ] ].rows.push( i );
- }
-
- for ( i in ns ) {
- if ( hasOwn.call( ns, i ) ) {
- if ( ns[ i ].rows.length === 1 && hasOwn.call( os, i ) && os[ i ].rows.length === 1 ) {
- n[ ns[ i ].rows[ 0 ] ] = {
- text: n[ ns[ i ].rows[ 0 ] ],
- row: os[ i ].rows[ 0 ]
- };
- o[ os[ i ].rows[ 0 ] ] = {
- text: o[ os[ i ].rows[ 0 ] ],
- row: ns[ i ].rows[ 0 ]
- };
- }
- }
- }
-
- for ( i = 0; i < n.length - 1; i++ ) {
- if ( n[ i ].text != null && n[ i + 1 ].text == null && n[ i ].row + 1 < o.length && o[ n[ i ].row + 1 ].text == null &&
- n[ i + 1 ] == o[ n[ i ].row + 1 ] ) {
-
- n[ i + 1 ] = {
- text: n[ i + 1 ],
- row: n[ i ].row + 1
- };
- o[ n[ i ].row + 1 ] = {
- text: o[ n[ i ].row + 1 ],
- row: i + 1
- };
- }
- }
-
- for ( i = n.length - 1; i > 0; i-- ) {
- if ( n[ i ].text != null && n[ i - 1 ].text == null && n[ i ].row > 0 && o[ n[ i ].row - 1 ].text == null &&
- n[ i - 1 ] == o[ n[ i ].row - 1 ] ) {
-
- n[ i - 1 ] = {
- text: n[ i - 1 ],
- row: n[ i ].row - 1
- };
- o[ n[ i ].row - 1 ] = {
- text: o[ n[ i ].row - 1 ],
- row: i - 1
- };
- }
- }
-
- return {
- o: o,
- n: n
- };
- }
-
- return function( o, n ) {
- o = o.replace( /\s+$/, "" );
- n = n.replace( /\s+$/, "" );
-
- var i, pre,
- str = "",
- out = diff( o === "" ? [] : o.split( /\s+/ ), n === "" ? [] : n.split( /\s+/ ) ),
- oSpace = o.match( /\s+/g ),
- nSpace = n.match( /\s+/g );
-
- if ( oSpace == null ) {
- oSpace = [ " " ];
- } else {
- oSpace.push( " " );
- }
-
- if ( nSpace == null ) {
- nSpace = [ " " ];
- } else {
- nSpace.push( " " );
- }
-
- if ( out.n.length === 0 ) {
- for ( i = 0; i < out.o.length; i++ ) {
- str += "<del>" + out.o[ i ] + oSpace[ i ] + "</del>";
- }
- } else {
- if ( out.n[ 0 ].text == null ) {
- for ( n = 0; n < out.o.length && out.o[ n ].text == null; n++ ) {
- str += "<del>" + out.o[ n ] + oSpace[ n ] + "</del>";
- }
- }
-
- for ( i = 0; i < out.n.length; i++ ) {
- if ( out.n[ i ].text == null ) {
- str += "<ins>" + out.n[ i ] + nSpace[ i ] + "</ins>";
- } else {
-
- // `pre` initialized at top of scope
- pre = "";
-
- for ( n = out.n[ i ].row + 1; n < out.o.length && out.o[ n ].text == null; n++ ) {
- pre += "<del>" + out.o[ n ] + oSpace[ n ] + "</del>";
- }
- str += " " + out.n[ i ].text + nSpace[ i ] + pre;
- }
- }
- }
-
- return str;
- };
-}());
-// jscs:enable
-
-(function() {
-
-// Deprecated QUnit.init - Ref #530
-// Re-initialize the configuration options
-QUnit.init = function() {
- var tests, banner, result, qunit,
- config = QUnit.config;
-
- config.stats = { all: 0, bad: 0 };
- config.moduleStats = { all: 0, bad: 0 };
- config.started = 0;
- config.updateRate = 1000;
- config.blocking = false;
- config.autostart = true;
- config.autorun = false;
- config.filter = "";
- config.queue = [];
-
- // Return on non-browser environments
- // This is necessary to not break on node tests
- if ( typeof window === "undefined" ) {
- return;
- }
-
- qunit = id( "qunit" );
- if ( qunit ) {
- qunit.innerHTML =
- "<h1 id='qunit-header'>" + escapeText( document.title ) + "</h1>" +
- "<h2 id='qunit-banner'></h2>" +
- "<div id='qunit-testrunner-toolbar'></div>" +
- "<h2 id='qunit-userAgent'></h2>" +
- "<ol id='qunit-tests'></ol>";
- }
-
- tests = id( "qunit-tests" );
- banner = id( "qunit-banner" );
- result = id( "qunit-testresult" );
-
- if ( tests ) {
- tests.innerHTML = "";
- }
-
- if ( banner ) {
- banner.className = "";
- }
-
- if ( result ) {
- result.parentNode.removeChild( result );
- }
-
- if ( tests ) {
- result = document.createElement( "p" );
- result.id = "qunit-testresult";
- result.className = "result";
- tests.parentNode.insertBefore( result, tests );
- result.innerHTML = "Running...<br /> ";
- }
-};
-
-// Don't load the HTML Reporter on non-Browser environments
-if ( typeof window === "undefined" ) {
- return;
-}
-
-var config = QUnit.config,
- hasOwn = Object.prototype.hasOwnProperty,
- defined = {
- document: window.document !== undefined,
- sessionStorage: (function() {
- var x = "qunit-test-string";
- try {
- sessionStorage.setItem( x, x );
- sessionStorage.removeItem( x );
- return true;
- } catch ( e ) {
- return false;
- }
- }())
- },
- modulesList = [];
-
-/**
-* Escape text for attribute or text content.
-*/
-function escapeText( s ) {
- if ( !s ) {
- return "";
- }
- s = s + "";
-
- // Both single quotes and double quotes (for attributes)
- return s.replace( /['"<>&]/g, function( s ) {
- switch ( s ) {
- case "'":
- return "'";
- case "\"":
- return """;
- case "<":
- return "<";
- case ">":
- return ">";
- case "&":
- return "&";
- }
- });
-}
-
-/**
- * @param {HTMLElement} elem
- * @param {string} type
- * @param {Function} fn
- */
-function addEvent( elem, type, fn ) {
- if ( elem.addEventListener ) {
-
- // Standards-based browsers
- elem.addEventListener( type, fn, false );
- } else if ( elem.attachEvent ) {
-
- // support: IE <9
- elem.attachEvent( "on" + type, fn );
- }
-}
-
-/**
- * @param {Array|NodeList} elems
- * @param {string} type
- * @param {Function} fn
- */
-function addEvents( elems, type, fn ) {
- var i = elems.length;
- while ( i-- ) {
- addEvent( elems[ i ], type, fn );
- }
-}
-
-function hasClass( elem, name ) {
- return ( " " + elem.className + " " ).indexOf( " " + name + " " ) >= 0;
-}
-
-function addClass( elem, name ) {
- if ( !hasClass( elem, name ) ) {
- elem.className += ( elem.className ? " " : "" ) + name;
- }
-}
-
-function toggleClass( elem, name ) {
- if ( hasClass( elem, name ) ) {
- removeClass( elem, name );
- } else {
- addClass( elem, name );
- }
-}
-
-function removeClass( elem, name ) {
- var set = " " + elem.className + " ";
-
- // Class name may appear multiple times
- while ( set.indexOf( " " + name + " " ) >= 0 ) {
- set = set.replace( " " + name + " ", " " );
- }
-
- // trim for prettiness
- elem.className = typeof set.trim === "function" ? set.trim() : set.replace( /^\s+|\s+$/g, "" );
-}
-
-function id( name ) {
- return defined.document && document.getElementById && document.getElementById( name );
-}
-
-function getUrlConfigHtml() {
- var i, j, val,
- escaped, escapedTooltip,
- selection = false,
- len = config.urlConfig.length,
- urlConfigHtml = "";
-
- for ( i = 0; i < len; i++ ) {
- val = config.urlConfig[ i ];
- if ( typeof val === "string" ) {
- val = {
- id: val,
- label: val
- };
- }
-
- escaped = escapeText( val.id );
- escapedTooltip = escapeText( val.tooltip );
-
- config[ val.id ] = QUnit.urlParams[ val.id ];
- if ( !val.value || typeof val.value === "string" ) {
- urlConfigHtml += "<input id='qunit-urlconfig-" + escaped +
- "' name='" + escaped + "' type='checkbox'" +
- ( val.value ? " value='" + escapeText( val.value ) + "'" : "" ) +
- ( config[ val.id ] ? " checked='checked'" : "" ) +
- " title='" + escapedTooltip + "' /><label for='qunit-urlconfig-" + escaped +
- "' title='" + escapedTooltip + "'>" + val.label + "</label>";
- } else {
- urlConfigHtml += "<label for='qunit-urlconfig-" + escaped +
- "' title='" + escapedTooltip + "'>" + val.label +
- ": </label><select id='qunit-urlconfig-" + escaped +
- "' name='" + escaped + "' title='" + escapedTooltip + "'><option></option>";
-
- if ( QUnit.is( "array", val.value ) ) {
- for ( j = 0; j < val.value.length; j++ ) {
- escaped = escapeText( val.value[ j ] );
- urlConfigHtml += "<option value='" + escaped + "'" +
- ( config[ val.id ] === val.value[ j ] ?
- ( selection = true ) && " selected='selected'" : "" ) +
- ">" + escaped + "</option>";
- }
- } else {
- for ( j in val.value ) {
- if ( hasOwn.call( val.value, j ) ) {
- urlConfigHtml += "<option value='" + escapeText( j ) + "'" +
- ( config[ val.id ] === j ?
- ( selection = true ) && " selected='selected'" : "" ) +
- ">" + escapeText( val.value[ j ] ) + "</option>";
- }
- }
- }
- if ( config[ val.id ] && !selection ) {
- escaped = escapeText( config[ val.id ] );
- urlConfigHtml += "<option value='" + escaped +
- "' selected='selected' disabled='disabled'>" + escaped + "</option>";
- }
- urlConfigHtml += "</select>";
- }
- }
-
- return urlConfigHtml;
-}
-
-// Handle "click" events on toolbar checkboxes and "change" for select menus.
-// Updates the URL with the new state of `config.urlConfig` values.
-function toolbarChanged() {
- var updatedUrl, value,
- field = this,
- params = {};
-
- // Detect if field is a select menu or a checkbox
- if ( "selectedIndex" in field ) {
- value = field.options[ field.selectedIndex ].value || undefined;
- } else {
- value = field.checked ? ( field.defaultValue || true ) : undefined;
- }
-
- params[ field.name ] = value;
- updatedUrl = QUnit.url( params );
-
- if ( "hidepassed" === field.name && "replaceState" in window.history ) {
- config[ field.name ] = value || false;
- if ( value ) {
- addClass( id( "qunit-tests" ), "hidepass" );
- } else {
- removeClass( id( "qunit-tests" ), "hidepass" );
- }
-
- // It is not necessary to refresh the whole page
- window.history.replaceState( null, "", updatedUrl );
- } else {
- window.location = updatedUrl;
- }
-}
-
-function toolbarUrlConfigContainer() {
- var urlConfigContainer = document.createElement( "span" );
-
- urlConfigContainer.innerHTML = getUrlConfigHtml();
-
- // For oldIE support:
- // * Add handlers to the individual elements instead of the container
- // * Use "click" instead of "change" for checkboxes
- addEvents( urlConfigContainer.getElementsByTagName( "input" ), "click", toolbarChanged );
- addEvents( urlConfigContainer.getElementsByTagName( "select" ), "change", toolbarChanged );
-
- return urlConfigContainer;
-}
-
-function toolbarModuleFilterHtml() {
- var i,
- moduleFilterHtml = "";
-
- if ( !modulesList.length ) {
- return false;
- }
-
- modulesList.sort(function( a, b ) {
- return a.localeCompare( b );
- });
-
- moduleFilterHtml += "<label for='qunit-modulefilter'>Module: </label>" +
- "<select id='qunit-modulefilter' name='modulefilter'><option value='' " +
- ( QUnit.urlParams.module === undefined ? "selected='selected'" : "" ) +
- ">< All Modules ></option>";
-
- for ( i = 0; i < modulesList.length; i++ ) {
- moduleFilterHtml += "<option value='" +
- escapeText( encodeURIComponent( modulesList[ i ] ) ) + "' " +
- ( QUnit.urlParams.module === modulesList[ i ] ? "selected='selected'" : "" ) +
- ">" + escapeText( modulesList[ i ] ) + "</option>";
- }
- moduleFilterHtml += "</select>";
-
- return moduleFilterHtml;
-}
-
-function toolbarModuleFilter() {
- var toolbar = id( "qunit-testrunner-toolbar" ),
- moduleFilter = document.createElement( "span" ),
- moduleFilterHtml = toolbarModuleFilterHtml();
-
- if ( !moduleFilterHtml ) {
- return false;
- }
-
- moduleFilter.setAttribute( "id", "qunit-modulefilter-container" );
- moduleFilter.innerHTML = moduleFilterHtml;
-
- addEvent( moduleFilter.lastChild, "change", function() {
- var selectBox = moduleFilter.getElementsByTagName( "select" )[ 0 ],
- selection = decodeURIComponent( selectBox.options[ selectBox.selectedIndex ].value );
-
- window.location = QUnit.url({
- module: ( selection === "" ) ? undefined : selection,
-
- // Remove any existing filters
- filter: undefined,
- testId: undefined
- });
- });
-
- toolbar.appendChild( moduleFilter );
-}
-
-function appendToolbar() {
- var toolbar = id( "qunit-testrunner-toolbar" );
-
- if ( toolbar ) {
- toolbar.appendChild( toolbarUrlConfigContainer() );
- }
-}
-
-function appendBanner() {
- var banner = id( "qunit-banner" );
-
- if ( banner ) {
- banner.className = "";
- banner.innerHTML = "<a href='" +
- QUnit.url({ filter: undefined, module: undefined, testId: undefined }) +
- "'>" + banner.innerHTML + "</a> ";
- }
-}
-
-function appendTestResults() {
- var tests = id( "qunit-tests" ),
- result = id( "qunit-testresult" );
-
- if ( result ) {
- result.parentNode.removeChild( result );
- }
-
- if ( tests ) {
- tests.innerHTML = "";
- result = document.createElement( "p" );
- result.id = "qunit-testresult";
- result.className = "result";
- tests.parentNode.insertBefore( result, tests );
- result.innerHTML = "Running...<br /> ";
- }
-}
-
-function storeFixture() {
- var fixture = id( "qunit-fixture" );
- if ( fixture ) {
- config.fixture = fixture.innerHTML;
- }
-}
-
-function appendUserAgent() {
- var userAgent = id( "qunit-userAgent" );
- if ( userAgent ) {
- userAgent.innerHTML = navigator.userAgent;
- }
-}
-
-function appendTestsList( modules ) {
- var i, l, x, z, test, moduleObj;
-
- for ( i = 0, l = modules.length; i < l; i++ ) {
- moduleObj = modules[ i ];
-
- if ( moduleObj.name ) {
- modulesList.push( moduleObj.name );
- }
-
- for ( x = 0, z = moduleObj.tests.length; x < z; x++ ) {
- test = moduleObj.tests[ x ];
-
- appendTest( test.name, test.testId, moduleObj.name );
- }
- }
-}
-
-function appendTest( name, testId, moduleName ) {
- var title, rerunTrigger, testBlock, assertList,
- tests = id( "qunit-tests" );
-
- if ( !tests ) {
- return;
- }
-
- title = document.createElement( "strong" );
- title.innerHTML = getNameHtml( name, moduleName );
-
- rerunTrigger = document.createElement( "a" );
- rerunTrigger.innerHTML = "Rerun";
- rerunTrigger.href = QUnit.url({ testId: testId });
-
- testBlock = document.createElement( "li" );
- testBlock.appendChild( title );
- testBlock.appendChild( rerunTrigger );
- testBlock.id = "qunit-test-output-" + testId;
-
- assertList = document.createElement( "ol" );
- assertList.className = "qunit-assert-list";
-
- testBlock.appendChild( assertList );
-
- tests.appendChild( testBlock );
-}
-
-// HTML Reporter initialization and load
-QUnit.begin(function( details ) {
- var qunit = id( "qunit" );
-
- // Fixture is the only one necessary to run without the #qunit element
- storeFixture();
-
- if ( !qunit ) {
- return;
- }
-
- qunit.innerHTML =
- "<h1 id='qunit-header'>" + escapeText( document.title ) + "</h1>" +
- "<h2 id='qunit-banner'></h2>" +
- "<div id='qunit-testrunner-toolbar'></div>" +
- "<h2 id='qunit-userAgent'></h2>" +
- "<ol id='qunit-tests'></ol>";
-
- appendBanner();
- appendTestResults();
- appendUserAgent();
- appendToolbar();
- appendTestsList( details.modules );
- toolbarModuleFilter();
-
- if ( config.hidepassed ) {
- addClass( qunit.lastChild, "hidepass" );
- }
-});
-
-QUnit.done(function( details ) {
- var i, key,
- banner = id( "qunit-banner" ),
- tests = id( "qunit-tests" ),
- html = [
- "Tests completed in ",
- details.runtime,
- " milliseconds.<br />",
- "<span class='passed'>",
- details.passed,
- "</span> assertions of <span class='total'>",
- details.total,
- "</span> passed, <span class='failed'>",
- details.failed,
- "</span> failed."
- ].join( "" );
-
- if ( banner ) {
- banner.className = details.failed ? "qunit-fail" : "qunit-pass";
- }
-
- if ( tests ) {
- id( "qunit-testresult" ).innerHTML = html;
- }
-
- if ( config.altertitle && defined.document && document.title ) {
-
- // show ✖ for good, ✔ for bad suite result in title
- // use escape sequences in case file gets loaded with non-utf-8-charset
- document.title = [
- ( details.failed ? "\u2716" : "\u2714" ),
- document.title.replace( /^[\u2714\u2716] /i, "" )
- ].join( " " );
- }
-
- // clear own sessionStorage items if all tests passed
- if ( config.reorder && defined.sessionStorage && details.failed === 0 ) {
- for ( i = 0; i < sessionStorage.length; i++ ) {
- key = sessionStorage.key( i++ );
- if ( key.indexOf( "qunit-test-" ) === 0 ) {
- sessionStorage.removeItem( key );
- }
- }
- }
-
- // scroll back to top to show results
- if ( config.scrolltop && window.scrollTo ) {
- window.scrollTo( 0, 0 );
- }
-});
-
-function getNameHtml( name, module ) {
- var nameHtml = "";
-
- if ( module ) {
- nameHtml = "<span class='module-name'>" + escapeText( module ) + "</span>: ";
- }
-
- nameHtml += "<span class='test-name'>" + escapeText( name ) + "</span>";
-
- return nameHtml;
-}
-
-QUnit.testStart(function( details ) {
- var running, testBlock;
-
- testBlock = id( "qunit-test-output-" + details.testId );
- if ( testBlock ) {
- testBlock.className = "running";
- } else {
-
- // Report later registered tests
- appendTest( details.name, details.testId, details.module );
- }
-
- running = id( "qunit-testresult" );
- if ( running ) {
- running.innerHTML = "Running: <br />" + getNameHtml( details.name, details.module );
- }
-
-});
-
-QUnit.log(function( details ) {
- var assertList, assertLi,
- message, expected, actual,
- testItem = id( "qunit-test-output-" + details.testId );
-
- if ( !testItem ) {
- return;
- }
-
- message = escapeText( details.message ) || ( details.result ? "okay" : "failed" );
- message = "<span class='test-message'>" + message + "</span>";
- message += "<span class='runtime'>@ " + details.runtime + " ms</span>";
-
- // pushFailure doesn't provide details.expected
- // when it calls, it's implicit to also not show expected and diff stuff
- // Also, we need to check details.expected existence, as it can exist and be undefined
- if ( !details.result && hasOwn.call( details, "expected" ) ) {
- expected = escapeText( QUnit.dump.parse( details.expected ) );
- actual = escapeText( QUnit.dump.parse( details.actual ) );
- message += "<table><tr class='test-expected'><th>Expected: </th><td><pre>" +
- expected +
- "</pre></td></tr>";
-
- if ( actual !== expected ) {
- message += "<tr class='test-actual'><th>Result: </th><td><pre>" +
- actual + "</pre></td></tr>" +
- "<tr class='test-diff'><th>Diff: </th><td><pre>" +
- QUnit.diff( expected, actual ) + "</pre></td></tr>";
- }
-
- if ( details.source ) {
- message += "<tr class='test-source'><th>Source: </th><td><pre>" +
- escapeText( details.source ) + "</pre></td></tr>";
- }
-
- message += "</table>";
-
- // this occours when pushFailure is set and we have an extracted stack trace
- } else if ( !details.result && details.source ) {
- message += "<table>" +
- "<tr class='test-source'><th>Source: </th><td><pre>" +
- escapeText( details.source ) + "</pre></td></tr>" +
- "</table>";
- }
-
- assertList = testItem.getElementsByTagName( "ol" )[ 0 ];
-
- assertLi = document.createElement( "li" );
- assertLi.className = details.result ? "pass" : "fail";
- assertLi.innerHTML = message;
- assertList.appendChild( assertLi );
-});
-
-QUnit.testDone(function( details ) {
- var testTitle, time, testItem, assertList,
- good, bad, testCounts, skipped,
- tests = id( "qunit-tests" );
-
- if ( !tests ) {
- return;
- }
-
- testItem = id( "qunit-test-output-" + details.testId );
-
- assertList = testItem.getElementsByTagName( "ol" )[ 0 ];
-
- good = details.passed;
- bad = details.failed;
-
- // store result when possible
- if ( config.reorder && defined.sessionStorage ) {
- if ( bad ) {
- sessionStorage.setItem( "qunit-test-" + details.module + "-" + details.name, bad );
- } else {
- sessionStorage.removeItem( "qunit-test-" + details.module + "-" + details.name );
- }
- }
-
- if ( bad === 0 ) {
- addClass( assertList, "qunit-collapsed" );
- }
-
- // testItem.firstChild is the test name
- testTitle = testItem.firstChild;
-
- testCounts = bad ?
- "<b class='failed'>" + bad + "</b>, " + "<b class='passed'>" + good + "</b>, " :
- "";
-
- testTitle.innerHTML += " <b class='counts'>(" + testCounts +
- details.assertions.length + ")</b>";
-
- if ( details.skipped ) {
- addClass( testItem, "skipped" );
- skipped = document.createElement( "em" );
- skipped.className = "qunit-skipped-label";
- skipped.innerHTML = "skipped";
- testItem.insertBefore( skipped, testTitle );
- } else {
- addEvent( testTitle, "click", function() {
- toggleClass( assertList, "qunit-collapsed" );
- });
-
- testItem.className = bad ? "fail" : "pass";
-
- time = document.createElement( "span" );
- time.className = "runtime";
- time.innerHTML = details.runtime + " ms";
- testItem.insertBefore( time, assertList );
- }
-});
-
-if ( !defined.document || document.readyState === "complete" ) {
- config.pageLoaded = true;
- config.autorun = true;
-}
-
-if ( defined.document ) {
- addEvent( window, "load", QUnit.load );
-}
-
-})();
--- /dev/null
+/*!
+ * QUnit 1.17.1
+ * http://qunitjs.com/
+ *
+ * Copyright jQuery Foundation and other contributors
+ * Released under the MIT license
+ * http://jquery.org/license
+ *
+ * Date: 2015-01-20T19:39Z
+ */
+
+/** Font Family and Sizes */
+
+#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult {
+ font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
+}
+
+#qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
+#qunit-tests { font-size: smaller; }
+
+
+/** Resets */
+
+#qunit-tests, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter {
+ margin: 0;
+ padding: 0;
+}
+
+
+/** Header */
+
+#qunit-header {
+ padding: 0.5em 0 0.5em 1em;
+
+ color: #8699A4;
+ background-color: #0D3349;
+
+ font-size: 1.5em;
+ line-height: 1em;
+ font-weight: 400;
+
+ border-radius: 5px 5px 0 0;
+}
+
+#qunit-header a {
+ text-decoration: none;
+ color: #C2CCD1;
+}
+
+#qunit-header a:hover,
+#qunit-header a:focus {
+ color: #FFF;
+}
+
+#qunit-testrunner-toolbar label {
+ display: inline-block;
+ padding: 0 0.5em 0 0.1em;
+}
+
+#qunit-banner {
+ height: 5px;
+}
+
+#qunit-testrunner-toolbar {
+ padding: 0.5em 1em 0.5em 1em;
+ color: #5E740B;
+ background-color: #EEE;
+ overflow: hidden;
+}
+
+#qunit-userAgent {
+ padding: 0.5em 1em 0.5em 1em;
+ background-color: #2B81AF;
+ color: #FFF;
+ text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
+}
+
+#qunit-modulefilter-container {
+ float: right;
+ padding: 0.2em;
+}
+
+.qunit-url-config {
+ display: inline-block;
+ padding: 0.1em;
+}
+
+.qunit-filter {
+ display: block;
+ float: right;
+ margin-left: 1em;
+}
+
+/** Tests: Pass/Fail */
+
+#qunit-tests {
+ list-style-position: inside;
+}
+
+#qunit-tests li {
+ padding: 0.4em 1em 0.4em 1em;
+ border-bottom: 1px solid #FFF;
+ list-style-position: inside;
+}
+
+#qunit-tests > li {
+ display: none;
+}
+
+#qunit-tests li.running,
+#qunit-tests li.pass,
+#qunit-tests li.fail,
+#qunit-tests li.skipped {
+ display: list-item;
+}
+
+#qunit-tests.hidepass li.running,
+#qunit-tests.hidepass li.pass {
+ display: none;
+}
+
+#qunit-tests li strong {
+ cursor: pointer;
+}
+
+#qunit-tests li.skipped strong {
+ cursor: default;
+}
+
+#qunit-tests li a {
+ padding: 0.5em;
+ color: #C2CCD1;
+ text-decoration: none;
+}
+#qunit-tests li a:hover,
+#qunit-tests li a:focus {
+ color: #000;
+}
+
+#qunit-tests li .runtime {
+ float: right;
+ font-size: smaller;
+}
+
+.qunit-assert-list {
+ margin-top: 0.5em;
+ padding: 0.5em;
+
+ background-color: #FFF;
+
+ border-radius: 5px;
+}
+
+.qunit-collapsed {
+ display: none;
+}
+
+#qunit-tests table {
+ border-collapse: collapse;
+ margin-top: 0.2em;
+}
+
+#qunit-tests th {
+ text-align: right;
+ vertical-align: top;
+ padding: 0 0.5em 0 0;
+}
+
+#qunit-tests td {
+ vertical-align: top;
+}
+
+#qunit-tests pre {
+ margin: 0;
+ white-space: pre-wrap;
+ word-wrap: break-word;
+}
+
+#qunit-tests del {
+ background-color: #E0F2BE;
+ color: #374E0C;
+ text-decoration: none;
+}
+
+#qunit-tests ins {
+ background-color: #FFCACA;
+ color: #500;
+ text-decoration: none;
+}
+
+/*** Test Counts */
+
+#qunit-tests b.counts { color: #000; }
+#qunit-tests b.passed { color: #5E740B; }
+#qunit-tests b.failed { color: #710909; }
+
+#qunit-tests li li {
+ padding: 5px;
+ background-color: #FFF;
+ border-bottom: none;
+ list-style-position: inside;
+}
+
+/*** Passing Styles */
+
+#qunit-tests li li.pass {
+ color: #3C510C;
+ background-color: #FFF;
+ border-left: 10px solid #C6E746;
+}
+
+#qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; }
+#qunit-tests .pass .test-name { color: #366097; }
+
+#qunit-tests .pass .test-actual,
+#qunit-tests .pass .test-expected { color: #999; }
+
+#qunit-banner.qunit-pass { background-color: #C6E746; }
+
+/*** Failing Styles */
+
+#qunit-tests li li.fail {
+ color: #710909;
+ background-color: #FFF;
+ border-left: 10px solid #EE5757;
+ white-space: pre;
+}
+
+#qunit-tests > li:last-child {
+ border-radius: 0 0 5px 5px;
+}
+
+#qunit-tests .fail { color: #000; background-color: #EE5757; }
+#qunit-tests .fail .test-name,
+#qunit-tests .fail .module-name { color: #000; }
+
+#qunit-tests .fail .test-actual { color: #EE5757; }
+#qunit-tests .fail .test-expected { color: #008000; }
+
+#qunit-banner.qunit-fail { background-color: #EE5757; }
+
+/*** Skipped tests */
+
+#qunit-tests .skipped {
+ background-color: #EBECE9;
+}
+
+#qunit-tests .qunit-skipped-label {
+ background-color: #F4FF77;
+ display: inline-block;
+ font-style: normal;
+ color: #366097;
+ line-height: 1.8em;
+ padding: 0 0.5em;
+ margin: -0.4em 0.4em -0.4em 0;
+}
+
+/** Result */
+
+#qunit-testresult {
+ padding: 0.5em 1em 0.5em 1em;
+
+ color: #2B81AF;
+ background-color: #D2E0E6;
+
+ border-bottom: 1px solid #FFF;
+}
+#qunit-testresult .module-name {
+ font-weight: 700;
+}
+
+/** Fixture */
+
+#qunit-fixture {
+ position: absolute;
+ top: -10000px;
+ left: -10000px;
+ width: 1000px;
+ height: 1000px;
+}
--- /dev/null
+/*!
+ * QUnit 1.17.1
+ * http://qunitjs.com/
+ *
+ * Copyright jQuery Foundation and other contributors
+ * Released under the MIT license
+ * http://jquery.org/license
+ *
+ * Date: 2015-01-20T19:39Z
+ */
+
+(function( window ) {
+
+var QUnit,
+ config,
+ onErrorFnPrev,
+ loggingCallbacks = {},
+ fileName = ( sourceFromStacktrace( 0 ) || "" ).replace( /(:\d+)+\)?/, "" ).replace( /.+\//, "" ),
+ toString = Object.prototype.toString,
+ hasOwn = Object.prototype.hasOwnProperty,
+ // Keep a local reference to Date (GH-283)
+ Date = window.Date,
+ now = Date.now || function() {
+ return new Date().getTime();
+ },
+ globalStartCalled = false,
+ runStarted = false,
+ setTimeout = window.setTimeout,
+ clearTimeout = window.clearTimeout,
+ defined = {
+ document: window.document !== undefined,
+ setTimeout: window.setTimeout !== undefined,
+ sessionStorage: (function() {
+ var x = "qunit-test-string";
+ try {
+ sessionStorage.setItem( x, x );
+ sessionStorage.removeItem( x );
+ return true;
+ } catch ( e ) {
+ return false;
+ }
+ }())
+ },
+ /**
+ * Provides a normalized error string, correcting an issue
+ * with IE 7 (and prior) where Error.prototype.toString is
+ * not properly implemented
+ *
+ * Based on http://es5.github.com/#x15.11.4.4
+ *
+ * @param {String|Error} error
+ * @return {String} error message
+ */
+ errorString = function( error ) {
+ var name, message,
+ errorString = error.toString();
+ if ( errorString.substring( 0, 7 ) === "[object" ) {
+ name = error.name ? error.name.toString() : "Error";
+ message = error.message ? error.message.toString() : "";
+ if ( name && message ) {
+ return name + ": " + message;
+ } else if ( name ) {
+ return name;
+ } else if ( message ) {
+ return message;
+ } else {
+ return "Error";
+ }
+ } else {
+ return errorString;
+ }
+ },
+ /**
+ * Makes a clone of an object using only Array or Object as base,
+ * and copies over the own enumerable properties.
+ *
+ * @param {Object} obj
+ * @return {Object} New object with only the own properties (recursively).
+ */
+ objectValues = function( obj ) {
+ var key, val,
+ vals = QUnit.is( "array", obj ) ? [] : {};
+ for ( key in obj ) {
+ if ( hasOwn.call( obj, key ) ) {
+ val = obj[ key ];
+ vals[ key ] = val === Object( val ) ? objectValues( val ) : val;
+ }
+ }
+ return vals;
+ };
+
+QUnit = {};
+
+/**
+ * Config object: Maintain internal state
+ * Later exposed as QUnit.config
+ * `config` initialized at top of scope
+ */
+config = {
+ // The queue of tests to run
+ queue: [],
+
+ // block until document ready
+ blocking: true,
+
+ // by default, run previously failed tests first
+ // very useful in combination with "Hide passed tests" checked
+ reorder: true,
+
+ // by default, modify document.title when suite is done
+ altertitle: true,
+
+ // by default, scroll to top of the page when suite is done
+ scrolltop: true,
+
+ // when enabled, all tests must call expect()
+ requireExpects: false,
+
+ // add checkboxes that are persisted in the query-string
+ // when enabled, the id is set to `true` as a `QUnit.config` property
+ urlConfig: [
+ {
+ id: "hidepassed",
+ label: "Hide passed tests",
+ tooltip: "Only show tests and assertions that fail. Stored as query-strings."
+ },
+ {
+ id: "noglobals",
+ label: "Check for Globals",
+ tooltip: "Enabling this will test if any test introduces new properties on the " +
+ "`window` object. Stored as query-strings."
+ },
+ {
+ id: "notrycatch",
+ label: "No try-catch",
+ tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging " +
+ "exceptions in IE reasonable. Stored as query-strings."
+ }
+ ],
+
+ // Set of all modules.
+ modules: [],
+
+ // The first unnamed module
+ currentModule: {
+ name: "",
+ tests: []
+ },
+
+ callbacks: {}
+};
+
+// Push a loose unnamed module to the modules collection
+config.modules.push( config.currentModule );
+
+// Initialize more QUnit.config and QUnit.urlParams
+(function() {
+ var i, current,
+ location = window.location || { search: "", protocol: "file:" },
+ params = location.search.slice( 1 ).split( "&" ),
+ length = params.length,
+ urlParams = {};
+
+ if ( params[ 0 ] ) {
+ for ( i = 0; i < length; i++ ) {
+ current = params[ i ].split( "=" );
+ current[ 0 ] = decodeURIComponent( current[ 0 ] );
+
+ // allow just a key to turn on a flag, e.g., test.html?noglobals
+ current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true;
+ if ( urlParams[ current[ 0 ] ] ) {
+ urlParams[ current[ 0 ] ] = [].concat( urlParams[ current[ 0 ] ], current[ 1 ] );
+ } else {
+ urlParams[ current[ 0 ] ] = current[ 1 ];
+ }
+ }
+ }
+
+ if ( urlParams.filter === true ) {
+ delete urlParams.filter;
+ }
+
+ QUnit.urlParams = urlParams;
+
+ // String search anywhere in moduleName+testName
+ config.filter = urlParams.filter;
+
+ config.testId = [];
+ if ( urlParams.testId ) {
+
+ // Ensure that urlParams.testId is an array
+ urlParams.testId = [].concat( urlParams.testId );
+ for ( i = 0; i < urlParams.testId.length; i++ ) {
+ config.testId.push( urlParams.testId[ i ] );
+ }
+ }
+
+ // Figure out if we're running the tests from a server or not
+ QUnit.isLocal = location.protocol === "file:";
+}());
+
+// Root QUnit object.
+// `QUnit` initialized at top of scope
+extend( QUnit, {
+
+ // call on start of module test to prepend name to all tests
+ module: function( name, testEnvironment ) {
+ var currentModule = {
+ name: name,
+ testEnvironment: testEnvironment,
+ tests: []
+ };
+
+ // DEPRECATED: handles setup/teardown functions,
+ // beforeEach and afterEach should be used instead
+ if ( testEnvironment && testEnvironment.setup ) {
+ testEnvironment.beforeEach = testEnvironment.setup;
+ delete testEnvironment.setup;
+ }
+ if ( testEnvironment && testEnvironment.teardown ) {
+ testEnvironment.afterEach = testEnvironment.teardown;
+ delete testEnvironment.teardown;
+ }
+
+ config.modules.push( currentModule );
+ config.currentModule = currentModule;
+ },
+
+ // DEPRECATED: QUnit.asyncTest() will be removed in QUnit 2.0.
+ asyncTest: function( testName, expected, callback ) {
+ if ( arguments.length === 2 ) {
+ callback = expected;
+ expected = null;
+ }
+
+ QUnit.test( testName, expected, callback, true );
+ },
+
+ test: function( testName, expected, callback, async ) {
+ var test;
+
+ if ( arguments.length === 2 ) {
+ callback = expected;
+ expected = null;
+ }
+
+ test = new Test({
+ testName: testName,
+ expected: expected,
+ async: async,
+ callback: callback
+ });
+
+ test.queue();
+ },
+
+ skip: function( testName ) {
+ var test = new Test({
+ testName: testName,
+ skip: true
+ });
+
+ test.queue();
+ },
+
+ // DEPRECATED: The functionality of QUnit.start() will be altered in QUnit 2.0.
+ // In QUnit 2.0, invoking it will ONLY affect the `QUnit.config.autostart` blocking behavior.
+ start: function( count ) {
+ var globalStartAlreadyCalled = globalStartCalled;
+
+ if ( !config.current ) {
+ globalStartCalled = true;
+
+ if ( runStarted ) {
+ throw new Error( "Called start() outside of a test context while already started" );
+ } else if ( globalStartAlreadyCalled || count > 1 ) {
+ throw new Error( "Called start() outside of a test context too many times" );
+ } else if ( config.autostart ) {
+ throw new Error( "Called start() outside of a test context when " +
+ "QUnit.config.autostart was true" );
+ } else if ( !config.pageLoaded ) {
+
+ // The page isn't completely loaded yet, so bail out and let `QUnit.load` handle it
+ config.autostart = true;
+ return;
+ }
+ } else {
+
+ // If a test is running, adjust its semaphore
+ config.current.semaphore -= count || 1;
+
+ // Don't start until equal number of stop-calls
+ if ( config.current.semaphore > 0 ) {
+ return;
+ }
+
+ // throw an Error if start is called more often than stop
+ if ( config.current.semaphore < 0 ) {
+ config.current.semaphore = 0;
+
+ QUnit.pushFailure(
+ "Called start() while already started (test's semaphore was 0 already)",
+ sourceFromStacktrace( 2 )
+ );
+ return;
+ }
+ }
+
+ resumeProcessing();
+ },
+
+ // DEPRECATED: QUnit.stop() will be removed in QUnit 2.0.
+ stop: function( count ) {
+
+ // If there isn't a test running, don't allow QUnit.stop() to be called
+ if ( !config.current ) {
+ throw new Error( "Called stop() outside of a test context" );
+ }
+
+ // If a test is running, adjust its semaphore
+ config.current.semaphore += count || 1;
+
+ pauseProcessing();
+ },
+
+ config: config,
+
+ // Safe object type checking
+ is: function( type, obj ) {
+ return QUnit.objectType( obj ) === type;
+ },
+
+ objectType: function( obj ) {
+ if ( typeof obj === "undefined" ) {
+ return "undefined";
+ }
+
+ // Consider: typeof null === object
+ if ( obj === null ) {
+ return "null";
+ }
+
+ var match = toString.call( obj ).match( /^\[object\s(.*)\]$/ ),
+ type = match && match[ 1 ] || "";
+
+ switch ( type ) {
+ case "Number":
+ if ( isNaN( obj ) ) {
+ return "nan";
+ }
+ return "number";
+ case "String":
+ case "Boolean":
+ case "Array":
+ case "Date":
+ case "RegExp":
+ case "Function":
+ return type.toLowerCase();
+ }
+ if ( typeof obj === "object" ) {
+ return "object";
+ }
+ return undefined;
+ },
+
+ extend: extend,
+
+ load: function() {
+ config.pageLoaded = true;
+
+ // Initialize the configuration options
+ extend( config, {
+ stats: { all: 0, bad: 0 },
+ moduleStats: { all: 0, bad: 0 },
+ started: 0,
+ updateRate: 1000,
+ autostart: true,
+ filter: ""
+ }, true );
+
+ config.blocking = false;
+
+ if ( config.autostart ) {
+ resumeProcessing();
+ }
+ }
+});
+
+// Register logging callbacks
+(function() {
+ var i, l, key,
+ callbacks = [ "begin", "done", "log", "testStart", "testDone",
+ "moduleStart", "moduleDone" ];
+
+ function registerLoggingCallback( key ) {
+ var loggingCallback = function( callback ) {
+ if ( QUnit.objectType( callback ) !== "function" ) {
+ throw new Error(
+ "QUnit logging methods require a callback function as their first parameters."
+ );
+ }
+
+ config.callbacks[ key ].push( callback );
+ };
+
+ // DEPRECATED: This will be removed on QUnit 2.0.0+
+ // Stores the registered functions allowing restoring
+ // at verifyLoggingCallbacks() if modified
+ loggingCallbacks[ key ] = loggingCallback;
+
+ return loggingCallback;
+ }
+
+ for ( i = 0, l = callbacks.length; i < l; i++ ) {
+ key = callbacks[ i ];
+
+ // Initialize key collection of logging callback
+ if ( QUnit.objectType( config.callbacks[ key ] ) === "undefined" ) {
+ config.callbacks[ key ] = [];
+ }
+
+ QUnit[ key ] = registerLoggingCallback( key );
+ }
+})();
+
+// `onErrorFnPrev` initialized at top of scope
+// Preserve other handlers
+onErrorFnPrev = window.onerror;
+
+// Cover uncaught exceptions
+// Returning true will suppress the default browser handler,
+// returning false will let it run.
+window.onerror = function( error, filePath, linerNr ) {
+ var ret = false;
+ if ( onErrorFnPrev ) {
+ ret = onErrorFnPrev( error, filePath, linerNr );
+ }
+
+ // Treat return value as window.onerror itself does,
+ // Only do our handling if not suppressed.
+ if ( ret !== true ) {
+ if ( QUnit.config.current ) {
+ if ( QUnit.config.current.ignoreGlobalErrors ) {
+ return true;
+ }
+ QUnit.pushFailure( error, filePath + ":" + linerNr );
+ } else {
+ QUnit.test( "global failure", extend(function() {
+ QUnit.pushFailure( error, filePath + ":" + linerNr );
+ }, { validTest: true } ) );
+ }
+ return false;
+ }
+
+ return ret;
+};
+
+function done() {
+ var runtime, passed;
+
+ config.autorun = true;
+
+ // Log the last module results
+ if ( config.previousModule ) {
+ runLoggingCallbacks( "moduleDone", {
+ name: config.previousModule.name,
+ tests: config.previousModule.tests,
+ failed: config.moduleStats.bad,
+ passed: config.moduleStats.all - config.moduleStats.bad,
+ total: config.moduleStats.all,
+ runtime: now() - config.moduleStats.started
+ });
+ }
+ delete config.previousModule;
+
+ runtime = now() - config.started;
+ passed = config.stats.all - config.stats.bad;
+
+ runLoggingCallbacks( "done", {
+ failed: config.stats.bad,
+ passed: passed,
+ total: config.stats.all,
+ runtime: runtime
+ });
+}
+
+// Doesn't support IE6 to IE9
+// See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack
+function extractStacktrace( e, offset ) {
+ offset = offset === undefined ? 4 : offset;
+
+ var stack, include, i;
+
+ if ( e.stacktrace ) {
+
+ // Opera 12.x
+ return e.stacktrace.split( "\n" )[ offset + 3 ];
+ } else if ( e.stack ) {
+
+ // Firefox, Chrome, Safari 6+, IE10+, PhantomJS and Node
+ stack = e.stack.split( "\n" );
+ if ( /^error$/i.test( stack[ 0 ] ) ) {
+ stack.shift();
+ }
+ if ( fileName ) {
+ include = [];
+ for ( i = offset; i < stack.length; i++ ) {
+ if ( stack[ i ].indexOf( fileName ) !== -1 ) {
+ break;
+ }
+ include.push( stack[ i ] );
+ }
+ if ( include.length ) {
+ return include.join( "\n" );
+ }
+ }
+ return stack[ offset ];
+ } else if ( e.sourceURL ) {
+
+ // Safari < 6
+ // exclude useless self-reference for generated Error objects
+ if ( /qunit.js$/.test( e.sourceURL ) ) {
+ return;
+ }
+
+ // for actual exceptions, this is useful
+ return e.sourceURL + ":" + e.line;
+ }
+}
+
+function sourceFromStacktrace( offset ) {
+ var e = new Error();
+ if ( !e.stack ) {
+ try {
+ throw e;
+ } catch ( err ) {
+ // This should already be true in most browsers
+ e = err;
+ }
+ }
+ return extractStacktrace( e, offset );
+}
+
+function synchronize( callback, last ) {
+ if ( QUnit.objectType( callback ) === "array" ) {
+ while ( callback.length ) {
+ synchronize( callback.shift() );
+ }
+ return;
+ }
+ config.queue.push( callback );
+
+ if ( config.autorun && !config.blocking ) {
+ process( last );
+ }
+}
+
+function process( last ) {
+ function next() {
+ process( last );
+ }
+ var start = now();
+ config.depth = ( config.depth || 0 ) + 1;
+
+ while ( config.queue.length && !config.blocking ) {
+ if ( !defined.setTimeout || config.updateRate <= 0 ||
+ ( ( now() - start ) < config.updateRate ) ) {
+ if ( config.current ) {
+
+ // Reset async tracking for each phase of the Test lifecycle
+ config.current.usedAsync = false;
+ }
+ config.queue.shift()();
+ } else {
+ setTimeout( next, 13 );
+ break;
+ }
+ }
+ config.depth--;
+ if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) {
+ done();
+ }
+}
+
+function begin() {
+ var i, l,
+ modulesLog = [];
+
+ // If the test run hasn't officially begun yet
+ if ( !config.started ) {
+
+ // Record the time of the test run's beginning
+ config.started = now();
+
+ verifyLoggingCallbacks();
+
+ // Delete the loose unnamed module if unused.
+ if ( config.modules[ 0 ].name === "" && config.modules[ 0 ].tests.length === 0 ) {
+ config.modules.shift();
+ }
+
+ // Avoid unnecessary information by not logging modules' test environments
+ for ( i = 0, l = config.modules.length; i < l; i++ ) {
+ modulesLog.push({
+ name: config.modules[ i ].name,
+ tests: config.modules[ i ].tests
+ });
+ }
+
+ // The test run is officially beginning now
+ runLoggingCallbacks( "begin", {
+ totalTests: Test.count,
+ modules: modulesLog
+ });
+ }
+
+ config.blocking = false;
+ process( true );
+}
+
+function resumeProcessing() {
+ runStarted = true;
+
+ // A slight delay to allow this iteration of the event loop to finish (more assertions, etc.)
+ if ( defined.setTimeout ) {
+ setTimeout(function() {
+ if ( config.current && config.current.semaphore > 0 ) {
+ return;
+ }
+ if ( config.timeout ) {
+ clearTimeout( config.timeout );
+ }
+
+ begin();
+ }, 13 );
+ } else {
+ begin();
+ }
+}
+
+function pauseProcessing() {
+ config.blocking = true;
+
+ if ( config.testTimeout && defined.setTimeout ) {
+ clearTimeout( config.timeout );
+ config.timeout = setTimeout(function() {
+ if ( config.current ) {
+ config.current.semaphore = 0;
+ QUnit.pushFailure( "Test timed out", sourceFromStacktrace( 2 ) );
+ } else {
+ throw new Error( "Test timed out" );
+ }
+ resumeProcessing();
+ }, config.testTimeout );
+ }
+}
+
+function saveGlobal() {
+ config.pollution = [];
+
+ if ( config.noglobals ) {
+ for ( var key in window ) {
+ if ( hasOwn.call( window, key ) ) {
+ // in Opera sometimes DOM element ids show up here, ignore them
+ if ( /^qunit-test-output/.test( key ) ) {
+ continue;
+ }
+ config.pollution.push( key );
+ }
+ }
+ }
+}
+
+function checkPollution() {
+ var newGlobals,
+ deletedGlobals,
+ old = config.pollution;
+
+ saveGlobal();
+
+ newGlobals = diff( config.pollution, old );
+ if ( newGlobals.length > 0 ) {
+ QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join( ", " ) );
+ }
+
+ deletedGlobals = diff( old, config.pollution );
+ if ( deletedGlobals.length > 0 ) {
+ QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join( ", " ) );
+ }
+}
+
+// returns a new Array with the elements that are in a but not in b
+function diff( a, b ) {
+ var i, j,
+ result = a.slice();
+
+ for ( i = 0; i < result.length; i++ ) {
+ for ( j = 0; j < b.length; j++ ) {
+ if ( result[ i ] === b[ j ] ) {
+ result.splice( i, 1 );
+ i--;
+ break;
+ }
+ }
+ }
+ return result;
+}
+
+function extend( a, b, undefOnly ) {
+ for ( var prop in b ) {
+ if ( hasOwn.call( b, prop ) ) {
+
+ // Avoid "Member not found" error in IE8 caused by messing with window.constructor
+ if ( !( prop === "constructor" && a === window ) ) {
+ if ( b[ prop ] === undefined ) {
+ delete a[ prop ];
+ } else if ( !( undefOnly && typeof a[ prop ] !== "undefined" ) ) {
+ a[ prop ] = b[ prop ];
+ }
+ }
+ }
+ }
+
+ return a;
+}
+
+function runLoggingCallbacks( key, args ) {
+ var i, l, callbacks;
+
+ callbacks = config.callbacks[ key ];
+ for ( i = 0, l = callbacks.length; i < l; i++ ) {
+ callbacks[ i ]( args );
+ }
+}
+
+// DEPRECATED: This will be removed on 2.0.0+
+// This function verifies if the loggingCallbacks were modified by the user
+// If so, it will restore it, assign the given callback and print a console warning
+function verifyLoggingCallbacks() {
+ var loggingCallback, userCallback;
+
+ for ( loggingCallback in loggingCallbacks ) {
+ if ( QUnit[ loggingCallback ] !== loggingCallbacks[ loggingCallback ] ) {
+
+ userCallback = QUnit[ loggingCallback ];
+
+ // Restore the callback function
+ QUnit[ loggingCallback ] = loggingCallbacks[ loggingCallback ];
+
+ // Assign the deprecated given callback
+ QUnit[ loggingCallback ]( userCallback );
+
+ if ( window.console && window.console.warn ) {
+ window.console.warn(
+ "QUnit." + loggingCallback + " was replaced with a new value.\n" +
+ "Please, check out the documentation on how to apply logging callbacks.\n" +
+ "Reference: http://api.qunitjs.com/category/callbacks/"
+ );
+ }
+ }
+ }
+}
+
+// from jquery.js
+function inArray( elem, array ) {
+ if ( array.indexOf ) {
+ return array.indexOf( elem );
+ }
+
+ for ( var i = 0, length = array.length; i < length; i++ ) {
+ if ( array[ i ] === elem ) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+function Test( settings ) {
+ var i, l;
+
+ ++Test.count;
+
+ extend( this, settings );
+ this.assertions = [];
+ this.semaphore = 0;
+ this.usedAsync = false;
+ this.module = config.currentModule;
+ this.stack = sourceFromStacktrace( 3 );
+
+ // Register unique strings
+ for ( i = 0, l = this.module.tests; i < l.length; i++ ) {
+ if ( this.module.tests[ i ].name === this.testName ) {
+ this.testName += " ";
+ }
+ }
+
+ this.testId = generateHash( this.module.name, this.testName );
+
+ this.module.tests.push({
+ name: this.testName,
+ testId: this.testId
+ });
+
+ if ( settings.skip ) {
+
+ // Skipped tests will fully ignore any sent callback
+ this.callback = function() {};
+ this.async = false;
+ this.expected = 0;
+ } else {
+ this.assert = new Assert( this );
+ }
+}
+
+Test.count = 0;
+
+Test.prototype = {
+ before: function() {
+ if (
+
+ // Emit moduleStart when we're switching from one module to another
+ this.module !== config.previousModule ||
+
+ // They could be equal (both undefined) but if the previousModule property doesn't
+ // yet exist it means this is the first test in a suite that isn't wrapped in a
+ // module, in which case we'll just emit a moduleStart event for 'undefined'.
+ // Without this, reporters can get testStart before moduleStart which is a problem.
+ !hasOwn.call( config, "previousModule" )
+ ) {
+ if ( hasOwn.call( config, "previousModule" ) ) {
+ runLoggingCallbacks( "moduleDone", {
+ name: config.previousModule.name,
+ tests: config.previousModule.tests,
+ failed: config.moduleStats.bad,
+ passed: config.moduleStats.all - config.moduleStats.bad,
+ total: config.moduleStats.all,
+ runtime: now() - config.moduleStats.started
+ });
+ }
+ config.previousModule = this.module;
+ config.moduleStats = { all: 0, bad: 0, started: now() };
+ runLoggingCallbacks( "moduleStart", {
+ name: this.module.name,
+ tests: this.module.tests
+ });
+ }
+
+ config.current = this;
+
+ this.testEnvironment = extend( {}, this.module.testEnvironment );
+ delete this.testEnvironment.beforeEach;
+ delete this.testEnvironment.afterEach;
+
+ this.started = now();
+ runLoggingCallbacks( "testStart", {
+ name: this.testName,
+ module: this.module.name,
+ testId: this.testId
+ });
+
+ if ( !config.pollution ) {
+ saveGlobal();
+ }
+ },
+
+ run: function() {
+ var promise;
+
+ config.current = this;
+
+ if ( this.async ) {
+ QUnit.stop();
+ }
+
+ this.callbackStarted = now();
+
+ if ( config.notrycatch ) {
+ promise = this.callback.call( this.testEnvironment, this.assert );
+ this.resolvePromise( promise );
+ return;
+ }
+
+ try {
+ promise = this.callback.call( this.testEnvironment, this.assert );
+ this.resolvePromise( promise );
+ } catch ( e ) {
+ this.pushFailure( "Died on test #" + ( this.assertions.length + 1 ) + " " +
+ this.stack + ": " + ( e.message || e ), extractStacktrace( e, 0 ) );
+
+ // else next test will carry the responsibility
+ saveGlobal();
+
+ // Restart the tests if they're blocking
+ if ( config.blocking ) {
+ QUnit.start();
+ }
+ }
+ },
+
+ after: function() {
+ checkPollution();
+ },
+
+ queueHook: function( hook, hookName ) {
+ var promise,
+ test = this;
+ return function runHook() {
+ config.current = test;
+ if ( config.notrycatch ) {
+ promise = hook.call( test.testEnvironment, test.assert );
+ test.resolvePromise( promise, hookName );
+ return;
+ }
+ try {
+ promise = hook.call( test.testEnvironment, test.assert );
+ test.resolvePromise( promise, hookName );
+ } catch ( error ) {
+ test.pushFailure( hookName + " failed on " + test.testName + ": " +
+ ( error.message || error ), extractStacktrace( error, 0 ) );
+ }
+ };
+ },
+
+ // Currently only used for module level hooks, can be used to add global level ones
+ hooks: function( handler ) {
+ var hooks = [];
+
+ // Hooks are ignored on skipped tests
+ if ( this.skip ) {
+ return hooks;
+ }
+
+ if ( this.module.testEnvironment &&
+ QUnit.objectType( this.module.testEnvironment[ handler ] ) === "function" ) {
+ hooks.push( this.queueHook( this.module.testEnvironment[ handler ], handler ) );
+ }
+
+ return hooks;
+ },
+
+ finish: function() {
+ config.current = this;
+ if ( config.requireExpects && this.expected === null ) {
+ this.pushFailure( "Expected number of assertions to be defined, but expect() was " +
+ "not called.", this.stack );
+ } else if ( this.expected !== null && this.expected !== this.assertions.length ) {
+ this.pushFailure( "Expected " + this.expected + " assertions, but " +
+ this.assertions.length + " were run", this.stack );
+ } else if ( this.expected === null && !this.assertions.length ) {
+ this.pushFailure( "Expected at least one assertion, but none were run - call " +
+ "expect(0) to accept zero assertions.", this.stack );
+ }
+
+ var i,
+ bad = 0;
+
+ this.runtime = now() - this.started;
+ config.stats.all += this.assertions.length;
+ config.moduleStats.all += this.assertions.length;
+
+ for ( i = 0; i < this.assertions.length; i++ ) {
+ if ( !this.assertions[ i ].result ) {
+ bad++;
+ config.stats.bad++;
+ config.moduleStats.bad++;
+ }
+ }
+
+ runLoggingCallbacks( "testDone", {
+ name: this.testName,
+ module: this.module.name,
+ skipped: !!this.skip,
+ failed: bad,
+ passed: this.assertions.length - bad,
+ total: this.assertions.length,
+ runtime: this.runtime,
+
+ // HTML Reporter use
+ assertions: this.assertions,
+ testId: this.testId,
+
+ // DEPRECATED: this property will be removed in 2.0.0, use runtime instead
+ duration: this.runtime
+ });
+
+ // QUnit.reset() is deprecated and will be replaced for a new
+ // fixture reset function on QUnit 2.0/2.1.
+ // It's still called here for backwards compatibility handling
+ QUnit.reset();
+
+ config.current = undefined;
+ },
+
+ queue: function() {
+ var bad,
+ test = this;
+
+ if ( !this.valid() ) {
+ return;
+ }
+
+ function run() {
+
+ // each of these can by async
+ synchronize([
+ function() {
+ test.before();
+ },
+
+ test.hooks( "beforeEach" ),
+
+ function() {
+ test.run();
+ },
+
+ test.hooks( "afterEach" ).reverse(),
+
+ function() {
+ test.after();
+ },
+ function() {
+ test.finish();
+ }
+ ]);
+ }
+
+ // `bad` initialized at top of scope
+ // defer when previous test run passed, if storage is available
+ bad = QUnit.config.reorder && defined.sessionStorage &&
+ +sessionStorage.getItem( "qunit-test-" + this.module.name + "-" + this.testName );
+
+ if ( bad ) {
+ run();
+ } else {
+ synchronize( run, true );
+ }
+ },
+
+ push: function( result, actual, expected, message ) {
+ var source,
+ details = {
+ module: this.module.name,
+ name: this.testName,
+ result: result,
+ message: message,
+ actual: actual,
+ expected: expected,
+ testId: this.testId,
+ runtime: now() - this.started
+ };
+
+ if ( !result ) {
+ source = sourceFromStacktrace();
+
+ if ( source ) {
+ details.source = source;
+ }
+ }
+
+ runLoggingCallbacks( "log", details );
+
+ this.assertions.push({
+ result: !!result,
+ message: message
+ });
+ },
+
+ pushFailure: function( message, source, actual ) {
+ if ( !this instanceof Test ) {
+ throw new Error( "pushFailure() assertion outside test context, was " +
+ sourceFromStacktrace( 2 ) );
+ }
+
+ var details = {
+ module: this.module.name,
+ name: this.testName,
+ result: false,
+ message: message || "error",
+ actual: actual || null,
+ testId: this.testId,
+ runtime: now() - this.started
+ };
+
+ if ( source ) {
+ details.source = source;
+ }
+
+ runLoggingCallbacks( "log", details );
+
+ this.assertions.push({
+ result: false,
+ message: message
+ });
+ },
+
+ resolvePromise: function( promise, phase ) {
+ var then, message,
+ test = this;
+ if ( promise != null ) {
+ then = promise.then;
+ if ( QUnit.objectType( then ) === "function" ) {
+ QUnit.stop();
+ then.call(
+ promise,
+ QUnit.start,
+ function( error ) {
+ message = "Promise rejected " +
+ ( !phase ? "during" : phase.replace( /Each$/, "" ) ) +
+ " " + test.testName + ": " + ( error.message || error );
+ test.pushFailure( message, extractStacktrace( error, 0 ) );
+
+ // else next test will carry the responsibility
+ saveGlobal();
+
+ // Unblock
+ QUnit.start();
+ }
+ );
+ }
+ }
+ },
+
+ valid: function() {
+ var include,
+ filter = config.filter,
+ module = QUnit.urlParams.module && QUnit.urlParams.module.toLowerCase(),
+ fullName = ( this.module.name + ": " + this.testName ).toLowerCase();
+
+ // Internally-generated tests are always valid
+ if ( this.callback && this.callback.validTest ) {
+ return true;
+ }
+
+ if ( config.testId.length > 0 && inArray( this.testId, config.testId ) < 0 ) {
+ return false;
+ }
+
+ if ( module && ( !this.module.name || this.module.name.toLowerCase() !== module ) ) {
+ return false;
+ }
+
+ if ( !filter ) {
+ return true;
+ }
+
+ include = filter.charAt( 0 ) !== "!";
+ if ( !include ) {
+ filter = filter.toLowerCase().slice( 1 );
+ }
+
+ // If the filter matches, we need to honour include
+ if ( fullName.indexOf( filter ) !== -1 ) {
+ return include;
+ }
+
+ // Otherwise, do the opposite
+ return !include;
+ }
+
+};
+
+// Resets the test setup. Useful for tests that modify the DOM.
+/*
+DEPRECATED: Use multiple tests instead of resetting inside a test.
+Use testStart or testDone for custom cleanup.
+This method will throw an error in 2.0, and will be removed in 2.1
+*/
+QUnit.reset = function() {
+
+ // Return on non-browser environments
+ // This is necessary to not break on node tests
+ if ( typeof window === "undefined" ) {
+ return;
+ }
+
+ var fixture = defined.document && document.getElementById &&
+ document.getElementById( "qunit-fixture" );
+
+ if ( fixture ) {
+ fixture.innerHTML = config.fixture;
+ }
+};
+
+QUnit.pushFailure = function() {
+ if ( !QUnit.config.current ) {
+ throw new Error( "pushFailure() assertion outside test context, in " +
+ sourceFromStacktrace( 2 ) );
+ }
+
+ // Gets current test obj
+ var currentTest = QUnit.config.current;
+
+ return currentTest.pushFailure.apply( currentTest, arguments );
+};
+
+// Based on Java's String.hashCode, a simple but not
+// rigorously collision resistant hashing function
+function generateHash( module, testName ) {
+ var hex,
+ i = 0,
+ hash = 0,
+ str = module + "\x1C" + testName,
+ len = str.length;
+
+ for ( ; i < len; i++ ) {
+ hash = ( ( hash << 5 ) - hash ) + str.charCodeAt( i );
+ hash |= 0;
+ }
+
+ // Convert the possibly negative integer hash code into an 8 character hex string, which isn't
+ // strictly necessary but increases user understanding that the id is a SHA-like hash
+ hex = ( 0x100000000 + hash ).toString( 16 );
+ if ( hex.length < 8 ) {
+ hex = "0000000" + hex;
+ }
+
+ return hex.slice( -8 );
+}
+
+function Assert( testContext ) {
+ this.test = testContext;
+}
+
+// Assert helpers
+QUnit.assert = Assert.prototype = {
+
+ // Specify the number of expected assertions to guarantee that failed test
+ // (no assertions are run at all) don't slip through.
+ expect: function( asserts ) {
+ if ( arguments.length === 1 ) {
+ this.test.expected = asserts;
+ } else {
+ return this.test.expected;
+ }
+ },
+
+ // Increment this Test's semaphore counter, then return a single-use function that
+ // decrements that counter a maximum of once.
+ async: function() {
+ var test = this.test,
+ popped = false;
+
+ test.semaphore += 1;
+ test.usedAsync = true;
+ pauseProcessing();
+
+ return function done() {
+ if ( !popped ) {
+ test.semaphore -= 1;
+ popped = true;
+ resumeProcessing();
+ } else {
+ test.pushFailure( "Called the callback returned from `assert.async` more than once",
+ sourceFromStacktrace( 2 ) );
+ }
+ };
+ },
+
+ // Exports test.push() to the user API
+ push: function( /* result, actual, expected, message */ ) {
+ var assert = this,
+ currentTest = ( assert instanceof Assert && assert.test ) || QUnit.config.current;
+
+ // Backwards compatibility fix.
+ // Allows the direct use of global exported assertions and QUnit.assert.*
+ // Although, it's use is not recommended as it can leak assertions
+ // to other tests from async tests, because we only get a reference to the current test,
+ // not exactly the test where assertion were intended to be called.
+ if ( !currentTest ) {
+ throw new Error( "assertion outside test context, in " + sourceFromStacktrace( 2 ) );
+ }
+
+ if ( currentTest.usedAsync === true && currentTest.semaphore === 0 ) {
+ currentTest.pushFailure( "Assertion after the final `assert.async` was resolved",
+ sourceFromStacktrace( 2 ) );
+
+ // Allow this assertion to continue running anyway...
+ }
+
+ if ( !( assert instanceof Assert ) ) {
+ assert = currentTest.assert;
+ }
+ return assert.test.push.apply( assert.test, arguments );
+ },
+
+ /**
+ * Asserts rough true-ish result.
+ * @name ok
+ * @function
+ * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );
+ */
+ ok: function( result, message ) {
+ message = message || ( result ? "okay" : "failed, expected argument to be truthy, was: " +
+ QUnit.dump.parse( result ) );
+ this.push( !!result, result, true, message );
+ },
+
+ /**
+ * Assert that the first two arguments are equal, with an optional message.
+ * Prints out both actual and expected values.
+ * @name equal
+ * @function
+ * @example equal( format( "{0} bytes.", 2), "2 bytes.", "replaces {0} with next argument" );
+ */
+ equal: function( actual, expected, message ) {
+ /*jshint eqeqeq:false */
+ this.push( expected == actual, actual, expected, message );
+ },
+
+ /**
+ * @name notEqual
+ * @function
+ */
+ notEqual: function( actual, expected, message ) {
+ /*jshint eqeqeq:false */
+ this.push( expected != actual, actual, expected, message );
+ },
+
+ /**
+ * @name propEqual
+ * @function
+ */
+ propEqual: function( actual, expected, message ) {
+ actual = objectValues( actual );
+ expected = objectValues( expected );
+ this.push( QUnit.equiv( actual, expected ), actual, expected, message );
+ },
+
+ /**
+ * @name notPropEqual
+ * @function
+ */
+ notPropEqual: function( actual, expected, message ) {
+ actual = objectValues( actual );
+ expected = objectValues( expected );
+ this.push( !QUnit.equiv( actual, expected ), actual, expected, message );
+ },
+
+ /**
+ * @name deepEqual
+ * @function
+ */
+ deepEqual: function( actual, expected, message ) {
+ this.push( QUnit.equiv( actual, expected ), actual, expected, message );
+ },
+
+ /**
+ * @name notDeepEqual
+ * @function
+ */
+ notDeepEqual: function( actual, expected, message ) {
+ this.push( !QUnit.equiv( actual, expected ), actual, expected, message );
+ },
+
+ /**
+ * @name strictEqual
+ * @function
+ */
+ strictEqual: function( actual, expected, message ) {
+ this.push( expected === actual, actual, expected, message );
+ },
+
+ /**
+ * @name notStrictEqual
+ * @function
+ */
+ notStrictEqual: function( actual, expected, message ) {
+ this.push( expected !== actual, actual, expected, message );
+ },
+
+ "throws": function( block, expected, message ) {
+ var actual, expectedType,
+ expectedOutput = expected,
+ ok = false;
+
+ // 'expected' is optional unless doing string comparison
+ if ( message == null && typeof expected === "string" ) {
+ message = expected;
+ expected = null;
+ }
+
+ this.test.ignoreGlobalErrors = true;
+ try {
+ block.call( this.test.testEnvironment );
+ } catch (e) {
+ actual = e;
+ }
+ this.test.ignoreGlobalErrors = false;
+
+ if ( actual ) {
+ expectedType = QUnit.objectType( expected );
+
+ // we don't want to validate thrown error
+ if ( !expected ) {
+ ok = true;
+ expectedOutput = null;
+
+ // expected is a regexp
+ } else if ( expectedType === "regexp" ) {
+ ok = expected.test( errorString( actual ) );
+
+ // expected is a string
+ } else if ( expectedType === "string" ) {
+ ok = expected === errorString( actual );
+
+ // expected is a constructor, maybe an Error constructor
+ } else if ( expectedType === "function" && actual instanceof expected ) {
+ ok = true;
+
+ // expected is an Error object
+ } else if ( expectedType === "object" ) {
+ ok = actual instanceof expected.constructor &&
+ actual.name === expected.name &&
+ actual.message === expected.message;
+
+ // expected is a validation function which returns true if validation passed
+ } else if ( expectedType === "function" && expected.call( {}, actual ) === true ) {
+ expectedOutput = null;
+ ok = true;
+ }
+
+ this.push( ok, actual, expectedOutput, message );
+ } else {
+ this.test.pushFailure( message, null, "No exception was thrown." );
+ }
+ }
+};
+
+// Provide an alternative to assert.throws(), for enviroments that consider throws a reserved word
+// Known to us are: Closure Compiler, Narwhal
+(function() {
+ /*jshint sub:true */
+ Assert.prototype.raises = Assert.prototype[ "throws" ];
+}());
+
+// Test for equality any JavaScript type.
+// Author: Philippe Rathé <prathe@gmail.com>
+QUnit.equiv = (function() {
+
+ // Call the o related callback with the given arguments.
+ function bindCallbacks( o, callbacks, args ) {
+ var prop = QUnit.objectType( o );
+ if ( prop ) {
+ if ( QUnit.objectType( callbacks[ prop ] ) === "function" ) {
+ return callbacks[ prop ].apply( callbacks, args );
+ } else {
+ return callbacks[ prop ]; // or undefined
+ }
+ }
+ }
+
+ // the real equiv function
+ var innerEquiv,
+
+ // stack to decide between skip/abort functions
+ callers = [],
+
+ // stack to avoiding loops from circular referencing
+ parents = [],
+ parentsB = [],
+
+ getProto = Object.getPrototypeOf || function( obj ) {
+ /* jshint camelcase: false, proto: true */
+ return obj.__proto__;
+ },
+ callbacks = (function() {
+
+ // for string, boolean, number and null
+ function useStrictEquality( b, a ) {
+
+ /*jshint eqeqeq:false */
+ if ( b instanceof a.constructor || a instanceof b.constructor ) {
+
+ // to catch short annotation VS 'new' annotation of a
+ // declaration
+ // e.g. var i = 1;
+ // var j = new Number(1);
+ return a == b;
+ } else {
+ return a === b;
+ }
+ }
+
+ return {
+ "string": useStrictEquality,
+ "boolean": useStrictEquality,
+ "number": useStrictEquality,
+ "null": useStrictEquality,
+ "undefined": useStrictEquality,
+
+ "nan": function( b ) {
+ return isNaN( b );
+ },
+
+ "date": function( b, a ) {
+ return QUnit.objectType( b ) === "date" && a.valueOf() === b.valueOf();
+ },
+
+ "regexp": function( b, a ) {
+ return QUnit.objectType( b ) === "regexp" &&
+
+ // the regex itself
+ a.source === b.source &&
+
+ // and its modifiers
+ a.global === b.global &&
+
+ // (gmi) ...
+ a.ignoreCase === b.ignoreCase &&
+ a.multiline === b.multiline &&
+ a.sticky === b.sticky;
+ },
+
+ // - skip when the property is a method of an instance (OOP)
+ // - abort otherwise,
+ // initial === would have catch identical references anyway
+ "function": function() {
+ var caller = callers[ callers.length - 1 ];
+ return caller !== Object && typeof caller !== "undefined";
+ },
+
+ "array": function( b, a ) {
+ var i, j, len, loop, aCircular, bCircular;
+
+ // b could be an object literal here
+ if ( QUnit.objectType( b ) !== "array" ) {
+ return false;
+ }
+
+ len = a.length;
+ if ( len !== b.length ) {
+ // safe and faster
+ return false;
+ }
+
+ // track reference to avoid circular references
+ parents.push( a );
+ parentsB.push( b );
+ for ( i = 0; i < len; i++ ) {
+ loop = false;
+ for ( j = 0; j < parents.length; j++ ) {
+ aCircular = parents[ j ] === a[ i ];
+ bCircular = parentsB[ j ] === b[ i ];
+ if ( aCircular || bCircular ) {
+ if ( a[ i ] === b[ i ] || aCircular && bCircular ) {
+ loop = true;
+ } else {
+ parents.pop();
+ parentsB.pop();
+ return false;
+ }
+ }
+ }
+ if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) {
+ parents.pop();
+ parentsB.pop();
+ return false;
+ }
+ }
+ parents.pop();
+ parentsB.pop();
+ return true;
+ },
+
+ "object": function( b, a ) {
+
+ /*jshint forin:false */
+ var i, j, loop, aCircular, bCircular,
+ // Default to true
+ eq = true,
+ aProperties = [],
+ bProperties = [];
+
+ // comparing constructors is more strict than using
+ // instanceof
+ if ( a.constructor !== b.constructor ) {
+
+ // Allow objects with no prototype to be equivalent to
+ // objects with Object as their constructor.
+ if ( !( ( getProto( a ) === null && getProto( b ) === Object.prototype ) ||
+ ( getProto( b ) === null && getProto( a ) === Object.prototype ) ) ) {
+ return false;
+ }
+ }
+
+ // stack constructor before traversing properties
+ callers.push( a.constructor );
+
+ // track reference to avoid circular references
+ parents.push( a );
+ parentsB.push( b );
+
+ // be strict: don't ensure hasOwnProperty and go deep
+ for ( i in a ) {
+ loop = false;
+ for ( j = 0; j < parents.length; j++ ) {
+ aCircular = parents[ j ] === a[ i ];
+ bCircular = parentsB[ j ] === b[ i ];
+ if ( aCircular || bCircular ) {
+ if ( a[ i ] === b[ i ] || aCircular && bCircular ) {
+ loop = true;
+ } else {
+ eq = false;
+ break;
+ }
+ }
+ }
+ aProperties.push( i );
+ if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) {
+ eq = false;
+ break;
+ }
+ }
+
+ parents.pop();
+ parentsB.pop();
+ callers.pop(); // unstack, we are done
+
+ for ( i in b ) {
+ bProperties.push( i ); // collect b's properties
+ }
+
+ // Ensures identical properties name
+ return eq && innerEquiv( aProperties.sort(), bProperties.sort() );
+ }
+ };
+ }());
+
+ innerEquiv = function() { // can take multiple arguments
+ var args = [].slice.apply( arguments );
+ if ( args.length < 2 ) {
+ return true; // end transition
+ }
+
+ return ( (function( a, b ) {
+ if ( a === b ) {
+ return true; // catch the most you can
+ } else if ( a === null || b === null || typeof a === "undefined" ||
+ typeof b === "undefined" ||
+ QUnit.objectType( a ) !== QUnit.objectType( b ) ) {
+
+ // don't lose time with error prone cases
+ return false;
+ } else {
+ return bindCallbacks( a, callbacks, [ b, a ] );
+ }
+
+ // apply transition with (1..n) arguments
+ }( args[ 0 ], args[ 1 ] ) ) &&
+ innerEquiv.apply( this, args.splice( 1, args.length - 1 ) ) );
+ };
+
+ return innerEquiv;
+}());
+
+// Based on jsDump by Ariel Flesler
+// http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html
+QUnit.dump = (function() {
+ function quote( str ) {
+ return "\"" + str.toString().replace( /"/g, "\\\"" ) + "\"";
+ }
+ function literal( o ) {
+ return o + "";
+ }
+ function join( pre, arr, post ) {
+ var s = dump.separator(),
+ base = dump.indent(),
+ inner = dump.indent( 1 );
+ if ( arr.join ) {
+ arr = arr.join( "," + s + inner );
+ }
+ if ( !arr ) {
+ return pre + post;
+ }
+ return [ pre, inner + arr, base + post ].join( s );
+ }
+ function array( arr, stack ) {
+ var i = arr.length,
+ ret = new Array( i );
+
+ if ( dump.maxDepth && dump.depth > dump.maxDepth ) {
+ return "[object Array]";
+ }
+
+ this.up();
+ while ( i-- ) {
+ ret[ i ] = this.parse( arr[ i ], undefined, stack );
+ }
+ this.down();
+ return join( "[", ret, "]" );
+ }
+
+ var reName = /^function (\w+)/,
+ dump = {
+
+ // objType is used mostly internally, you can fix a (custom) type in advance
+ parse: function( obj, objType, stack ) {
+ stack = stack || [];
+ var res, parser, parserType,
+ inStack = inArray( obj, stack );
+
+ if ( inStack !== -1 ) {
+ return "recursion(" + ( inStack - stack.length ) + ")";
+ }
+
+ objType = objType || this.typeOf( obj );
+ parser = this.parsers[ objType ];
+ parserType = typeof parser;
+
+ if ( parserType === "function" ) {
+ stack.push( obj );
+ res = parser.call( this, obj, stack );
+ stack.pop();
+ return res;
+ }
+ return ( parserType === "string" ) ? parser : this.parsers.error;
+ },
+ typeOf: function( obj ) {
+ var type;
+ if ( obj === null ) {
+ type = "null";
+ } else if ( typeof obj === "undefined" ) {
+ type = "undefined";
+ } else if ( QUnit.is( "regexp", obj ) ) {
+ type = "regexp";
+ } else if ( QUnit.is( "date", obj ) ) {
+ type = "date";
+ } else if ( QUnit.is( "function", obj ) ) {
+ type = "function";
+ } else if ( obj.setInterval !== undefined &&
+ obj.document !== undefined &&
+ obj.nodeType === undefined ) {
+ type = "window";
+ } else if ( obj.nodeType === 9 ) {
+ type = "document";
+ } else if ( obj.nodeType ) {
+ type = "node";
+ } else if (
+
+ // native arrays
+ toString.call( obj ) === "[object Array]" ||
+
+ // NodeList objects
+ ( typeof obj.length === "number" && obj.item !== undefined &&
+ ( obj.length ? obj.item( 0 ) === obj[ 0 ] : ( obj.item( 0 ) === null &&
+ obj[ 0 ] === undefined ) ) )
+ ) {
+ type = "array";
+ } else if ( obj.constructor === Error.prototype.constructor ) {
+ type = "error";
+ } else {
+ type = typeof obj;
+ }
+ return type;
+ },
+ separator: function() {
+ return this.multiline ? this.HTML ? "<br />" : "\n" : this.HTML ? " " : " ";
+ },
+ // extra can be a number, shortcut for increasing-calling-decreasing
+ indent: function( extra ) {
+ if ( !this.multiline ) {
+ return "";
+ }
+ var chr = this.indentChar;
+ if ( this.HTML ) {
+ chr = chr.replace( /\t/g, " " ).replace( / /g, " " );
+ }
+ return new Array( this.depth + ( extra || 0 ) ).join( chr );
+ },
+ up: function( a ) {
+ this.depth += a || 1;
+ },
+ down: function( a ) {
+ this.depth -= a || 1;
+ },
+ setParser: function( name, parser ) {
+ this.parsers[ name ] = parser;
+ },
+ // The next 3 are exposed so you can use them
+ quote: quote,
+ literal: literal,
+ join: join,
+ //
+ depth: 1,
+ maxDepth: 5,
+
+ // This is the list of parsers, to modify them, use dump.setParser
+ parsers: {
+ window: "[Window]",
+ document: "[Document]",
+ error: function( error ) {
+ return "Error(\"" + error.message + "\")";
+ },
+ unknown: "[Unknown]",
+ "null": "null",
+ "undefined": "undefined",
+ "function": function( fn ) {
+ var ret = "function",
+
+ // functions never have name in IE
+ name = "name" in fn ? fn.name : ( reName.exec( fn ) || [] )[ 1 ];
+
+ if ( name ) {
+ ret += " " + name;
+ }
+ ret += "( ";
+
+ ret = [ ret, dump.parse( fn, "functionArgs" ), "){" ].join( "" );
+ return join( ret, dump.parse( fn, "functionCode" ), "}" );
+ },
+ array: array,
+ nodelist: array,
+ "arguments": array,
+ object: function( map, stack ) {
+ var keys, key, val, i, nonEnumerableProperties,
+ ret = [];
+
+ if ( dump.maxDepth && dump.depth > dump.maxDepth ) {
+ return "[object Object]";
+ }
+
+ dump.up();
+ keys = [];
+ for ( key in map ) {
+ keys.push( key );
+ }
+
+ // Some properties are not always enumerable on Error objects.
+ nonEnumerableProperties = [ "message", "name" ];
+ for ( i in nonEnumerableProperties ) {
+ key = nonEnumerableProperties[ i ];
+ if ( key in map && !( key in keys ) ) {
+ keys.push( key );
+ }
+ }
+ keys.sort();
+ for ( i = 0; i < keys.length; i++ ) {
+ key = keys[ i ];
+ val = map[ key ];
+ ret.push( dump.parse( key, "key" ) + ": " +
+ dump.parse( val, undefined, stack ) );
+ }
+ dump.down();
+ return join( "{", ret, "}" );
+ },
+ node: function( node ) {
+ var len, i, val,
+ open = dump.HTML ? "<" : "<",
+ close = dump.HTML ? ">" : ">",
+ tag = node.nodeName.toLowerCase(),
+ ret = open + tag,
+ attrs = node.attributes;
+
+ if ( attrs ) {
+ for ( i = 0, len = attrs.length; i < len; i++ ) {
+ val = attrs[ i ].nodeValue;
+
+ // IE6 includes all attributes in .attributes, even ones not explicitly
+ // set. Those have values like undefined, null, 0, false, "" or
+ // "inherit".
+ if ( val && val !== "inherit" ) {
+ ret += " " + attrs[ i ].nodeName + "=" +
+ dump.parse( val, "attribute" );
+ }
+ }
+ }
+ ret += close;
+
+ // Show content of TextNode or CDATASection
+ if ( node.nodeType === 3 || node.nodeType === 4 ) {
+ ret += node.nodeValue;
+ }
+
+ return ret + open + "/" + tag + close;
+ },
+
+ // function calls it internally, it's the arguments part of the function
+ functionArgs: function( fn ) {
+ var args,
+ l = fn.length;
+
+ if ( !l ) {
+ return "";
+ }
+
+ args = new Array( l );
+ while ( l-- ) {
+
+ // 97 is 'a'
+ args[ l ] = String.fromCharCode( 97 + l );
+ }
+ return " " + args.join( ", " ) + " ";
+ },
+ // object calls it internally, the key part of an item in a map
+ key: quote,
+ // function calls it internally, it's the content of the function
+ functionCode: "[code]",
+ // node calls it internally, it's an html attribute value
+ attribute: quote,
+ string: quote,
+ date: quote,
+ regexp: literal,
+ number: literal,
+ "boolean": literal
+ },
+ // if true, entities are escaped ( <, >, \t, space and \n )
+ HTML: false,
+ // indentation unit
+ indentChar: " ",
+ // if true, items in a collection, are separated by a \n, else just a space.
+ multiline: true
+ };
+
+ return dump;
+}());
+
+// back compat
+QUnit.jsDump = QUnit.dump;
+
+// For browser, export only select globals
+if ( typeof window !== "undefined" ) {
+
+ // Deprecated
+ // Extend assert methods to QUnit and Global scope through Backwards compatibility
+ (function() {
+ var i,
+ assertions = Assert.prototype;
+
+ function applyCurrent( current ) {
+ return function() {
+ var assert = new Assert( QUnit.config.current );
+ current.apply( assert, arguments );
+ };
+ }
+
+ for ( i in assertions ) {
+ QUnit[ i ] = applyCurrent( assertions[ i ] );
+ }
+ })();
+
+ (function() {
+ var i, l,
+ keys = [
+ "test",
+ "module",
+ "expect",
+ "asyncTest",
+ "start",
+ "stop",
+ "ok",
+ "equal",
+ "notEqual",
+ "propEqual",
+ "notPropEqual",
+ "deepEqual",
+ "notDeepEqual",
+ "strictEqual",
+ "notStrictEqual",
+ "throws"
+ ];
+
+ for ( i = 0, l = keys.length; i < l; i++ ) {
+ window[ keys[ i ] ] = QUnit[ keys[ i ] ];
+ }
+ })();
+
+ window.QUnit = QUnit;
+}
+
+// For nodejs
+if ( typeof module !== "undefined" && module && module.exports ) {
+ module.exports = QUnit;
+
+ // For consistency with CommonJS environments' exports
+ module.exports.QUnit = QUnit;
+}
+
+// For CommonJS with exports, but without module.exports, like Rhino
+if ( typeof exports !== "undefined" && exports ) {
+ exports.QUnit = QUnit;
+}
+
+// Get a reference to the global object, like window in browsers
+}( (function() {
+ return this;
+})() ));
+
+/*istanbul ignore next */
+// jscs:disable maximumLineLength
+/*
+ * Javascript Diff Algorithm
+ * By John Resig (http://ejohn.org/)
+ * Modified by Chu Alan "sprite"
+ *
+ * Released under the MIT license.
+ *
+ * More Info:
+ * http://ejohn.org/projects/javascript-diff-algorithm/
+ *
+ * Usage: QUnit.diff(expected, actual)
+ *
+ * QUnit.diff( "the quick brown fox jumped over", "the quick fox jumps over" ) == "the quick <del>brown </del> fox <del>jumped </del><ins>jumps </ins> over"
+ */
+QUnit.diff = (function() {
+ var hasOwn = Object.prototype.hasOwnProperty;
+
+ /*jshint eqeqeq:false, eqnull:true */
+ function diff( o, n ) {
+ var i,
+ ns = {},
+ os = {};
+
+ for ( i = 0; i < n.length; i++ ) {
+ if ( !hasOwn.call( ns, n[ i ] ) ) {
+ ns[ n[ i ] ] = {
+ rows: [],
+ o: null
+ };
+ }
+ ns[ n[ i ] ].rows.push( i );
+ }
+
+ for ( i = 0; i < o.length; i++ ) {
+ if ( !hasOwn.call( os, o[ i ] ) ) {
+ os[ o[ i ] ] = {
+ rows: [],
+ n: null
+ };
+ }
+ os[ o[ i ] ].rows.push( i );
+ }
+
+ for ( i in ns ) {
+ if ( hasOwn.call( ns, i ) ) {
+ if ( ns[ i ].rows.length === 1 && hasOwn.call( os, i ) && os[ i ].rows.length === 1 ) {
+ n[ ns[ i ].rows[ 0 ] ] = {
+ text: n[ ns[ i ].rows[ 0 ] ],
+ row: os[ i ].rows[ 0 ]
+ };
+ o[ os[ i ].rows[ 0 ] ] = {
+ text: o[ os[ i ].rows[ 0 ] ],
+ row: ns[ i ].rows[ 0 ]
+ };
+ }
+ }
+ }
+
+ for ( i = 0; i < n.length - 1; i++ ) {
+ if ( n[ i ].text != null && n[ i + 1 ].text == null && n[ i ].row + 1 < o.length && o[ n[ i ].row + 1 ].text == null &&
+ n[ i + 1 ] == o[ n[ i ].row + 1 ] ) {
+
+ n[ i + 1 ] = {
+ text: n[ i + 1 ],
+ row: n[ i ].row + 1
+ };
+ o[ n[ i ].row + 1 ] = {
+ text: o[ n[ i ].row + 1 ],
+ row: i + 1
+ };
+ }
+ }
+
+ for ( i = n.length - 1; i > 0; i-- ) {
+ if ( n[ i ].text != null && n[ i - 1 ].text == null && n[ i ].row > 0 && o[ n[ i ].row - 1 ].text == null &&
+ n[ i - 1 ] == o[ n[ i ].row - 1 ] ) {
+
+ n[ i - 1 ] = {
+ text: n[ i - 1 ],
+ row: n[ i ].row - 1
+ };
+ o[ n[ i ].row - 1 ] = {
+ text: o[ n[ i ].row - 1 ],
+ row: i - 1
+ };
+ }
+ }
+
+ return {
+ o: o,
+ n: n
+ };
+ }
+
+ return function( o, n ) {
+ o = o.replace( /\s+$/, "" );
+ n = n.replace( /\s+$/, "" );
+
+ var i, pre,
+ str = "",
+ out = diff( o === "" ? [] : o.split( /\s+/ ), n === "" ? [] : n.split( /\s+/ ) ),
+ oSpace = o.match( /\s+/g ),
+ nSpace = n.match( /\s+/g );
+
+ if ( oSpace == null ) {
+ oSpace = [ " " ];
+ } else {
+ oSpace.push( " " );
+ }
+
+ if ( nSpace == null ) {
+ nSpace = [ " " ];
+ } else {
+ nSpace.push( " " );
+ }
+
+ if ( out.n.length === 0 ) {
+ for ( i = 0; i < out.o.length; i++ ) {
+ str += "<del>" + out.o[ i ] + oSpace[ i ] + "</del>";
+ }
+ } else {
+ if ( out.n[ 0 ].text == null ) {
+ for ( n = 0; n < out.o.length && out.o[ n ].text == null; n++ ) {
+ str += "<del>" + out.o[ n ] + oSpace[ n ] + "</del>";
+ }
+ }
+
+ for ( i = 0; i < out.n.length; i++ ) {
+ if ( out.n[ i ].text == null ) {
+ str += "<ins>" + out.n[ i ] + nSpace[ i ] + "</ins>";
+ } else {
+
+ // `pre` initialized at top of scope
+ pre = "";
+
+ for ( n = out.n[ i ].row + 1; n < out.o.length && out.o[ n ].text == null; n++ ) {
+ pre += "<del>" + out.o[ n ] + oSpace[ n ] + "</del>";
+ }
+ str += " " + out.n[ i ].text + nSpace[ i ] + pre;
+ }
+ }
+ }
+
+ return str;
+ };
+}());
+// jscs:enable
+
+(function() {
+
+// Deprecated QUnit.init - Ref #530
+// Re-initialize the configuration options
+QUnit.init = function() {
+ var tests, banner, result, qunit,
+ config = QUnit.config;
+
+ config.stats = { all: 0, bad: 0 };
+ config.moduleStats = { all: 0, bad: 0 };
+ config.started = 0;
+ config.updateRate = 1000;
+ config.blocking = false;
+ config.autostart = true;
+ config.autorun = false;
+ config.filter = "";
+ config.queue = [];
+
+ // Return on non-browser environments
+ // This is necessary to not break on node tests
+ if ( typeof window === "undefined" ) {
+ return;
+ }
+
+ qunit = id( "qunit" );
+ if ( qunit ) {
+ qunit.innerHTML =
+ "<h1 id='qunit-header'>" + escapeText( document.title ) + "</h1>" +
+ "<h2 id='qunit-banner'></h2>" +
+ "<div id='qunit-testrunner-toolbar'></div>" +
+ "<h2 id='qunit-userAgent'></h2>" +
+ "<ol id='qunit-tests'></ol>";
+ }
+
+ tests = id( "qunit-tests" );
+ banner = id( "qunit-banner" );
+ result = id( "qunit-testresult" );
+
+ if ( tests ) {
+ tests.innerHTML = "";
+ }
+
+ if ( banner ) {
+ banner.className = "";
+ }
+
+ if ( result ) {
+ result.parentNode.removeChild( result );
+ }
+
+ if ( tests ) {
+ result = document.createElement( "p" );
+ result.id = "qunit-testresult";
+ result.className = "result";
+ tests.parentNode.insertBefore( result, tests );
+ result.innerHTML = "Running...<br /> ";
+ }
+};
+
+// Don't load the HTML Reporter on non-Browser environments
+if ( typeof window === "undefined" ) {
+ return;
+}
+
+var config = QUnit.config,
+ hasOwn = Object.prototype.hasOwnProperty,
+ defined = {
+ document: window.document !== undefined,
+ sessionStorage: (function() {
+ var x = "qunit-test-string";
+ try {
+ sessionStorage.setItem( x, x );
+ sessionStorage.removeItem( x );
+ return true;
+ } catch ( e ) {
+ return false;
+ }
+ }())
+ },
+ modulesList = [];
+
+/**
+* Escape text for attribute or text content.
+*/
+function escapeText( s ) {
+ if ( !s ) {
+ return "";
+ }
+ s = s + "";
+
+ // Both single quotes and double quotes (for attributes)
+ return s.replace( /['"<>&]/g, function( s ) {
+ switch ( s ) {
+ case "'":
+ return "'";
+ case "\"":
+ return """;
+ case "<":
+ return "<";
+ case ">":
+ return ">";
+ case "&":
+ return "&";
+ }
+ });
+}
+
+/**
+ * @param {HTMLElement} elem
+ * @param {string} type
+ * @param {Function} fn
+ */
+function addEvent( elem, type, fn ) {
+ if ( elem.addEventListener ) {
+
+ // Standards-based browsers
+ elem.addEventListener( type, fn, false );
+ } else if ( elem.attachEvent ) {
+
+ // support: IE <9
+ elem.attachEvent( "on" + type, fn );
+ }
+}
+
+/**
+ * @param {Array|NodeList} elems
+ * @param {string} type
+ * @param {Function} fn
+ */
+function addEvents( elems, type, fn ) {
+ var i = elems.length;
+ while ( i-- ) {
+ addEvent( elems[ i ], type, fn );
+ }
+}
+
+function hasClass( elem, name ) {
+ return ( " " + elem.className + " " ).indexOf( " " + name + " " ) >= 0;
+}
+
+function addClass( elem, name ) {
+ if ( !hasClass( elem, name ) ) {
+ elem.className += ( elem.className ? " " : "" ) + name;
+ }
+}
+
+function toggleClass( elem, name ) {
+ if ( hasClass( elem, name ) ) {
+ removeClass( elem, name );
+ } else {
+ addClass( elem, name );
+ }
+}
+
+function removeClass( elem, name ) {
+ var set = " " + elem.className + " ";
+
+ // Class name may appear multiple times
+ while ( set.indexOf( " " + name + " " ) >= 0 ) {
+ set = set.replace( " " + name + " ", " " );
+ }
+
+ // trim for prettiness
+ elem.className = typeof set.trim === "function" ? set.trim() : set.replace( /^\s+|\s+$/g, "" );
+}
+
+function id( name ) {
+ return defined.document && document.getElementById && document.getElementById( name );
+}
+
+function getUrlConfigHtml() {
+ var i, j, val,
+ escaped, escapedTooltip,
+ selection = false,
+ len = config.urlConfig.length,
+ urlConfigHtml = "";
+
+ for ( i = 0; i < len; i++ ) {
+ val = config.urlConfig[ i ];
+ if ( typeof val === "string" ) {
+ val = {
+ id: val,
+ label: val
+ };
+ }
+
+ escaped = escapeText( val.id );
+ escapedTooltip = escapeText( val.tooltip );
+
+ if ( config[ val.id ] === undefined ) {
+ config[ val.id ] = QUnit.urlParams[ val.id ];
+ }
+
+ if ( !val.value || typeof val.value === "string" ) {
+ urlConfigHtml += "<input id='qunit-urlconfig-" + escaped +
+ "' name='" + escaped + "' type='checkbox'" +
+ ( val.value ? " value='" + escapeText( val.value ) + "'" : "" ) +
+ ( config[ val.id ] ? " checked='checked'" : "" ) +
+ " title='" + escapedTooltip + "' /><label for='qunit-urlconfig-" + escaped +
+ "' title='" + escapedTooltip + "'>" + val.label + "</label>";
+ } else {
+ urlConfigHtml += "<label for='qunit-urlconfig-" + escaped +
+ "' title='" + escapedTooltip + "'>" + val.label +
+ ": </label><select id='qunit-urlconfig-" + escaped +
+ "' name='" + escaped + "' title='" + escapedTooltip + "'><option></option>";
+
+ if ( QUnit.is( "array", val.value ) ) {
+ for ( j = 0; j < val.value.length; j++ ) {
+ escaped = escapeText( val.value[ j ] );
+ urlConfigHtml += "<option value='" + escaped + "'" +
+ ( config[ val.id ] === val.value[ j ] ?
+ ( selection = true ) && " selected='selected'" : "" ) +
+ ">" + escaped + "</option>";
+ }
+ } else {
+ for ( j in val.value ) {
+ if ( hasOwn.call( val.value, j ) ) {
+ urlConfigHtml += "<option value='" + escapeText( j ) + "'" +
+ ( config[ val.id ] === j ?
+ ( selection = true ) && " selected='selected'" : "" ) +
+ ">" + escapeText( val.value[ j ] ) + "</option>";
+ }
+ }
+ }
+ if ( config[ val.id ] && !selection ) {
+ escaped = escapeText( config[ val.id ] );
+ urlConfigHtml += "<option value='" + escaped +
+ "' selected='selected' disabled='disabled'>" + escaped + "</option>";
+ }
+ urlConfigHtml += "</select>";
+ }
+ }
+
+ return urlConfigHtml;
+}
+
+// Handle "click" events on toolbar checkboxes and "change" for select menus.
+// Updates the URL with the new state of `config.urlConfig` values.
+function toolbarChanged() {
+ var updatedUrl, value,
+ field = this,
+ params = {};
+
+ // Detect if field is a select menu or a checkbox
+ if ( "selectedIndex" in field ) {
+ value = field.options[ field.selectedIndex ].value || undefined;
+ } else {
+ value = field.checked ? ( field.defaultValue || true ) : undefined;
+ }
+
+ params[ field.name ] = value;
+ updatedUrl = setUrl( params );
+
+ if ( "hidepassed" === field.name && "replaceState" in window.history ) {
+ config[ field.name ] = value || false;
+ if ( value ) {
+ addClass( id( "qunit-tests" ), "hidepass" );
+ } else {
+ removeClass( id( "qunit-tests" ), "hidepass" );
+ }
+
+ // It is not necessary to refresh the whole page
+ window.history.replaceState( null, "", updatedUrl );
+ } else {
+ window.location = updatedUrl;
+ }
+}
+
+function setUrl( params ) {
+ var key,
+ querystring = "?";
+
+ params = QUnit.extend( QUnit.extend( {}, QUnit.urlParams ), params );
+
+ for ( key in params ) {
+ if ( hasOwn.call( params, key ) ) {
+ if ( params[ key ] === undefined ) {
+ continue;
+ }
+ querystring += encodeURIComponent( key );
+ if ( params[ key ] !== true ) {
+ querystring += "=" + encodeURIComponent( params[ key ] );
+ }
+ querystring += "&";
+ }
+ }
+ return location.protocol + "//" + location.host +
+ location.pathname + querystring.slice( 0, -1 );
+}
+
+function applyUrlParams() {
+ var selectBox = id( "qunit-modulefilter" ),
+ selection = decodeURIComponent( selectBox.options[ selectBox.selectedIndex ].value ),
+ filter = id( "qunit-filter-input" ).value;
+
+ window.location = setUrl({
+ module: ( selection === "" ) ? undefined : selection,
+ filter: ( filter === "" ) ? undefined : filter,
+
+ // Remove testId filter
+ testId: undefined
+ });
+}
+
+function toolbarUrlConfigContainer() {
+ var urlConfigContainer = document.createElement( "span" );
+
+ urlConfigContainer.innerHTML = getUrlConfigHtml();
+ addClass( urlConfigContainer, "qunit-url-config" );
+
+ // For oldIE support:
+ // * Add handlers to the individual elements instead of the container
+ // * Use "click" instead of "change" for checkboxes
+ addEvents( urlConfigContainer.getElementsByTagName( "input" ), "click", toolbarChanged );
+ addEvents( urlConfigContainer.getElementsByTagName( "select" ), "change", toolbarChanged );
+
+ return urlConfigContainer;
+}
+
+function toolbarLooseFilter() {
+ var filter = document.createElement( "form" ),
+ label = document.createElement( "label" ),
+ input = document.createElement( "input" ),
+ button = document.createElement( "button" );
+
+ addClass( filter, "qunit-filter" );
+
+ label.innerHTML = "Filter: ";
+
+ input.type = "text";
+ input.value = config.filter || "";
+ input.name = "filter";
+ input.id = "qunit-filter-input";
+
+ button.innerHTML = "Go";
+
+ label.appendChild( input );
+
+ filter.appendChild( label );
+ filter.appendChild( button );
+ addEvent( filter, "submit", function( ev ) {
+ applyUrlParams();
+
+ if ( ev && ev.preventDefault ) {
+ ev.preventDefault();
+ }
+
+ return false;
+ });
+
+ return filter;
+}
+
+function toolbarModuleFilterHtml() {
+ var i,
+ moduleFilterHtml = "";
+
+ if ( !modulesList.length ) {
+ return false;
+ }
+
+ modulesList.sort(function( a, b ) {
+ return a.localeCompare( b );
+ });
+
+ moduleFilterHtml += "<label for='qunit-modulefilter'>Module: </label>" +
+ "<select id='qunit-modulefilter' name='modulefilter'><option value='' " +
+ ( QUnit.urlParams.module === undefined ? "selected='selected'" : "" ) +
+ ">< All Modules ></option>";
+
+ for ( i = 0; i < modulesList.length; i++ ) {
+ moduleFilterHtml += "<option value='" +
+ escapeText( encodeURIComponent( modulesList[ i ] ) ) + "' " +
+ ( QUnit.urlParams.module === modulesList[ i ] ? "selected='selected'" : "" ) +
+ ">" + escapeText( modulesList[ i ] ) + "</option>";
+ }
+ moduleFilterHtml += "</select>";
+
+ return moduleFilterHtml;
+}
+
+function toolbarModuleFilter() {
+ var toolbar = id( "qunit-testrunner-toolbar" ),
+ moduleFilter = document.createElement( "span" ),
+ moduleFilterHtml = toolbarModuleFilterHtml();
+
+ if ( !toolbar || !moduleFilterHtml ) {
+ return false;
+ }
+
+ moduleFilter.setAttribute( "id", "qunit-modulefilter-container" );
+ moduleFilter.innerHTML = moduleFilterHtml;
+
+ addEvent( moduleFilter.lastChild, "change", applyUrlParams );
+
+ toolbar.appendChild( moduleFilter );
+}
+
+function appendToolbar() {
+ var toolbar = id( "qunit-testrunner-toolbar" );
+
+ if ( toolbar ) {
+ toolbar.appendChild( toolbarUrlConfigContainer() );
+ toolbar.appendChild( toolbarLooseFilter() );
+ }
+}
+
+function appendHeader() {
+ var header = id( "qunit-header" );
+
+ if ( header ) {
+ header.innerHTML = "<a href='" +
+ setUrl({ filter: undefined, module: undefined, testId: undefined }) +
+ "'>" + header.innerHTML + "</a> ";
+ }
+}
+
+function appendBanner() {
+ var banner = id( "qunit-banner" );
+
+ if ( banner ) {
+ banner.className = "";
+ }
+}
+
+function appendTestResults() {
+ var tests = id( "qunit-tests" ),
+ result = id( "qunit-testresult" );
+
+ if ( result ) {
+ result.parentNode.removeChild( result );
+ }
+
+ if ( tests ) {
+ tests.innerHTML = "";
+ result = document.createElement( "p" );
+ result.id = "qunit-testresult";
+ result.className = "result";
+ tests.parentNode.insertBefore( result, tests );
+ result.innerHTML = "Running...<br /> ";
+ }
+}
+
+function storeFixture() {
+ var fixture = id( "qunit-fixture" );
+ if ( fixture ) {
+ config.fixture = fixture.innerHTML;
+ }
+}
+
+function appendUserAgent() {
+ var userAgent = id( "qunit-userAgent" );
+ if ( userAgent ) {
+ userAgent.innerHTML = "";
+ userAgent.appendChild( document.createTextNode( navigator.userAgent ) );
+ }
+}
+
+function appendTestsList( modules ) {
+ var i, l, x, z, test, moduleObj;
+
+ for ( i = 0, l = modules.length; i < l; i++ ) {
+ moduleObj = modules[ i ];
+
+ if ( moduleObj.name ) {
+ modulesList.push( moduleObj.name );
+ }
+
+ for ( x = 0, z = moduleObj.tests.length; x < z; x++ ) {
+ test = moduleObj.tests[ x ];
+
+ appendTest( test.name, test.testId, moduleObj.name );
+ }
+ }
+}
+
+function appendTest( name, testId, moduleName ) {
+ var title, rerunTrigger, testBlock, assertList,
+ tests = id( "qunit-tests" );
+
+ if ( !tests ) {
+ return;
+ }
+
+ title = document.createElement( "strong" );
+ title.innerHTML = getNameHtml( name, moduleName );
+
+ rerunTrigger = document.createElement( "a" );
+ rerunTrigger.innerHTML = "Rerun";
+ rerunTrigger.href = setUrl({ testId: testId });
+
+ testBlock = document.createElement( "li" );
+ testBlock.appendChild( title );
+ testBlock.appendChild( rerunTrigger );
+ testBlock.id = "qunit-test-output-" + testId;
+
+ assertList = document.createElement( "ol" );
+ assertList.className = "qunit-assert-list";
+
+ testBlock.appendChild( assertList );
+
+ tests.appendChild( testBlock );
+}
+
+// HTML Reporter initialization and load
+QUnit.begin(function( details ) {
+ var qunit = id( "qunit" );
+
+ // Fixture is the only one necessary to run without the #qunit element
+ storeFixture();
+
+ if ( qunit ) {
+ qunit.innerHTML =
+ "<h1 id='qunit-header'>" + escapeText( document.title ) + "</h1>" +
+ "<h2 id='qunit-banner'></h2>" +
+ "<div id='qunit-testrunner-toolbar'></div>" +
+ "<h2 id='qunit-userAgent'></h2>" +
+ "<ol id='qunit-tests'></ol>";
+ }
+
+ appendHeader();
+ appendBanner();
+ appendTestResults();
+ appendUserAgent();
+ appendToolbar();
+ appendTestsList( details.modules );
+ toolbarModuleFilter();
+
+ if ( qunit && config.hidepassed ) {
+ addClass( qunit.lastChild, "hidepass" );
+ }
+});
+
+QUnit.done(function( details ) {
+ var i, key,
+ banner = id( "qunit-banner" ),
+ tests = id( "qunit-tests" ),
+ html = [
+ "Tests completed in ",
+ details.runtime,
+ " milliseconds.<br />",
+ "<span class='passed'>",
+ details.passed,
+ "</span> assertions of <span class='total'>",
+ details.total,
+ "</span> passed, <span class='failed'>",
+ details.failed,
+ "</span> failed."
+ ].join( "" );
+
+ if ( banner ) {
+ banner.className = details.failed ? "qunit-fail" : "qunit-pass";
+ }
+
+ if ( tests ) {
+ id( "qunit-testresult" ).innerHTML = html;
+ }
+
+ if ( config.altertitle && defined.document && document.title ) {
+
+ // show ✖ for good, ✔ for bad suite result in title
+ // use escape sequences in case file gets loaded with non-utf-8-charset
+ document.title = [
+ ( details.failed ? "\u2716" : "\u2714" ),
+ document.title.replace( /^[\u2714\u2716] /i, "" )
+ ].join( " " );
+ }
+
+ // clear own sessionStorage items if all tests passed
+ if ( config.reorder && defined.sessionStorage && details.failed === 0 ) {
+ for ( i = 0; i < sessionStorage.length; i++ ) {
+ key = sessionStorage.key( i++ );
+ if ( key.indexOf( "qunit-test-" ) === 0 ) {
+ sessionStorage.removeItem( key );
+ }
+ }
+ }
+
+ // scroll back to top to show results
+ if ( config.scrolltop && window.scrollTo ) {
+ window.scrollTo( 0, 0 );
+ }
+});
+
+function getNameHtml( name, module ) {
+ var nameHtml = "";
+
+ if ( module ) {
+ nameHtml = "<span class='module-name'>" + escapeText( module ) + "</span>: ";
+ }
+
+ nameHtml += "<span class='test-name'>" + escapeText( name ) + "</span>";
+
+ return nameHtml;
+}
+
+QUnit.testStart(function( details ) {
+ var running, testBlock;
+
+ testBlock = id( "qunit-test-output-" + details.testId );
+ if ( testBlock ) {
+ testBlock.className = "running";
+ } else {
+
+ // Report later registered tests
+ appendTest( details.name, details.testId, details.module );
+ }
+
+ running = id( "qunit-testresult" );
+ if ( running ) {
+ running.innerHTML = "Running: <br />" + getNameHtml( details.name, details.module );
+ }
+
+});
+
+QUnit.log(function( details ) {
+ var assertList, assertLi,
+ message, expected, actual,
+ testItem = id( "qunit-test-output-" + details.testId );
+
+ if ( !testItem ) {
+ return;
+ }
+
+ message = escapeText( details.message ) || ( details.result ? "okay" : "failed" );
+ message = "<span class='test-message'>" + message + "</span>";
+ message += "<span class='runtime'>@ " + details.runtime + " ms</span>";
+
+ // pushFailure doesn't provide details.expected
+ // when it calls, it's implicit to also not show expected and diff stuff
+ // Also, we need to check details.expected existence, as it can exist and be undefined
+ if ( !details.result && hasOwn.call( details, "expected" ) ) {
+ expected = escapeText( QUnit.dump.parse( details.expected ) );
+ actual = escapeText( QUnit.dump.parse( details.actual ) );
+ message += "<table><tr class='test-expected'><th>Expected: </th><td><pre>" +
+ expected +
+ "</pre></td></tr>";
+
+ if ( actual !== expected ) {
+ message += "<tr class='test-actual'><th>Result: </th><td><pre>" +
+ actual + "</pre></td></tr>" +
+ "<tr class='test-diff'><th>Diff: </th><td><pre>" +
+ QUnit.diff( expected, actual ) + "</pre></td></tr>";
+ }
+
+ if ( details.source ) {
+ message += "<tr class='test-source'><th>Source: </th><td><pre>" +
+ escapeText( details.source ) + "</pre></td></tr>";
+ }
+
+ message += "</table>";
+
+ // this occours when pushFailure is set and we have an extracted stack trace
+ } else if ( !details.result && details.source ) {
+ message += "<table>" +
+ "<tr class='test-source'><th>Source: </th><td><pre>" +
+ escapeText( details.source ) + "</pre></td></tr>" +
+ "</table>";
+ }
+
+ assertList = testItem.getElementsByTagName( "ol" )[ 0 ];
+
+ assertLi = document.createElement( "li" );
+ assertLi.className = details.result ? "pass" : "fail";
+ assertLi.innerHTML = message;
+ assertList.appendChild( assertLi );
+});
+
+QUnit.testDone(function( details ) {
+ var testTitle, time, testItem, assertList,
+ good, bad, testCounts, skipped,
+ tests = id( "qunit-tests" );
+
+ if ( !tests ) {
+ return;
+ }
+
+ testItem = id( "qunit-test-output-" + details.testId );
+
+ assertList = testItem.getElementsByTagName( "ol" )[ 0 ];
+
+ good = details.passed;
+ bad = details.failed;
+
+ // store result when possible
+ if ( config.reorder && defined.sessionStorage ) {
+ if ( bad ) {
+ sessionStorage.setItem( "qunit-test-" + details.module + "-" + details.name, bad );
+ } else {
+ sessionStorage.removeItem( "qunit-test-" + details.module + "-" + details.name );
+ }
+ }
+
+ if ( bad === 0 ) {
+ addClass( assertList, "qunit-collapsed" );
+ }
+
+ // testItem.firstChild is the test name
+ testTitle = testItem.firstChild;
+
+ testCounts = bad ?
+ "<b class='failed'>" + bad + "</b>, " + "<b class='passed'>" + good + "</b>, " :
+ "";
+
+ testTitle.innerHTML += " <b class='counts'>(" + testCounts +
+ details.assertions.length + ")</b>";
+
+ if ( details.skipped ) {
+ testItem.className = "skipped";
+ skipped = document.createElement( "em" );
+ skipped.className = "qunit-skipped-label";
+ skipped.innerHTML = "skipped";
+ testItem.insertBefore( skipped, testTitle );
+ } else {
+ addEvent( testTitle, "click", function() {
+ toggleClass( assertList, "qunit-collapsed" );
+ });
+
+ testItem.className = bad ? "fail" : "pass";
+
+ time = document.createElement( "span" );
+ time.className = "runtime";
+ time.innerHTML = details.runtime + " ms";
+ testItem.insertBefore( time, assertList );
+ }
+});
+
+if ( !defined.document || document.readyState === "complete" ) {
+ config.pageLoaded = true;
+ config.autorun = true;
+}
+
+if ( defined.document ) {
+ addEvent( window, "load", QUnit.load );
+}
+
+})();
return str.charAt( 0 ).toUpperCase() + str.slice( 1 );
},
escapeRE: function ( str ) {
- return str.replace ( /([\\{}()|.?*+\-\^$\[\]])/g, '\\$1' );
+ return str.replace( /([\\{}()|.?*+\-\^$\[\]])/g, '\\$1' );
},
isDomElement: function ( el ) {
return !!el && !!el.nodeType;
},
isEmpty: function ( v ) {
var key;
- if ( v === '' || v === 0 || v === '0' || v === null
- || v === false || v === undefined )
- {
+ if (
+ v === '' || v === 0 || v === '0' || v === null || v === false || v === undefined
+ ) {
return true;
}
// the for-loop could potentially contain prototypes
* @version 2.1.0
* @license MIT
*/
-(function ($) {
+( function ($) {
var isInputSupported = 'placeholder' in document.createElement('input'),
isTextareaSupported = 'placeholder' in document.createElement('textarea'),
$this
.filter((isInputSupported ? 'textarea' : ':input') + '[placeholder]')
- .filter(function () {
+ .filter( function () {
return !$(this).data('placeholder-enabled');
})
.bind({
propHooks.value = hooks;
}
- $(function () {
+ $( function () {
// Look for forms
$(document).delegate('form', 'submit.placeholder', function () {
// Clear the placeholder values so they don't get submitted
var $inputs = $('.placeholder', this).each(clearPlaceholder);
- setTimeout(function () {
+ setTimeout( function () {
$inputs.each(setPlaceholder);
}, 10);
});
// Clear placeholder values upon page reload
$(window).bind('beforeunload.placeholder', function () {
- $('.placeholder').each(function () {
+ $('.placeholder').each( function () {
this.value = '';
});
});
endPos = this.selectionEnd;
scrollTop = this.scrollTop;
checkSelectedText();
- if ( options.selectionStart !== undefined
- && endPos - startPos !== options.selectionEnd - options.selectionStart )
- {
+ if (
+ options.selectionStart !== undefined &&
+ endPos - startPos !== options.selectionEnd - options.selectionStart
+ ) {
// This means there is a difference in the selection range returned by browser and what we passed.
// This happens for Chrome in the case of composite characters. Ref bug #30130
// Set the startPos to the correct position.
mw.cookie.set( cookieKey, null );
}
-} ( mediaWiki, jQuery ) );
+}( mediaWiki, jQuery ) );
* @return {jQuery.Promise} See mw.Api#post
*/
login: function ( username, password ) {
- var params, request,
- deferred = $.Deferred(),
+ var params, apiPromise,
api = this;
params = {
lgpassword: password
};
- request = api.post( params );
- request.fail( deferred.reject );
- request.done( function ( data ) {
- params.lgtoken = data.login.token;
- api.post( params )
- .fail( deferred.reject )
- .done( function ( data ) {
- var code;
- if ( data.login && data.login.result === 'Success' ) {
- deferred.resolve( data );
- } else {
- // Set proper error code whenever possible
- code = data.error && data.error.code || 'unknown';
- deferred.reject( code, data );
- }
- } );
- } );
-
- return deferred.promise( { abort: request.abort } );
+ apiPromise = api.post( params );
+ return apiPromise
+ .then( function ( data ) {
+ params.lgtoken = data.login.token;
+ return api.post( params )
+ .then( function ( data ) {
+ var code;
+ if ( data.login.result !== 'Success' ) {
+ // Set proper error code whenever possible
+ code = data.error && data.error.code || 'unknown';
+ return $.Deferred().reject( code, data );
+ }
+ return data;
+ } );
+ } )
+ .promise( { abort: apiPromise.abort } );
}
} );
$( '#mw-pl-options-2' ).prop( 'checked', true );
} );
} );
-} ( jQuery ) );
+}( jQuery ) );
}
};
-} ( mediaWiki, jQuery ) );
+}( mediaWiki, jQuery ) );
// Attach to window and globally alias
window.mw = window.mediaWiki = mw;
-
- // Auto-register from pre-loaded startup scripts
- if ( $.isFunction( window.startUp ) ) {
- window.startUp();
- window.startUp = undefined;
- }
-
}( jQuery ) );
--- /dev/null
+/*!
+ * Auto-register from pre-loaded startup scripts
+ */
+( function ( $ ) {
+ 'use strict';
+
+ if ( $.isFunction( window.startUp ) ) {
+ window.startUp();
+ window.startUp = undefined;
+ }
+}( jQuery ) );
var registration = mw.config.get( 'wgUserRegistration' );
if ( user.isAnon() ) {
return false;
- } else if ( registration === null ) {
+ }
+ if ( registration === null ) {
// Information may not be available if they signed up before
// MW began storing this.
return null;
- } else {
- return new Date( registration );
}
+ return new Date( registration );
},
/**
/**
* Get the link to a page name (relative to `wgServer`),
*
- * @param {string} str Page name
+ * @param {string|null} [str=wgPageName] Page name
* @param {Object} [params] A mapping of query parameter names to values,
* e.g. `{ action: 'edit' }`
* @return {string} Url of the page with name of `str`
$this->checkDbIsSupported();
if ( !self::$dbSetup ) {
- wfProfileIn( $logName . ' (clone-db)' );
-
// switch to a temporary clone of the database
self::setupTestDB( $this->db, $this->dbPrefix() );
if ( ( $this->db->getType() == 'oracle' || !self::$useTemporaryTables ) && self::$reuseDB ) {
$this->resetDB();
}
-
- wfProfileOut( $logName . ' (clone-db)' );
}
-
- wfProfileIn( $logName . ' (prepare-db)' );
$this->addCoreDBData();
$this->addDBData();
- wfProfileOut( $logName . ' (prepare-db)' );
-
$needsResetDB = true;
}
- wfProfileIn( $logName );
parent::run( $result );
- wfProfileOut( $logName );
if ( $needsResetDB ) {
- wfProfileIn( $logName . ' (reset-db)' );
$this->resetDB();
- wfProfileOut( $logName . ' (reset-db)' );
}
}
}
class MediaWikiPHPUnitBootstrap {
-
- public function __construct() {
- wfProfileIn( __CLASS__ );
- }
-
public function __destruct() {
- wfProfileOut( __CLASS__ );
-
// Return to real wiki db, so profiling data is preserved
MediaWikiTestCase::teardownTestDB();
public function testIsGood( $ok, $errors, $expected ) {
$status = new Status();
$status->ok = $ok;
- $status->errors = $errors;
+ foreach ( $errors as $error ) {
+ $status->warning( $error );
+ }
$this->assertEquals( $expected, $status->isGood() );
}
}
}
+ /**
+ * @covers ExtensionProcessor::extractResourceLoaderModules
+ * @dataProvider provideExtractResourceLoaderModules
+ */
+ public function testExtractResourceLoaderModules( $input, $expected ) {
+ $processor = new ExtensionProcessor();
+ $processor->extractInfo( $this->dir, $input + self::$default );
+ $out = $processor->getExtractedInfo();
+ foreach ( $expected as $key => $value ) {
+ $this->assertEquals( $value, $out['globals'][$key] );
+ }
+ }
+
+ public static function provideExtractResourceLoaderModules() {
+ $dir = __DIR__ . '/FooBar/';
+ return array(
+ // Generic module with localBasePath/remoteExtPath specified
+ array(
+ // Input
+ array(
+ 'ResourceModules' => array(
+ 'test.foo' => array(
+ 'styles' => 'foobar.js',
+ 'localBasePath' => '',
+ 'remoteExtPath' => 'FooBar',
+ ),
+ ),
+ ),
+ // Expected
+ array(
+ 'wgResourceModules' => array(
+ 'test.foo' => array(
+ 'styles' => 'foobar.js',
+ 'localBasePath' => $dir,
+ 'remoteExtPath' => 'FooBar',
+ ),
+ ),
+ ),
+ ),
+ // ResourceFileModulePaths specified:
+ array(
+ // Input
+ array(
+ 'ResourceFileModulePaths' => array(
+ 'localBasePath' => '',
+ 'remoteExtPath' => 'FooBar',
+ ),
+ 'ResourceModules' => array(
+ // No paths
+ 'test.foo' => array(
+ 'styles' => 'foo.js',
+ ),
+ // Different paths set
+ 'test.bar' => array(
+ 'styles' => 'bar.js',
+ 'localBasePath' => 'subdir',
+ 'remoteExtPath' => 'FooBar/subdir',
+ ),
+ // Custom class with no paths set
+ 'test.class' => array(
+ 'class' => 'FooBarModule',
+ 'extra' => 'argument',
+ ),
+ // Custom class with a localBasePath
+ 'test.class.with.path' => array(
+ 'class' => 'FooBarPathModule',
+ 'extra' => 'argument',
+ 'localBasePath' => '',
+ )
+ ),
+ ),
+ // Expected
+ array(
+ 'wgResourceModules' => array(
+ 'test.foo' => array(
+ 'styles' => 'foo.js',
+ 'localBasePath' => $dir,
+ 'remoteExtPath' => 'FooBar',
+ ),
+ 'test.bar' => array(
+ 'styles' => 'bar.js',
+ 'localBasePath' => $dir . 'subdir',
+ 'remoteExtPath' => 'FooBar/subdir',
+ ),
+ 'test.class' => array(
+ 'class' => 'FooBarModule',
+ 'extra' => 'argument',
+ 'localBasePath' => $dir,
+ 'remoteExtPath' => 'FooBar',
+ ),
+ 'test.class.with.path' => array(
+ 'class' => 'FooBarPathModule',
+ 'extra' => 'argument',
+ 'localBasePath' => $dir,
+ 'remoteExtPath' => 'FooBar',
+ )
+ ),
+ ),
+ ),
+ );
+ }
+
public static function provideSetToGlobal() {
return array(
array(
} );
QUnit.test( 'updateTooltipAccessKeys - current browser', 2, function ( assert ) {
- var title = $( makeInput ( 'Title', 'a' ) ).updateTooltipAccessKeys().prop( 'title' ),
+ var title = $( makeInput( 'Title', 'a' ) ).updateTooltipAccessKeys().prop( 'title' ),
//The new title should be something like "Title [alt-a]", but the exact label will depend on the browser.
//The "a" could be capitalized, and the prefix could be anything, e.g. a simple "^" for ctrl-
//(no browser is known using such a short prefix, though) or "Alt+Umschalt+" in German Firefox.
-(function ($) {
+( function ($) {
QUnit.module('jquery.placeholder', QUnit.newMwEnvironment());
QUnit.test('caches results of feature tests', 2, function (assert) {
- assert.strictEqual(typeof $.fn.placeholder.input, 'boolean', '$.fn.placeholder.input');
- assert.strictEqual(typeof $.fn.placeholder.textarea, 'boolean', '$.fn.placeholder.textarea');
+ assert.strictEqual( typeof $.fn.placeholder.input, 'boolean', '$.fn.placeholder.input');
+ assert.strictEqual( typeof $.fn.placeholder.textarea, 'boolean', '$.fn.placeholder.textarea');
});
if ($.fn.placeholder.input && $.fn.placeholder.textarea) {
only: 'scripts'
},
dataType: 'script'
- } ).done(function () {
+ } ).done( function () {
mwLanguageCache[langCode].fire( mw.language );
} ).fail( function () {
mwLanguageCache[langCode].fire( false );
} );
} );
- QUnit.test( 'getUrl', 4, function ( assert ) {
+ QUnit.test( 'getUrl', 5, function ( assert ) {
// Not part of startUp module
mw.config.set( 'wgArticlePath', '/wiki/$1' );
mw.config.set( 'wgPageName', 'Foobar' );
var href = mw.util.getUrl( 'Sandbox' );
- assert.equal( href, '/wiki/Sandbox', 'Simple title; Get link for "Sandbox"' );
+ assert.equal( href, '/wiki/Sandbox', 'simple title' );
- href = mw.util.getUrl( 'Foo:Sandbox ? 5+5=10 ! (test)/subpage' );
- assert.equal( href, '/wiki/Foo:Sandbox_%3F_5%2B5%3D10_!_(test)/subpage',
- 'Advanced title; Get link for "Foo:Sandbox ? 5+5=10 ! (test)/subpage"' );
+ href = mw.util.getUrl( 'Foo:Sandbox? 5+5=10! (test)/sub ' );
+ assert.equal( href, '/wiki/Foo:Sandbox%3F_5%2B5%3D10!_(test)/sub_', 'advanced title' );
href = mw.util.getUrl();
- assert.equal( href, '/wiki/Foobar', 'Default title; Get link for current page ("Foobar")' );
+ assert.equal( href, '/wiki/Foobar', 'default title' );
+
+ href = mw.util.getUrl( null, { action: 'edit' } );
+ assert.equal( href, '/wiki/Foobar?action=edit', 'default title with query string' );
href = mw.util.getUrl( 'Sandbox', { action: 'edit' } );
- assert.equal( href, '/wiki/Sandbox?action=edit',
- 'Simple title with query string; Get link for "Sandbox" with action=edit' );
+ assert.equal( href, '/wiki/Sandbox?action=edit', 'simple title with query string' );
} );
QUnit.test( 'wikiScript', 4, function ( assert ) {
try {
$thumbName = $img->thumbName( $params );
if ( !strlen( $thumbName ) ) { // invalid params?
- wfThumbError( 400, 'The specified thumbnail parameters are not valid.' );
- return;
+ throw new MediaTransformInvalidParametersException( 'Empty return from File::thumbName' );
}
$thumbName2 = $img->thumbName( $params, File::THUMB_FULL_NAME ); // b/c; "long" style
+ } catch ( MediaTransformInvalidParametersException $e ) {
+ wfThumbError( 400, 'The specified thumbnail parameters are not valid: ' . $e->getMessage() );
+ return;
} catch ( MWException $e ) {
wfThumbError( 500, $e->getHTML() );
return;
// Check for thumbnail generation errors...
$msg = wfMessage( 'thumbnail_error' );
+ $errorCode = 500;
if ( !$thumb ) {
$errorMsg = $errorMsg ?: $msg->rawParams( 'File::transform() returned false' )->escaped();
} elseif ( $thumb->isError() ) {
} elseif ( $thumb->fileIsSource() ) {
$errorMsg = $msg->
rawParams( 'Image was not scaled, is the requested width bigger than the source?' )->escaped();
+ $errorCode = 400;
}
if ( $errorMsg !== false ) {
- wfThumbError( 500, $errorMsg );
+ wfThumbError( $errorCode, $errorMsg );
} else {
// Stream the file if there were no errors
$thumb->streamFile( $headers );
header( 'Cache-Control: no-cache' );
header( 'Content-Type: text/html; charset=utf-8' );
- if ( $status == 404 ) {
+ if ( $status == 400 ) {
+ header( 'HTTP/1.1 400 Bad request' );
+ } elseif ( $status == 404 ) {
header( 'HTTP/1.1 404 Not found' );
} elseif ( $status == 403 ) {
header( 'HTTP/1.1 403 Forbidden' );