resources/jquery/jquery.hoverIntent.js
resources/jquery/jquery.js
resources/jquery/jquery.json.js
+resources/jquery/jquery.jStorage.js
resources/jquery/jquery.mockjax.js
resources/jquery/jquery.qunit.js
resources/jquery/jquery.validate.js
* (bug 35685) api.php URL and other entry point URLs are now listed on
Special:Version
* Edit notices can now be translated.
-* jQuery upgraded to 1.8.
+* (bug 35680) jQuery upgraded to 1.7.2.
* jQuery UI upgraded to 1.8.22.
* (bug 35705) QUnit upgraded from v1.2.0 to v1.8.0.
* (bug 37604) jquery.cookie upgraded to 2011 version.
* (bug 39376) jquery.form upgraded to 3.14
* SVG files will now show the actual width in the SVG's specified units
in the metadata box.
+* Added ResourceLoader module "jquery.jStorage".
=== Bug fixes in 1.20 ===
* (bug 30245) Use the correct way to construct a log page title.
// Bail if PHP is too low
if ( !function_exists( 'version_compare' ) || version_compare( phpversion(), '5.3.2' ) < 0 ) {
+ // We need to use dirname( __FILE__ ) here cause __DIR__ is PHP5.3+
require( dirname( __FILE__ ) . '/includes/PHPVersionError.php' );
wfPHPVersionError( 'api.php' );
}
refers to the link source, NOT to the target! You should check the env
variable MW_INSTALL_PATH in case the extension is not in the default location.
-The following code snippet lets you override the default path:
+The following code snippet lets you override the default path:
$IP = getenv( 'MW_INSTALL_PATH' );
if( $IP === false ) {
'JavascriptContentTest' => 'tests/phpunit/includes/JavascriptContentTest.php',
'DummyContentHandlerForTesting' => 'tests/phpunit/includes/ContentHandlerTest.php',
'DummyContentForTesting' => 'tests/phpunit/includes/ContentHandlerTest.php',
-
# tests/phpunit/includes
'GenericArrayObjectTest' => 'tests/phpunit/includes/libs/GenericArrayObjectTest.php',
# Split into three columns
$columns = array_chunk( $columns, ceil( count( $columns ) / 3 ), true /* preserve keys */ );
- $ret = '<table width="100%"><tr valign="top">';
+ $ret = '<table width="100%"><tr style="vertical-align: top;">';
$prevchar = null;
foreach ( $columns as $column ) {
$exception = null;
/**
- * @var $update StorageUpdate
- * @var $trans StorageUpdate
+ * @var $update DataUpdate
+ * @var $trans DataUpdate
*/
try {
// Create a link to the new section from the edit summary.
$cleanSummary = $wgParser->stripSectionName( $this->summary );
- $this->summary = wfMessage( 'newsectionsummary', $cleanSummary )->inContentLanguage()->text() ;
+ $this->summary = wfMessage( 'newsectionsummary', $cleanSummary )
+ ->inContentLanguage()->text();
}
}
);
}
}
+ # Add header copyright warning
+ $this->showHeaderCopyrightWarning();
}
+
/**
* Standard summary input and label (wgSummary), abstracted so EditPage
* subclasses may reorganize the form.
$wgOut->addHTML( '<div id="wikiDiff">' . $difftext . '</div>' );
}
+ /**
+ * Show the header copyright warning.
+ */
+ protected function showHeaderCopyrightWarning() {
+ $msg = 'editpage-head-copy-warn';
+ if ( !wfMessage( $msg )->isDisabled() ) {
+ global $wgOut;
+ $wgOut->wrapWikiMsg( "<div class='editpage-head-copywarn'>\n$1\n</div>",
+ 'editpage-head-copy-warn' );
+ }
+ }
+
/**
* Give a chance for site and per-namespace customizations of
* terms of service summary link that might exist separately
}
$date = $d->format( 'D M j G:i:s T Y' );
+ $date = $d->format( 'D M j G:i:s T Y' );
+
$text = "$date\t$host\t$wiki\t$text";
wfErrorLog( $text, $wgDBerrorLog );
}
* @return null
*/
function wfDeprecated( $function, $version = false, $component = false, $callerOffset = 2 ) {
- static $functionsWarned = array();
-
- MWDebug::deprecated( $function, $version, $component );
-
- if ( !isset( $functionsWarned[$function] ) ) {
- $functionsWarned[$function] = true;
-
- if ( $version ) {
- global $wgDeprecationReleaseLimit;
-
- if ( $wgDeprecationReleaseLimit && $component === false ) {
- # Strip -* off the end of $version so that branches can use the
- # format #.##-branchname to avoid issues if the branch is merged into
- # a version of MediaWiki later than what it was branched from
- $comparableVersion = preg_replace( '/-.*$/', '', $version );
-
- # If the comparableVersion is larger than our release limit then
- # skip the warning message for the deprecation
- if ( version_compare( $wgDeprecationReleaseLimit, $comparableVersion, '<' ) ) {
- return;
- }
- }
-
- $component = $component === false ? 'MediaWiki' : $component;
- wfWarn( "Use of $function was deprecated in $component $version.", $callerOffset );
- } else {
- wfWarn( "Use of $function is deprecated.", $callerOffset );
- }
- }
+ MWDebug::deprecated( $function, $version, $component, $callerOffset + 1 );
}
/**
* is true
*/
function wfWarn( $msg, $callerOffset = 1, $level = E_USER_NOTICE ) {
- global $wgDevelopmentWarnings;
-
- MWDebug::warning( $msg, $callerOffset + 2 );
-
- $callers = wfDebugBacktrace();
- if ( isset( $callers[$callerOffset + 1] ) ) {
- $callerfunc = $callers[$callerOffset + 1];
- $callerfile = $callers[$callerOffset];
- if ( isset( $callerfile['file'] ) && isset( $callerfile['line'] ) ) {
- $file = $callerfile['file'] . ' at line ' . $callerfile['line'];
- } else {
- $file = '(internal function)';
- }
- $func = '';
- if ( isset( $callerfunc['class'] ) ) {
- $func .= $callerfunc['class'] . '::';
- }
- if ( isset( $callerfunc['function'] ) ) {
- $func .= $callerfunc['function'];
- }
- $msg .= " [Called from $func in $file]";
- }
-
- if ( $wgDevelopmentWarnings ) {
- trigger_error( $msg, $level );
- } else {
- wfDebug( "$msg\n" );
- }
+ MWDebug::warning( $msg, $callerOffset + 1, $level );
}
/**
}
} else {
if ( !$suppressCount ) {
- // E_DEPRECATED is undefined in PHP 5.2
- if( !defined( 'E_DEPRECATED' ) ) {
- define( 'E_DEPRECATED', 8192 );
- }
- $originalLevel = error_reporting( E_ALL & ~( E_WARNING | E_NOTICE | E_USER_WARNING | E_USER_NOTICE | E_DEPRECATED ) );
+ $originalLevel = error_reporting( E_ALL & ~( E_WARNING | E_NOTICE | E_USER_WARNING | E_USER_NOTICE | E_DEPRECATED | E_USER_DEPRECATED ) );
}
++$suppressCount;
}
*/
function logoText( $align = '' ) {
if ( $align != '' ) {
- $a = " align='{$align}'";
+ $a = " style='float: {$align};'";
} else {
$a = '';
}
$ntl = '';
if ( count( $newtalks ) == 1 && $newtalks[0]['wiki'] === wfWikiID() ) {
- $userTalkTitle = $this->getUser()->getTalkPage();
+ $uTalkTitle = $this->getUser()->getTalkPage();
- if ( !$userTalkTitle->equals( $out->getTitle() ) ) {
+ if ( !$uTalkTitle->equals( $out->getTitle() ) ) {
$lastSeenRev = isset( $newtalks[0]['rev'] ) ? $newtalks[0]['rev'] : null;
$nofAuthors = 0;
if ( $lastSeenRev !== null ) {
$plural = true; // Default if we have a last seen revision: if unknown, use plural
- $latestRev = Revision::newFromTitle ($userTalkTitle);
+ $latestRev = Revision::newFromTitle( $uTalkTitle, false, Revision::READ_NORMAL );
if ( $latestRev !== null ) {
// Singular if only 1 unseen revision, plural if several unseen revisions.
$plural = $latestRev->getParentId() !== $lastSeenRev->getId();
- $nofAuthors = $userTalkTitle->countAuthorsBetween( $lastSeenRev, $latestRev, 10, 'include_new' );
+ $nofAuthors = $uTalkTitle->countAuthorsBetween(
+ $lastSeenRev, $latestRev, 10, 'include_new' );
}
} else {
// Singular if no revision -> diff link will show latest change only in any case
// the number of revisions or authors is not necessarily the same as the number of
// "messages".
$newMessagesLink = Linker::linkKnown(
- $userTalkTitle,
+ $uTalkTitle,
$this->msg( 'newmessageslinkplural' )->params( $plural )->escaped(),
array(),
array( 'redirect' => 'no' )
);
$newMessagesDiffLink = Linker::linkKnown(
- $userTalkTitle,
+ $uTalkTitle,
$this->msg( 'newmessagesdifflinkplural' )->params( $plural )->escaped(),
array(),
$lastSeenRev !== null
"<table border='0' cellspacing='0' width='100%'>\n<tr>\n";
if ( $this->getSkin()->qbSetting() == 0 ) {
- $s .= "<td class='top' align='left' valign='top' rowspan='{$rows}'>\n" .
+ $s .= "<td class='top' style='text-align: left; vertical-align: top;' rowspan='{$rows}'>\n" .
$this->getSkin()->logoText( $wgLang->alignStart() ) . '</td>';
}
$l = $wgLang->alignStart();
- $s .= "<td {$borderhack} align='$l' valign='top'>\n";
+ $s .= "<td {$borderhack} style='text-align: $l; vertical-align: top;'>\n";
$s .= $this->topLinks();
$s .= '<p class="subtitle">' . $this->pageTitleLinks() . "</p>\n";
$r = $wgLang->alignEnd();
- $s .= "</td>\n<td {$borderhack} valign='top' align='$r' nowrap='nowrap'>";
+ $s .= "</td>\n<td {$borderhack} style='text-align: $r; vertical-align: top;' nowrap='nowrap'>";
$s .= $this->nameAndLogin();
$s .= "\n<br />" . $this->searchForm() . '</td>';
return ( $old_cmp === '>' && $new_cmp === '<' ) ? 0 : 1;
}
return ( $old->getRawUserText() === $new->getRawUserText() ) ? 1 : 2;
- }
+ }\r
$dbr = wfGetDB( DB_SLAVE );
$res = $dbr->select( 'revision', 'DISTINCT rev_user_text',
array(
* Adds a warning entry to the log
*
* @since 1.19
- * @param $msg
- * @param int $callerOffset
+ * @param $msg string
+ * @param $callerOffset int
* @return mixed
*/
- public static function warning( $msg, $callerOffset = 1 ) {
- if ( !self::$enabled ) {
- return;
- }
+ public static function warning( $msg, $callerOffset = 1, $level = E_USER_NOTICE ) {
+ $callerDescription = self::getCallerDescription( $callerOffset );
- // Check to see if there was already a deprecation notice, so not to
- // get a duplicate warning
- $logCount = count( self::$log );
- $caller = wfGetCaller( $callerOffset + 1 );
- if ( $logCount ) {
- $lastLog = self::$log[ $logCount - 1 ];
- if ( $lastLog['type'] == 'deprecated' && $lastLog['caller'] == $caller ) {
- return;
- }
- }
+ self::sendWarning( $msg, $callerDescription, $level );
- self::$log[] = array(
- 'msg' => htmlspecialchars( $msg ),
- 'type' => 'warn',
- 'caller' => $caller,
- );
+ if ( self::$enabled ) {
+ self::$log[] = array(
+ 'msg' => htmlspecialchars( $msg ),
+ 'type' => 'warn',
+ 'caller' => $callerDescription['func'],
+ );
+ }
}
/**
- * Adds a depreciation entry to the log, along with a backtrace
+ * Show a warning that $function is deprecated.
+ * This will send it to the following locations:
+ * - Debug toolbar, with one item per function and caller, if $wgDebugToolbar
+ * is set to true.
+ * - PHP's error log, with level E_USER_DEPRECATED, if $wgDevelopmentWarnings
+ * is set to true.
+ * - MediaWiki's debug log, if $wgDevelopmentWarnings is set to false.
*
* @since 1.19
- * @param $function
- * @param $version
- * @param $component
+ * @param $function string: Function that is deprecated.
+ * @param $version string|bool: Version in which the function was deprecated.
+ * @param $component string|bool: Component to which the function belongs.
+ * If false, it is assumbed the function is in MediaWiki core.
+ * @param $callerOffset integer: How far up the callstack is the original
+ * caller. 2 = function that called the function that called
+ * MWDebug::deprecated() (Added in 1.20).
* @return mixed
*/
- public static function deprecated( $function, $version, $component ) {
- if ( !self::$enabled ) {
- return;
- }
+ public static function deprecated( $function, $version = false, $component = false, $callerOffset = 2 ) {
+ $callerDescription = self::getCallerDescription( $callerOffset );
+ $callerFunc = $callerDescription['func'];
- // Chain: This function -> wfDeprecated -> deprecatedFunction -> caller
- $caller = wfGetCaller( 4 );
+ $sendToLog = true;
// Check to see if there already was a warning about this function
- $functionString = "$function-$caller";
- if ( in_array( $functionString, self::$deprecationWarnings ) ) {
+ if ( isset( self::$deprecationWarnings[$function][$callerFunc] ) ) {
return;
+ } elseif ( isset( self::$deprecationWarnings[$function] ) ) {
+ if ( self::$enabled ) {
+ $sendToLog = false;
+ } else {
+ return;
+ }
}
- $version = $version === false ? '(unknown version)' : $version;
- $component = $component === false ? 'MediaWiki' : $component;
- $msg = htmlspecialchars( "Use of function $function was deprecated in $component $version" );
- $msg .= Html::rawElement( 'div', array( 'class' => 'mw-debug-backtrace' ),
- Html::element( 'span', array(), 'Backtrace:' )
- . wfBacktrace()
- );
+ self::$deprecationWarnings[$function][$callerFunc] = true;
- self::$deprecationWarnings[] = $functionString;
- self::$log[] = array(
- 'msg' => $msg,
- 'type' => 'deprecated',
- 'caller' => $caller,
- );
+ if ( $version ) {
+ global $wgDeprecationReleaseLimit;
+ if ( $wgDeprecationReleaseLimit && $component === false ) {
+ # Strip -* off the end of $version so that branches can use the
+ # format #.##-branchname to avoid issues if the branch is merged into
+ # a version of MediaWiki later than what it was branched from
+ $comparableVersion = preg_replace( '/-.*$/', '', $version );
+
+ # If the comparableVersion is larger than our release limit then
+ # skip the warning message for the deprecation
+ if ( version_compare( $wgDeprecationReleaseLimit, $comparableVersion, '<' ) ) {
+ $sendToLog = false;
+ }
+ }
+
+ $component = $component === false ? 'MediaWiki' : $component;
+ $msg = "Use of $function was deprecated in $component $version.";
+ } else {
+ $msg = "Use of $function is deprecated.";
+ }
+
+ if ( $sendToLog ) {
+ self::sendWarning( $msg, $callerDescription, E_USER_DEPRECATED );
+ }
+
+ if ( self::$enabled ) {
+ $logMsg = htmlspecialchars( $msg ) .
+ Html::rawElement( 'div', array( 'class' => 'mw-debug-backtrace' ),
+ Html::element( 'span', array(), 'Backtrace:' ) . wfBacktrace()
+ );
+
+ self::$log[] = array(
+ 'msg' => $logMsg,
+ 'type' => 'deprecated',
+ 'caller' => $callerFunc,
+ );
+ }
+ }
+
+ /**
+ * Get an array describing the calling function at a specified offset.
+ *
+ * @param $callerOffset integer: How far up the callstack is the original
+ * caller. 0 = function that called getCallerDescription()
+ * @return array with two keys: 'file' and 'func'
+ */
+ private static function getCallerDescription( $callerOffset ) {
+ $callers = wfDebugBacktrace();
+
+ if ( isset( $callers[$callerOffset] ) ) {
+ $callerfile = $callers[$callerOffset];
+ if ( isset( $callerfile['file'] ) && isset( $callerfile['line'] ) ) {
+ $file = $callerfile['file'] . ' at line ' . $callerfile['line'];
+ } else {
+ $file = '(internal function)';
+ }
+ } else {
+ $file = '(unknown location)';
+ }
+
+ if ( isset( $callers[$callerOffset + 1] ) ) {
+ $callerfunc = $callers[$callerOffset + 1];
+ $func = '';
+ if ( isset( $callerfunc['class'] ) ) {
+ $func .= $callerfunc['class'] . '::';
+ }
+ if ( isset( $callerfunc['function'] ) ) {
+ $func .= $callerfunc['function'];
+ }
+ } else {
+ $func = 'unknown';
+ }
+
+ return array( 'file' => $file, 'func' => $func );
+ }
+
+ /**
+ * Send a warning either to the debug log or by triggering an user PHP
+ * error depending on $wgDevelopmentWarnings.
+ *
+ * @param $msg string Message to send
+ * @param $caller array caller description get from getCallerDescription()
+ * @param $level error level to use if $wgDevelopmentWarnings is true
+ */
+ private static function sendWarning( $msg, $caller, $level ) {
+ global $wgDevelopmentWarnings;
+
+ $msg .= ' [Called from ' . $caller['func'] . ' in ' . $caller['file'] . ']';
+
+ if ( $wgDevelopmentWarnings ) {
+ trigger_error( $msg, $level );
+ } else {
+ wfDebug( "$msg\n" );
+ }
}
/**
if ( !$diff && !$otitle ) {
$header .= "
- <tr valign='top'>
+ <tr style='vertical-align: top;'>
<td class='diff-ntitle'>{$ntitle}</td>
</tr>";
$multiColspan = 1;
$multiColspan = 2;
}
$header .= "
- <tr valign='top'>
+ <tr style='vertical-align: top;'>
<td colspan='$colspan' class='diff-otitle'>{$otitle}</td>
<td colspan='$colspan' class='diff-ntitle'>{$ntitle}</td>
</tr>";
}
if ( $multi != '' ) {
- $header .= "<tr><td colspan='{$multiColspan}' align='center' class='diff-multi'>{$multi}</td></tr>";
+ $header .= "<tr><td colspan='{$multiColspan}' style='text-align: center;' class='diff-multi'>{$multi}</td></tr>";
}
if ( $notice != '' ) {
- $header .= "<tr><td colspan='{$multiColspan}' align='center'>{$notice}</td></tr>";
+ $header .= "<tr><td colspan='{$multiColspan}' style='text-align: center;'>{$notice}</td></tr>";
}
return $header . $diff . "</table>";
* - allowStale : Don't require the latest available data.
* This can increase performance for non-critical writes.
* This has no effect unless the 'force' flag is set.
+ * - preserveCache : Don't clear the process cache before checking files.
+ * This should only be used if all entries in the process
+ * cache were added after the files were already locked.
* - nonJournaled : Don't log this operation batch in the file journal.
* This limits the ability of recovery scripts.
* - parallelize : Try to do operations in parallel when possible.
}
/**
- * Invalidate any in-process file existence and property cache.
+ * Preload persistent file stat and property cache into in-process cache.
+ * This should be used when stat calls will be made on a known list of a many files.
+ *
+ * @param $paths Array Storage paths
+ * @return void
+ */
+ public function preloadCache( array $paths ) {}
+
+ /**
+ * Invalidate any in-process file stat and property cache.
* If $paths is given, then only the cache for those files will be cleared.
*
* @param $paths Array Storage paths (optional)
}
// Clear any cache entries (after locks acquired)
$this->clearCache();
+ $opts['preserveCache'] = true; // only locked files are cached
// Do a consistency check to see if the backends agree
$status->merge( $this->consistencyCheck( $this->fileStoragePathsForOps( $ops ) ) );
if ( !$status->isOK() ) {
}
// Clear any file cache entries (after locks acquired)
- $this->clearCache();
+ if ( empty( $opts['preserveCache'] ) ) {
+ $this->clearCache();
+ }
// Load from the persistent file and container caches
$this->primeFileCache( $performOps );
return array();
}
+ /**
+ * @see FileBackend::preloadCache()
+ */
+ final public function preloadCache( array $paths ) {
+ $fullConts = array(); // full container names
+ foreach ( $paths as $path ) {
+ list( $fullCont, $r, $s ) = $this->resolveStoragePath( $path );
+ $fullConts[] = $fullCont;
+ }
+ // Load from the persistent file and container caches
+ $this->primeContainerCache( $fullConts );
+ $this->primeFileCache( $paths );
+ }
+
/**
* @see FileBackend::clearCache()
*/
: false;
$this->swiftCDNExpiry = isset( $config['swiftCDNExpiry'] )
? $config['swiftCDNExpiry']
- : 3600; // hour
+ : 12*3600; // 12 hours is safe (tokens last 24 hours per http://docs.openstack.org)
$this->swiftCDNPurgable = isset( $config['swiftCDNPurgable'] )
? $config['swiftCDNPurgable']
: true;
// See function "create_container_table" in common/db.py.
// If a directory is not "greater" than the last one,
// then it was already listed by the calling iterator.
- if ( $objectDir > $lastDir ) {
+ if ( strcmp( $objectDir, $lastDir ) > 0 ) {
$pDir = $objectDir;
do { // add dir and all its parent dirs
$dirs[] = "{$pDir}/";
$pDir = $this->getParentDir( $pDir );
} while ( $pDir !== false // sanity
- && $pDir > $lastDir // not done already
+ && strcmp( $pDir, $lastDir ) > 0 // not done already
&& strlen( $pDir ) > strlen( $dir ) // within $dir
);
}
if ( $e->getMessage() ) {
trigger_error( "$func: " . $e->getMessage(), E_USER_WARNING );
}
+ if ( $e instanceof InvalidResponseException ) { // possibly a stale token
+ $this->srvCache->delete( $this->getCredsCacheKey( $this->auth->username ) );
+ }
wfDebugLog( 'SwiftBackend',
get_class( $e ) . " in '{$func}' (given '" . FormatJson::encode( $params ) . "')" .
( $e->getMessage() ? ": {$e->getMessage()}" : "" )
* @since 1.20
*/
class DBFileJournal extends FileJournal {
+ /** @var DatabaseBase */
+ protected $dbw;
+
protected $wiki = false; // string; wiki DB name
/**
}
try {
- $dbw->begin();
$dbw->insert( 'filejournal', $data, __METHOD__ );
- $dbw->commit();
} catch ( DBError $e ) {
$status->fatal( 'filejournal-fail-dbquery', $this->backend );
return $status;
$dbw = $this->getMasterDB();
$dbCutoff = $dbw->timestamp( time() - 86400 * $this->ttlDays );
- $dbw->begin();
$dbw->delete( 'filejournal',
array( 'fj_timestamp < ' . $dbw->addQuotes( $dbCutoff ) ),
__METHOD__
);
- $dbw->commit();
return $status;
}
* @throws DBError
*/
protected function getMasterDB() {
- $lb = wfGetLBFactory()->newMainLB();
- return $lb->getConnection( DB_MASTER, array(), $this->wiki );
+ if ( !$this->dbw ) {
+ // Get a separate connection in autocommit mode
+ $lb = wfGetLBFactory()->newMainLB();
+ $this->dbw = $lb->getConnection( DB_MASTER, array(), $this->wiki );
+ $this->dbw->clearFlag( DBO_TRX );
+ }
+ return $this->dbw;
}
}
else {
$this->applyPatch( $patch, $fullpath, $msg );
}
+
} else {
$this->output( "...$table doesn't exist.\n" );
}
$dbw = wfGetDB( DB_MASTER );
$dbw->delete( 'job', array( 'job_id' => $row->job_id ), __METHOD__ );
$affected = $dbw->affectedRows();
- $dbw->commit( __METHOD__ );
if ( !$affected ) {
// Failed, someone else beat us to it
// Delete the random row
$dbw->delete( 'job', array( 'job_id' => $row->job_id ), __METHOD__ );
$affected = $dbw->affectedRows();
- $dbw->commit( __METHOD__ );
if ( !$affected ) {
// Random job gone before we exclusively deleted it
wfGetLB()->waitFor( $this->params['masterPos'] );
}
- $revision = Revision::newFromTitle( $this->title, 0, Revision::READ_NORMAL );
+ $revision = Revision::newFromTitle( $this->title, false, Revision::READ_NORMAL );
if ( !$revision ) {
$this->error = 'refreshLinks: Article not found "' .
$this->title->getPrefixedDBkey() . '"';
}
# Re-parse each page that transcludes this page and update their tracking links...
foreach ( $titles as $title ) {
- $revision = Revision::newFromTitle( $title, 0, Revision::READ_NORMAL );
+ $revision = Revision::newFromTitle( $title, false, Revision::READ_NORMAL );
if ( !$revision ) {
$this->error = 'refreshLinks: Article not found "' .
$title->getPrefixedDBkey() . '"';
* @return string
*/
private static function fixBackgroundPosition( $css ) {
- $css = preg_replace_callback( self::$patterns['bg_horizontal_percentage'],
+ $replaced = preg_replace_callback( self::$patterns['bg_horizontal_percentage'],
array( 'self', 'calculateNewBackgroundPosition' ), $css );
- $css = preg_replace_callback( self::$patterns['bg_horizontal_percentage_x'],
+ if ( $replaced !== null ) {
+ // Check for null; sometimes preg_replace_callback() returns null here for some weird reason
+ $css = $replaced;
+ }
+ $replaced = preg_replace_callback( self::$patterns['bg_horizontal_percentage_x'],
array( 'self', 'calculateNewBackgroundPosition' ), $css );
+ if ( $replaced !== null ) {
+ $css = $replaced;
+ }
return $css;
}
static function plural( $parser, $text = '' ) {
$forms = array_slice( func_get_args(), 2 );
$text = $parser->getFunctionLang()->parseFormattedNumber( $text );
+ settype( $text, ctype_digit( $text ) ? 'int' : 'float' );
return $parser->getFunctionLang()->convertPlural( $text, $forms );
}
if( isset( $cache[$page] ) ) {
$length = $cache[$page];
} elseif( $parser->incrementExpensiveFunctionCount() ) {
- $rev = Revision::newFromTitle( $title );
+ $rev = Revision::newFromTitle( $title, false, Revision::READ_NORMAL );
$id = $rev ? $rev->getPage() : 0;
$length = $cache[$page] = $rev ? $rev->getSize() : 0;
# Get the revision
$rev = $id
? Revision::newFromId( $id )
- : Revision::newFromTitle( $title, 0, Revision::READ_NORMAL );
+ : Revision::newFromTitle( $title, false, Revision::READ_NORMAL );
$rev_id = $rev ? $rev->getId() : 0;
# If there is no current revision, there is no page
if ( $id === false && !$rev ) {
return "<li>" .
'<table class="searchResultImage">' .
'<tr>' .
- '<td width="120" align="center" valign="top">' .
+ '<td width="120" style="text-align: center; vertical-align: top;">' .
$thumb->toHtml( array( 'desc-link' => true ) ) .
'</td>' .
- '<td valign="top">' .
+ '<td style="vertical-align: top;">' .
$link .
$extract .
"<div class='mw-search-result-data'>{$score}{$desc} - {$date}{$related}</div>" .
"<col class='diff-marker' />" .
"<col class='diff-content' />" .
"<tr>" .
- "<td colspan='2' width='50%' align='center' class='diff-otitle'>" .
+ "<td colspan='2' width='50%' style='text-align: center' class='diff-otitle'>" .
$this->diffHeader( $previousRev, 'o' ) .
"</td>\n" .
- "<td colspan='2' width='50%' align='center' class='diff-ntitle'>" .
+ "<td colspan='2' width='50%' style='text-align: center' class='diff-ntitle'>" .
$this->diffHeader( $currentRev, 'n' ) .
"</td>\n" .
"</tr>" .
$this->mLanguage = $request->getText( 'uselang' );
$this->mSkipCookieCheck = $request->getCheck( 'wpSkipCookieCheck' );
$this->mToken = ( $this->mType == 'signup' ) ? $request->getVal( 'wpCreateaccountToken' ) : $request->getVal( 'wpLoginToken' );
-
$this->mReturnTo = $request->getVal( 'returnto', '' );
$this->mReturnToQuery = $request->getVal( 'returntoquery', '' );
# PHP 4. Setup.php and ObjectCache.php have structures invalid in PHP 5.0 and
# 5.1, respectively.
if ( !function_exists( 'version_compare' ) || version_compare( phpversion(), '5.3.2' ) < 0 ) {
+ // We need to use dirname( __FILE__ ) here cause __DIR__ is PHP5.3+
require( dirname( __FILE__ ) . '/includes/PHPVersionError.php' );
wfPHPVersionError( 'index.php' );
}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE supplementalData SYSTEM "../../common/dtd/ldmlSupplemental.dtd">
+<supplementalData>
+ <plurals>
+ <pluralRules locales="he">
+ <pluralRule count="one">n is 1</pluralRule>
+ <pluralRule count="two">n is 2</pluralRule>
+ </pluralRules>
+ </plurals>
+ <pluralRules locales="dsb">
+ <pluralRule count="one">n mod 100 is 1</pluralRule>
+ <pluralRule count="two">n mod 100 is 2</pluralRule>
+ <pluralRule count="few">n mod 100 in 3..4</pluralRule>
+ </pluralRules>
+ <pluralRules locales="cu">
+ <pluralRule count="one">n mod 10 is 1</pluralRule>
+ <pluralRule count="two">n mod 10 is 2</pluralRule>
+ <pluralRule count="few">n mod 10 in 3..4</pluralRule>
+ </pluralRules>
+ <!-- Plural form transformations
+ Based on this discussion: http://translatewiki.net/wiki/Thread:Support/New_plural_rules_for_Scots_Gaelic_(gd)
+ $forms[0] - 1
+ $forms[1] - 2
+ $forms[2] - 11
+ $forms[3] - 12
+ $forms[4] - 3-10, 13-19
+ $forms[5] - 0, 20, rest -->
+ <pluralRules locales="gd">
+ <pluralRule count="one">n is 1</pluralRule>
+ <pluralRule count="two">n is 2</pluralRule>
+ <pluralRule count="elevan">n is 11</pluralRule>
+ <pluralRule count="twelve">n is 12</pluralRule>
+ <pluralRule count="few">n in 3..10 or n in 13..19</pluralRule>
+ </pluralRules>
+</supplementalData>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE supplementalData SYSTEM "../../common/dtd/ldmlSupplemental.dtd">
+<supplementalData>
+ <version number="$Revision: 6155 $"/>
+ <generation date="$Date: 2011-09-21 23:51:12 +0530 (ബു, 21 സെപ് 2011) $"/>
+ <plurals>
+ <!-- if locale is known to have no plurals, there are no rules -->
+ <pluralRules locales="az bm bo dz fa id ig ii hu ja jv ka kde kea km kn ko lo ms my sah ses sg th to tr vi wo yo zh"/>
+ <pluralRules locales="ar">
+ <pluralRule count="zero">n is 0</pluralRule>
+ <pluralRule count="one">n is 1</pluralRule>
+ <pluralRule count="two">n is 2</pluralRule>
+ <pluralRule count="few">n mod 100 in 3..10</pluralRule>
+ <pluralRule count="many">n mod 100 in 11..99</pluralRule>
+ </pluralRules>
+ <pluralRules locales="asa af bem bez bg bn brx ca cgg chr da de dv ee el en eo es et eu fi fo fur fy gl gsw gu ha haw he is it jmc kaj kcg kk kl ksb ku lb lg mas ml mn mr nah nb nd ne nl nn no nr ny nyn om or pa pap ps pt rof rm rwk saq seh sn so sq ss ssy st sv sw syr ta te teo tig tk tn ts ur wae ve vun xh xog zu">
+ <pluralRule count="one">n is 1</pluralRule>
+ </pluralRules>
+ <pluralRules locales="ak am bh fil tl guw hi ln mg nso ti wa">
+ <pluralRule count="one">n in 0..1</pluralRule>
+ </pluralRules>
+ <pluralRules locales="ff fr kab">
+ <pluralRule count="one">n within 0..2 and n is not 2</pluralRule>
+ </pluralRules>
+ <pluralRules locales="lv">
+ <pluralRule count="zero">n is 0</pluralRule>
+ <pluralRule count="one">n mod 10 is 1 and n mod 100 is not 11</pluralRule>
+ </pluralRules>
+ <pluralRules locales="iu kw naq se sma smi smj smn sms">
+ <pluralRule count="one">n is 1</pluralRule>
+ <pluralRule count="two">n is 2</pluralRule>
+ </pluralRules>
+ <pluralRules locales="ga"> <!-- http://unicode.org/cldr/trac/ticket/3915 -->
+ <pluralRule count="one">n is 1</pluralRule>
+ <pluralRule count="two">n is 2</pluralRule>
+ <pluralRule count="few">n in 3..6</pluralRule>
+ <pluralRule count="many">n in 7..10</pluralRule>
+ </pluralRules>
+ <pluralRules locales="ro mo">
+ <pluralRule count="one">n is 1</pluralRule>
+ <pluralRule count="few">n is 0 OR n is not 1 AND n mod 100 in 1..19</pluralRule>
+ </pluralRules>
+ <pluralRules locales="lt">
+ <pluralRule count="one">n mod 10 is 1 and n mod 100 not in 11..19</pluralRule>
+ <pluralRule count="few">n mod 10 in 2..9 and n mod 100 not in 11..19</pluralRule>
+ </pluralRules>
+ <pluralRules locales="be bs hr ru sh sr uk">
+ <pluralRule count="one">n mod 10 is 1 and n mod 100 is not 11</pluralRule>
+ <pluralRule count="few">n mod 10 in 2..4 and n mod 100 not in 12..14</pluralRule>
+ <pluralRule count="many">n mod 10 is 0 or n mod 10 in 5..9 or n mod 100 in 11..14</pluralRule>
+ <!-- others are fractions -->
+ </pluralRules>
+ <pluralRules locales="cs sk">
+ <pluralRule count="one">n is 1</pluralRule>
+ <pluralRule count="few">n in 2..4</pluralRule>
+ </pluralRules>
+ <pluralRules locales="pl">
+ <pluralRule count="one">n is 1</pluralRule>
+ <pluralRule count="few">n mod 10 in 2..4 and n mod 100 not in 12..14</pluralRule>
+ <pluralRule count="many">n is not 1 and n mod 10 in 0..1 or n mod 10 in 5..9 or n mod 100 in 12..14</pluralRule>
+ <!-- others are fractions -->
+ <!-- and n mod 100 not in 22..24 from Tamplin -->
+ </pluralRules>
+ <pluralRules locales="sl">
+ <pluralRule count="one">n mod 100 is 1</pluralRule>
+ <pluralRule count="two">n mod 100 is 2</pluralRule>
+ <pluralRule count="few">n mod 100 in 3..4</pluralRule>
+ </pluralRules>
+ <pluralRules locales="mt"> <!-- from Tamplin's data -->
+ <pluralRule count="one">n is 1</pluralRule>
+ <pluralRule count="few">n is 0 or n mod 100 in 2..10</pluralRule>
+ <pluralRule count="many">n mod 100 in 11..19</pluralRule>
+ </pluralRules>
+ <pluralRules locales="mk"> <!-- from Tamplin's data -->
+ <pluralRule count="one">n mod 10 is 1 and n is not 11</pluralRule>
+ </pluralRules>
+ <pluralRules locales="cy"> <!-- from http://www.saltcymru.org/wordpress/?p=99&lang=en -->
+ <pluralRule count="zero">n is 0</pluralRule>
+ <pluralRule count="one">n is 1</pluralRule>
+ <pluralRule count="two">n is 2</pluralRule>
+ <pluralRule count="few">n is 3</pluralRule>
+ <pluralRule count="many">n is 6</pluralRule>
+ </pluralRules>
+ <pluralRules locales="lag">
+ <pluralRule count="zero">n is 0</pluralRule>
+ <pluralRule count="one">n within 0..2 and n is not 0 and n is not 2</pluralRule>
+ </pluralRules>
+ <pluralRules locales="shi">
+ <pluralRule count="one">n within 0..1</pluralRule>
+ <pluralRule count="few">n in 2..10</pluralRule>
+ </pluralRules>
+ <pluralRules locales="br"> <!-- from http://unicode.org/cldr/trac/ticket/2886 -->
+ <pluralRule count="one">n mod 10 is 1 and n mod 100 not in 11,71,91</pluralRule>
+ <pluralRule count="two">n mod 10 is 2 and n mod 100 not in 12,72,92</pluralRule>
+ <pluralRule count="few">n mod 10 in 3..4,9 and n mod 100 not in 10..19,70..79,90..99</pluralRule>
+ <pluralRule count="many">n mod 1000000 is 0 and n is not 0</pluralRule>
+ </pluralRules>
+ <pluralRules locales="ksh">
+ <pluralRule count="zero">n is 0</pluralRule>
+ <pluralRule count="one">n is 1</pluralRule>
+ </pluralRules>
+ <pluralRules locales="tzm">
+ <pluralRule count="one">n in 0..1 or n in 11..99</pluralRule>
+ </pluralRules>
+ <pluralRules locales="gv">
+ <pluralRule count="one">n mod 10 in 1..2 or n mod 20 is 0</pluralRule>
+ </pluralRules>
+ <pluralRules locales="gd">
+ <pluralRule count="one">n in 1,11</pluralRule>
+ <pluralRule count="two">n in 2,12</pluralRule>
+ <pluralRule count="few">n in 3..10,13..19</pluralRule>
+ </pluralRules>
+ </plurals>
+</supplementalData>
'note' => "'''تێبینی:'''",
'previewnote' => "'''لە بیرت نەچێت ئەمە تەنیا پێشبینینە.'''
گۆڕانکارییەکانت ھێشتا پاشەکەوت نەکراون!",
+'continue-editing' => 'بەردەوام بە لەدەستکاریکردن',
'previewconflict' => 'ئەم پێشبینینە بە تۆ نیشان ئەدات ئەو دەقەی لە شوێنی دەستکاری سەرەوە داتناوە چۆن بەرچاو ئەکەوێت ئەگەر پاشەکەوتی بکەیت.',
'session_fail_preview' => "'''ببوورە! ناتوانین دەستکارییەکەت پێواژۆ بکەین بە ھۆی لەدەستدانی session data.'''
تکایە دیسان ھەوڵبدەوە.
**لادان لە مافەکانی بڵاوکردنەوە
***پەڕگەی دووبارەکراوە',
'filedelete-edit-reasonlist' => 'دەستکاری هۆکارەکانی سڕینەوە',
+'filedelete-maintenance-title' => 'ناتوانیت پەڕگە بسڕیتەوە',
# MIME search
'mimesearch' => 'گەڕانی MIME',
ئێستا ڕەوانکەرە بۆ [[$2]].',
'double-redirect-fixer' => 'چارەسەرکەری ڕەوانکەر',
-'brokenredirects' => 'ڕەوانەکراوە شکاوەکان',
+'brokenredirects' => 'ڕەوانەکەرە شکاوەکان',
'brokenredirectstext' => 'ئەم ڕەوانەکراوانە بەستەرن بۆ ئەو پەڕانە کە بوونیان نییە:',
'brokenredirects-edit' => 'دەستکاری',
'brokenredirects-delete' => 'سڕینەوە',
'allpages-bad-ns' => '{{SITENAME}} ناوبۆشایی نیە "$1".',
'allpages-hide-redirects' => 'ڕەوانەکراوەکان بشارەوە',
+# SpecialCachedPage
+'cachedspecial-refresh-now' => 'دواترین پیشانبدە',
+
# Special:Categories
'categories' => 'پۆلەكان',
'categoriespagetext' => 'ئەم {{PLURAL:$1|پۆلە پەڕە یان پەڕگەی|پۆلانە پەڕە یان پەڕگەیان}} لەخۆگرتە.
'protectedarticle' => '«[[$1]]»ی پاراست',
'modifiedarticleprotection' => 'ئاستی پاراستنی «[[$1]]»ی گۆڕا',
'unprotectedarticle' => 'پاراستنی لەسەر «[[$1]]» لابرد',
-'movedarticleprotection' => 'ڕێککارییەکانی پاراستن لە "[[$2]]" گوازرایەوە بۆ "[[$1]]"',
+'movedarticleprotection' => 'ڕێککارییەکانی پاراستن لە «[[$2]]» گوازرایەوە بۆ «[[$1]]»',
'protect-title' => 'گۆڕینی ئاستی پاراستنی "$1"',
'prot_1movedto2' => '[[$1]] گوازرایەوە بۆ [[$2]]',
'protect-legend' => 'پاراستن تەیید بکە',
'movepagebtn' => 'ئەم پەڕەیە بگوازەوە',
'pagemovedsub' => 'گواستنەوە بە سەرکەوتوویی جێبەجێ کرا',
'movepage-moved' => "'''«$1» گوازرایەوە بۆ «$2»'''",
-'movepage-moved-redirect' => 'ڕەوانکەرێک درووستکرا.',
+'movepage-moved-redirect' => 'ڕەوانەکەرێک دروست کرا.',
'movepage-moved-noredirect' => 'لە دانانی ڕەوانەکەر بەرگری کرا.',
'articleexists' => 'پەڕەیەک بەم ناوە ھەیە یان ئەو ناوەی تۆ ھەڵتبژاردووە ڕێگەی پێنەدراوە.
تکایە ناوێکی دیکە ھەڵبژێرە.',
'movedto' => 'گواسترایەوە بۆ',
'movetalk' => 'پەڕەی وتووێژی پەیوەندیدار بگوازەوە',
'move-subpages' => 'ژێرپەڕەکانی بگوازەوە (ھەتا $1 پەڕە)',
-'move-talk-subpages' => 'ژێرپەڕەکانی پەڕەی وتووێژی بگۆزەرەوە (ھەتاکوو $1)',
+'move-talk-subpages' => 'ژێرپەڕەکانی پەڕەی وتووێژ بگوازەوە (ھەتا $1 پەڕە)',
'movepage-page-exists' => 'پەڕەی $1 هەیە و ناتوانرێت خۆکار بخرێتە جێی.',
'movepage-page-moved' => 'پەڕەی $1 گۆزرایەوە بۆ $2.',
'movepage-page-unmoved' => 'ناکرێ پەڕەی $1 بگوێزرێتەوە بۆ $2.',
'movelogpagetext' => 'لە خوارەوەدا لیستی ھەموو پەڕە گواستنەوەکان دەبینن.',
'movesubpage' => '{{PLURAL:$1|ژێرپەڕە|ژێرپەڕە}}',
'movesubpagetext' => 'ئەم لاپەڕە $1 {{PLURAL:$1|ژێرلاپەڕەی|ژێرلاپەڕەی}} هەیە کە لەخوارە نیشان دراوە.',
-'movenosubpage' => 'ئەم پەڕە ھیچ ژێرپەڕەیەکی نییە.',
+'movenosubpage' => 'ئەم پەڕەیە ھیچ ژێرپەڕەیەکی نییە.',
'movereason' => 'ھۆکار:',
'revertmove' => 'پێچەوانەکردنەوە',
'delete_and_move' => 'بیسڕەوە و بیگوازەوە',
# Info page
'pageinfo-title' => 'زانیاری بۆ «$1»',
+'pageinfo-header-basic' => 'زانیاریی سەرەتایی',
'pageinfo-header-edits' => 'دەستکاریەکان',
'pageinfo-views' => 'ژمارەی بینینەکان',
'pageinfo-watchers' => 'ژمارەی چاودێران',
+'pageinfo-firstuser' => 'دروستکەری پەڕە',
'pageinfo-edits' => 'ژمارەی دەستکارییەکان',
# Skin names
'exif-imagelength' => 'بەرزی',
'exif-imagedescription' => 'ناونیشانی وێنە',
'exif-model' => 'جۆری کامێرا',
+'exif-software' => 'نەرمەواڵەی بەکارهاتوو',
'exif-artist' => 'نووسەر',
'exif-colorspace' => 'بۆشایی رهنگ',
+'exif-pixelydimension' => 'پانی وێنە',
+'exif-pixelxdimension' => 'بەرزی وێنە',
'exif-usercomment' => 'بۆچوونەکانی بەکارهێنەر',
'exif-relatedsoundfile' => 'فایلی دهنگی لێکچوو',
'exif-lightsource' => 'سەرچاوەی ڕووناکی',
'exif-gpstrack' => 'ئاڕاستەی جوڵان',
'exif-gpsimgdirection' => 'ئاڕاستەی وێنە',
'exif-gpsdatestamp' => 'ڕێکەوتی GPS',
+'exif-objectname' => 'سەردێری کورت',
+'exif-headline' => 'سەردێر',
+'exif-source' => 'سەرچاوە',
+'exif-copyrighted' => 'ڕەوشی مافی لەبەرگرتنەوە',
# EXIF attributes
'exif-compression-1' => 'نەپەستێنراو',
'exif-gpsspeed-k' => 'کیلۆمەتر هەر کاتژمێر',
'exif-gpsspeed-m' => 'مایل هەر کاتژمێر',
+# Pseudotags used for GPSDestDistanceRef
+'exif-gpsdestdistance-k' => 'کیلۆمەتر',
+'exif-gpsdestdistance-m' => 'میل',
+'exif-gpsdestdistance-n' => 'میکی دەریایی',
+
+'exif-dc-date' => 'ڕۆژ(ەکان)',
+'exif-dc-publisher' => 'بڵاوکار',
+'exif-dc-relation' => 'پەڕگەی پەیوەندیدار',
+'exif-dc-rights' => 'مافەکان',
+'exif-dc-source' => 'سەرچاوەی پەڕگە',
+'exif-dc-type' => 'جۆری پەڕگە',
+
'exif-iimcategory-hth' => 'تەندروستی',
'exif-iimcategory-sci' => 'زانست و تەکنۆلۆژیا',
'exif-iimcategory-soi' => 'بابەتە کۆمەڵایەتییەکان',
'shared-repo' => 'yew embarê repositoryî',
'shared-repo-name-wikimediacommons' => 'Wikimedia Commons',
'filepage.css' => '/* CSS placed here is included on the file description page, also included on foreign client wikis */',
+'upload-disallowed-here' => 'Nê asengi sero theba nênusneyêno.',
# File reversion
'filerevert' => '$1 reyna biyere',
'pageinfo-authors' => 'Amarina nuştekaran pêro',
'pageinfo-recent-edits' => 'Amariya vurnayışan ($1 ra nata)',
'pageinfo-recent-authors' => 'Amarina nuştekaran pêro',
-'pageinfo-restriction' => 'Xısusiyetê pela da (<code>$1</code>)',
+'pageinfo-restriction' => 'Xısusiyetê pela (<code>{{lcfirst:$1}}</code>)',
'pageinfo-magic-words' => '{{PLURAL:$1|Çekuya|Çekuyê}} ($1) sihırini',
'pageinfo-hidden-categories' => '{{PLURAL:$1|Kategoriye|Kategoriyan}} ($1) bınımne',
'pageinfo-templates' => '{{PLURAL:$1|Şablon|Şabloni}} ($1) açarneyayê',
'file-info-size-pages' => '$1 × $2 pikse, dergeya dosyay: $3, MIME tipiya cı: $4, $5 {{PLURAL:$5|pela|pela}}',
'file-nohires' => 'Rovıleşiyayışo berzêr çıniyo.',
'svg-long-desc' => 'SVG dosya, nominalin $1 × $2 piksels, ebatê dosya: $3',
+'svg-long-desc-animated' => 'SVG dosya, nominalin $1 × $2 piksela, ebatê dosya: $3',
'show-big-image' => 'Resolosyonê temami',
'show-big-image-preview' => "Verqayd dergiya: $1'i.",
'show-big-image-other' => 'Zewmi{{PLURAL:$2|Vılêşnayış|Vılêşnayışê}}: $1.',
'file-info-png-looped' => 'atlama biyo',
'file-info-png-repeat' => '$1 {{PLURAL:$1|hew|hew}} kay biyê',
'file-info-png-frames' => '$1 {{PLURAL:$1|çerçeve|çerçeveyi}}',
+'file-no-thumb-animation' => "'''Not: Dılet tekniko limit, gırd agozneya resm de qıckek de animasyoni miyan dı nêbo.'''",
+'file-no-thumb-animation-gif' => "'''Not: Dılet tekniko limit, gırd agozneya resm de qıckek de GIF imaci de animasyon do nêbo.'''",
# Special:NewFiles
'newimages' => 'Galeriya dosyayan dê newan',
If you do not want your writing to be edited mercilessly, then do not submit it here.<br />
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource (see $1 for details).
'''Do not submit copyrighted work without permission!'''",
+'editpage-head-copy-warn' => '-', # do not translate or duplicate this message to other languages
'editpage-tos-summary' => '-', # do not translate or duplicate this message to other languages
'longpage-hint' => '-', # do not translate or duplicate this message to other languages
'longpageerror' => "'''Error: The text you have submitted is {{PLURAL:$1|one kilobyte|$1 kilobytes}} long, which is longer than the maximum of {{PLURAL:$2|one kilobyte|$2 kilobytes}}.'''
'otherlanguages' => 'Ann an cànain eile',
'redirectedfrom' => '(Air ath-sheòladh o $1)',
'redirectpagesub' => 'Ath-sheòl an duilleag',
-'lastmodifiedat' => 'Chaidh an duilleag seo a mhùthadh $1, aig $2 turas mu dheireadh.',
+'lastmodifiedat' => 'Chaidh an duilleag seo a mhùthadh $1 aig $2 turas mu dheireadh.',
'viewcount' => 'Chaidh inntrigeadh a dhèanam dhan duilleag seo {{PLURAL:$1|aon turas|$1 thuras|$1 turas|$1 turais|$1 turas}}.',
'protectedpage' => 'Duilleag fo dhìon',
'jumpto' => 'Gearr leum gu:',
'''Mas e deasachadh dligheach a tha seo, feuch ris a-rithist.'''
Mur obraich e fhathast, feuch is [[Special:UserLogout|clàraich a-mach]] is a-steach a-rithist an uairsin.",
+'token_suffix_mismatch' => "'''Dhiùlt sinn na dheasaich thu a chionn 's gun do chuir an cliant agad na caractaran puingeachaidh tro chèile san tòcan deasachaidh.'''
+Dhiùlt sinn na dheasaich thu air eagal 's gun coirbeadh e teacsa na duilleige.
+Tachraidh seo uaireannan ma chleachdar seirbheis-lìn progsaidh gun urra a tha làn de mhearachdan.",
+'edit_form_incomplete' => "'''Cha do ràinig cuid dhen fhoirm deasachaidh am frithealaichte; dèan cinnteach gu bheil gach deasachadh agad slàn is feuch ris a-rithist.'''",
'editing' => "A' deasachadh $1",
'editingsection' => "A' deasachadh $1 (earrann)",
+'editingcomment' => "A' deasachadh $1 (earrann ùr)",
'editconflict' => 'Còmhstri deasachaidh: $1',
'explainconflict' => "Tha cuideigin eile air an duilleag seo a mhùthadh on a thòisich thu fhèin air a dheasachadh.
Tha am bogsa teacsa gu h-àrd a' nochdadh na duilleige mar a tha i an-dràsta.
Cha dèid '''ach an teacsa gu h-àrd''' a shàbhaladh nuair a bhriogas tu air \"{{int:savearticle}}\".",
'yourtext' => 'An teacsa agad',
'storedversion' => 'Lethbhreac taisgte',
+'nonunicodebrowser' => "'''Rabhadh: Chan eil am brabhsair agad co-chòrdail le Unicode.'''
+Chuir sinn gleus air dòigh dhut a nì cinnteach gun urrainn dhut duilleagan a shàbhaladh gu tèarainte: Nochdaidh caractaran taobh a-muigh ASCII mar chòd sia-dheicheach sa bhogsa deasachaidh.",
'editingold' => "'''RABHADH: Tha thu a' deasachadh lethbhreac seann-aimsireil na duilleige seo.
Ma shàbhalas tu seo, thèid gach mùthadh air chall a rinneadh a-mach on mhùthadh seo.'''",
'yourdiff' => 'Caochlaidhean',
Mur eil thu ag iarraidh an sgrìobhaidh agad a dheasaichear is a sgaoilear le càch, na cuir e.<br />
Ma dh'fhoilleachas tu rudeigin an seo, bidh tu a' dearbhadh gun do sgrìobh thu fhèin e, no gur ann às an raon phòballach a thàinig e; thoir aire '''nach eil''' sin a' gabhail a-staigh duilleagan-lìn mar as àbhaist (seall $1 airson barrachd fiosrachaidh). <br />
'''NA CLEACHDAIBH SAOTHAIR FO DHLIGHE-SGRÌOBHAIDH GUN CHEAD!'''",
+'longpageerror' => "'''Mearachd: Tha an teacsa a chur thu thugainn {{PLURAL:$1 kilobyte|$1 kilobyte|$1 kilobyte|$1 kilobyte|$1 kilobyte|$1 kilobyte|}} a dh'fhaid is tha sin nas fhaide na tha ceadaichte ({{PLURAL:$1 kilobyte|$2 kilobyte|$2 kilobyte|$2 kilobyte|$2 kilobyte|$2 kilobyte|}}).'''
+Cha ghabh a shàbhaladh.",
+'readonlywarning' => "'''Rabhadh: Chaidh an stòr-dàta a ghlasadh a chum obair-ghlèidhidh agus chan urrainn dhut na còraichean-deasachaidh agad a chur gu feum an-dràsta fhèin.'''
+'S mathaid gum b' fheairrde dhut lethbhreac a dhèanamh dhen teacsa agus a shàbhaladh ann am faidhle ach an urrainn dhut a chleachdadh as a dhèidh seo.
+
+Seo am mìneachadh a thug an rianaire a ghlais e: $1",
'protectedpagewarning' => "'''Rabhadh: Chaidh an duilleag seo a dhìon 's chan urrainn ach dhan fheadhainn aig a bheil ùghdarras rianaire a dheasachadh.'''
Chì thu an clàr mu dheireadh san loga mar fhiosrachadh dhut gu h-ìosal:",
+'semiprotectedpagewarning' => "'''An aire:''' Chaidh an duilleag seo a dhìon 's chan fhaod ach cleachdaichean clàraichte a dheasachadh.
+Seo an rud mu dheireadh san loga mar fhiosrachadh dhut:",
+'cascadeprotectedwarning' => "'''Rabhadh:''' Chaidh an duilleag seo a dhìon 's chan fhaod ach rianairean a dheasachadh a chionn 's gun robh e am broinn {{PLURAL:$1|na duilleige|nan duilleagan}} a leanas a tha cascade-protected.",
+'titleprotectedwarning' => "'''Rabhadh: Chaidh an duilleag seo a dhìon 's feumar [[Special:ListGroupRights|còraichean sònraichte]] gus a dheasachadh.'''
+Seo an rud mu dheireadh san loga mar fhiosrachadh dhut:",
'templatesused' => "Tha {{PLURAL:$1|teamplaid|theamplaid||teamplaid|theamplaid|teamplaidean|teamplaid}} 'gan cleachdadh air an duilleag seo:",
'templatesusedpreview' => "Tha {{PLURAL:$1|teamplaid 'ga cleachdadh|teamplaidean 'gan cleachdadh|teamplaidean 'gan cleachdadh|teamplaidean 'gan cleachdadh|teamplaidean 'gan cleachdadh|teamplaidean 'gan cleachdadh}} san ro-shealladh seo:",
+'templatesusedsection' => "Tha {{PLURAL:$1|teamplaid 'ga cleachdadh|teamplaidean 'gan cleachdadh|teamplaidean 'gan cleachdadh|teamplaidean 'gan cleachdadh|teamplaidean 'gan cleachdadh|teamplaidean 'gan cleachdadh}} san earrann seo:",
'template-protected' => '(air a dhìon)',
'template-semiprotected' => '(air a leth-dhìon)',
'hiddencategories' => "Tha an duilleag seo 'na ball de {{PLURAL:$1|1 roinn-seòrsa fhalaichte|$1 roinn-seòrsa fhalaichte|1 roinn-seòrsa fhalaichte|$1 roinn-seòrsa fhalaichte|$1 roinnean-seòrsa falaichte|$1 roinn-seòrsa fhalaichte}}:",
L'ultimo elemento del registro dei blocchi è riportato di seguito per informazione:",
'clearyourcache' => "'''Nota:''' dopo aver salvato, potrebbe essere necessario pulire la cache del proprio browser per vedere i cambiamenti.
*'''Firefox / Safari''': tenere premuto il tasto delle maiuscole e fare clic su ''Ricarica'', oppure premere ''Ctrl-F5'' o ''Ctrl-R'' (''⌘-R'' su Mac)
-*'''Google Chrome''': premere ''Ctrl-Shift-R'' (''⌘-Shift-R'' su un Mac)
+*'''Google Chrome''': fare clic su ''Ricarica'', oppure premere ''Ctrl-R'' o ''Ctrl-Shift-R'' (''⌘-Shift-R'' su un Mac)
*'''Internet Explorer''': tenere premuto il tasto ''Ctrl'' mentre si fa clic su ''Refresh'', oppure premere ''Ctrl-F5''
*'''Opera''': svuotare completamente la cache dal menu ''Strumenti → Preferenze''",
'usercssyoucanpreview' => "'''Suggerimento:''' usa il pulsante 'Visualizza anteprima' per provare il tuo nuovo CSS prima di salvarlo.",
'resetpass_announce' => 'メールでお送りした仮パスワードでログインしました。
ログインを完了するには、ここで新しいパスワードを設定する必要があります:',
'resetpass_text' => '<!-- ここに文を挿入 -->',
-'resetpass_header' => 'ã\82¢ã\82«ã\82¦ã\83³ã\83\88ã\81®ã\83\91ã\82¹ã\83¯ã\83¼ã\83\89ã\82\92変更',
-'oldpassword' => '古いパスワード:',
-'newpassword' => '新しいパスワード:',
+'resetpass_header' => 'ã\82¢ã\82«ã\82¦ã\83³ã\83\88ã\81®ã\83\91ã\82¹ã\83¯ã\83¼ã\83\89ã\81®変更',
+'oldpassword' => '古いパスワード:',
+'newpassword' => '新しいパスワード:',
'retypenew' => '新しいパスワードを再入力:',
'resetpass_submit' => '再設定してログイン',
'resetpass_success' => 'パスワードの変更に成功しました!
手動で統合してください。'''",
'movedto' => '移動先:',
'movetalk' => '付随するトークページも移動',
-'move-subpages' => '下位ページも移動($1ページまで)',
-'move-talk-subpages' => 'トークページの下位ページも移動($1個まで)',
+'move-subpages' => '下位ページも移動($1 件まで)',
+'move-talk-subpages' => 'トークページの下位ページも移動($1 件まで)',
'movepage-page-exists' => 'ページ「$1」は既に存在するため、自動的な上書きはできませんでした。',
'movepage-page-moved' => 'ページ「$1」は「$2」に移動しました。',
'movepage-page-unmoved' => 'ページ「$1」は「$2」に移動できませんでした。',
'movelogpage' => '移動記録',
'movelogpagetext' => '以下はすべてのページ移動の一覧です。',
'movesubpage' => '{{PLURAL:$1|下位ページ}}',
-'movesubpagetext' => 'このページには{{PLURAL:$1|下位ページ}}が以下の $1 件あります。',
+'movesubpagetext' => 'このページには、以下の $1 {{PLURAL:$1|下位ページ}}があります。',
'movenosubpage' => 'このページに下位ページはありません。',
'movereason' => '理由:',
'revertmove' => '差し戻し',
'variantname-ike-latn' => 'イヌクティトゥット語 (ラテン文字)',
'variantname-iu' => 'イヌクティトゥット語',
+# Variants for Tachelhit language
+'variantname-shi-tfng' => 'シルハ語 (ティフィナグ文字)',
+'variantname-shi-latn' => 'シルハ語 (ラテン文字)',
+'variantname-shi' => 'シルハ語',
+
# Metadata
'metadata' => 'メタデータ',
'metadata-help' => 'このファイルには、追加情報があります(おそらく、作成やデジタル化する際に使用したデジタルカメラやスキャナーが追加したものです)。
* @author Junaidpv
* @author Jyothis
* @author Kaganer
+ * @author Krenair
* @author Manjith Joseph <manjithkaini@gmail.com>
* @author Naveen Sankar
* @author Praveen Prakash <me.praveen@gmail.com>
'newarticle' => '(പുതിയത്)',
'newarticletext' => 'ഇതുവരെ നിലവിലില്ലാത്ത ഒരു താൾ സൃഷ്ടിക്കാനുള്ള ശ്രമത്തിലാണ് താങ്കൾ. അതിനായി താഴെ ആവശ്യമുള്ള വിവരങ്ങൾ എഴുതിച്ചേർത്ത് സേവ് ചെയ്യുക (കൂടുതൽ വിവരങ്ങൾക്ക് [[{{MediaWiki:Helppage}}|സഹായം താൾ]] കാണുക). താങ്കളിവിടെ അബദ്ധത്തിൽ വന്നതാണെങ്കിൽ ബ്രൗസറിന്റെ ബാക്ക് ബട്ടൺ ഞെക്കിയാൽ തിരിച്ചുപോകാം.',
'anontalkpagetext' => "----
-{| class=\"messagebox standard-talk\" style=\"border: 1px solid #B3B300; background-color:#FFFFBF;\"
-|align=\"left\" |
+{| class=\"messagebox standard-talk\" style=\"border: 1px solid #B3B300; background-color:#FFFFBF; text-align: left;\"
+|
''ഇതുവരെ അംഗത്വം എടുക്കാതിരിക്കുകയോ, നിലവിലുള്ള അംഗത്വം ഉപയോഗിക്കാതിരിക്കുകയോ ചെയ്യുന്ന '''ഒരു അജ്ഞാത ഉപയോക്താവിന്റെ സംവാദം താളാണിത്'''.
അതിനാൽ അദ്ദേഹത്തെ തിരിച്ചറിയുവാൻ അക്കരൂപത്തിലുള്ള ഐ.പി. വിലാസം ഉപയോഗിക്കേണ്ടതുണ്ട്. ഇത്തരം ഒരു ഐ.പി. വിലാസം പല ഉപയോക്താക്കൾ പങ്കുവെക്കുന്നുണ്ടാവാം.
താങ്കൾ ഈ സന്ദേശം ലഭിച്ച ഒരു അജ്ഞാത ഉപയോക്താവാണെങ്കിൽ, ഭാവിയിൽ ഇതര ഉപയോക്താക്കളുമായി ഉണ്ടായേക്കാവുന്ന ആശയക്കുഴപ്പം ഒഴിവാക്കാൻ ദയവായി [[Special:UserLogin/signup|ഒരു അംഗത്വമെടുക്കുക]] അല്ലെങ്കിൽ [[Special:UserLogin|പ്രവേശിക്കുക]].
'spamprotectiontitle' => 'Spam ਸੁਰੱਖਿਆ ਫਿਲਟਰ',
# Info page
-'pageinfo-header-edits' => 'ਸੋਧਾਂ',
-'pageinfo-watchers' => 'ਨà¨\9c਼ਰ ਰੱà¨\96ਣ ਵਾਲ਼ਿà¨\86à¨\82 ਦà©\80 à¨\97ਿਣਤà©\80',
-'pageinfo-edits' => 'ਸà©\8bਧਾà¨\82 ਦà©\80 à¨\97ਿਣਤà©\80',
+'pageinfo-header-edits' => 'ਸੋਧਾਂ ਦਾ ਅਤੀਤ',
+'pageinfo-watchers' => 'ਸਫ਼à©\87 â\80\99ਤà©\87 ਨà¨\9c਼ਰ ਰੱà¨\96ਣ ਵਾਲ਼à©\87',
+'pageinfo-edits' => 'à¨\95à©\81ੱਲ ਸà©\8bਧਾà¨\82',
# Skin names
'skinname-standard' => 'ਕਲਾਸਿਕ',
'tooltip-t-permalink' => 'Anliura fissa a sta version-sì dla pàgina',
'tooltip-ca-nstab-main' => 'Vardé la pàgina ëd contnù.',
'tooltip-ca-nstab-user' => 'Vardé la pàgina Utent.',
-'tooltip-ca-nstab-media' => 'Vardé la pàgina dl',
+'tooltip-ca-nstab-media' => 'Vardé la pàgina dël mojen',
'tooltip-ca-nstab-special' => 'Costa a l',
'tooltip-ca-nstab-project' => 'Vardé la pàgina proteta.',
'tooltip-ca-nstab-image' => 'Vardé la pàgina dl',
* @ingroup Language
* @file
*
+ * @author Arjunaraoc
* @author Chaduvari
* @author Jprmvnvijay5
* @author Kaganer
# Special:ChangeEmail
'changeemail' => 'ఈ-మెయిలు చిరునామా మార్పు',
'changeemail-header' => 'ఖాతా ఈ-మెయిల్ చిరునామాని మార్చండి',
+'changeemail-text' => 'మీ ఈమెయిల్ చిరునామా మార్చుటకు ఈ ఫారము నింపండి. ఈ మార్పుని ఖచ్చితపరచుటకు మీ సంకేతపదం ప్రవేశపెట్టాలి.',
'changeemail-no-info' => 'ఈ పేజీని నేరుగా చూడటానికి మీరు లోనికి ప్రవేశించివుండాలి.',
'changeemail-oldemail' => 'ప్రస్తుత ఈ-మెయిలు చిరునామా:',
'changeemail-newemail' => 'కొత్త ఈ-మెయిలు చిరునామా:',
'contribslink' => 'kontribuisaun',
'block-log-flags-nocreate' => 'la bele kria konta foun',
'block-log-flags-noemail' => 'korreiu eletróniku blokeiu',
+'block-log-flags-nousertalk' => 'la bele edita pájina diskusaun rasik',
'ipb_already_blocked' => 'Ema ruma blokeiu "$1" tiha ona',
# Move page
'uploadlogpagetext' => 'درج ذیل میں حالیہ زبراثقال (اپ لوڈ) کی گئی املاف (فائلوں) کی فہرست دی گئی ہے۔',
'filedesc' => 'خلاصہ',
'fileuploadsummary' => 'خلاصہ :',
+'filesource' => 'ذرائع',
'uploadedfiles' => 'زبراثقال ملف (فائل اپ لوڈ)',
'ignorewarning' => 'انتباہ نظرانداز کرتے ہوۓ بہرصورت ملف (فائل) کو محفوظ کرلیا جاۓ۔',
'ignorewarnings' => 'ہر انتباہ نظرانداز کردیا جاۓ۔',
--- /dev/null
+<?php
+/**
+ * Parse and evaluate a plural rule.
+ *
+ * http://unicode.org/reports/tr35/#Language_Plural_Rules
+ *
+ * @author Niklas Laxstrom, Tim Starling
+ *
+ * @copyright Copyright © 2010-2012, Niklas Laxström
+ * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
+ * @file
+ * @since 1.20
+ */
+
+class CLDRPluralRuleEvaluator {
+ /**
+ * Evaluate a number against a set of plural rules. If a rule passes,
+ * return the index of plural rule.
+ *
+ * @param int The number to be evaluated against the rules
+ * @param array The associative array of plural rules in pluralform => rule format.
+ * @return int The index of the plural form which passed the evaluation
+ */
+ public static function evaluate( $number, array $rules ) {
+ $rules = self::compile( $rules );
+ return self::evaluateCompiled( $number, $rules );
+ }
+
+ /**
+ * Convert a set of rules to a compiled form which is optimised for
+ * fast evaluation. The result will be an array of strings, and may be cached.
+ *
+ * @param $rules The rules to compile
+ * @return An array of compile rules.
+ */
+ public static function compile( array $rules ) {
+ // We can't use array_map() for this because it generates a warning if
+ // there is an exception.
+ foreach ( $rules as &$rule ) {
+ $rule = CLDRPluralRuleConverter::convert( $rule );
+ }
+ return $rules;
+ }
+
+ /**
+ * Evaluate a compiled set of rules returned by compile(). Do not allow
+ * the user to edit the compiled form, or else PHP errors may result.
+ */
+ public static function evaluateCompiled( $number, array $rules ) {
+ // The compiled form is RPN, with tokens strictly delimited by
+ // spaces, so this is a simple RPN evaluator.
+ foreach ( $rules as $i => $rule ) {
+ $stack = array();
+ $zero = ord( '0' );
+ $nine = ord( '9' );
+ foreach ( StringUtils::explode( ' ', $rule ) as $token ) {
+ $ord = ord( $token );
+ if ( $token === 'n' ) {
+ $stack[] = $number;
+ } elseif ( $ord >= $zero && $ord <= $nine ) {
+ $stack[] = intval( $token );
+ } else {
+ $right = array_pop( $stack );
+ $left = array_pop( $stack );
+ $result = self::doOperation( $token, $left, $right );
+ $stack[] = $result;
+ }
+ }
+ if ( $stack[0] ) {
+ return $i;
+ }
+ }
+ // None of the provided rules match. The number belongs to caregory
+ // 'other' which comes last.
+ return count( $rules );
+ }
+
+ /**
+ * Do a single operation
+ *
+ * @param $token string The token string
+ * @param $left The left operand. If it is an object, its state may be destroyed.
+ * @param $right The right operand
+ * @return mixed
+ */
+ private static function doOperation( $token, $left, $right ) {
+ if ( in_array( $token, array( 'in', 'not-in', 'within', 'not-within' ) ) ) {
+ if ( !($right instanceof CLDRPluralRuleEvaluator_Range ) ) {
+ $right = new CLDRPluralRuleEvaluator_Range( $right );
+ }
+ }
+ switch ( $token ) {
+ case 'or':
+ return $left || $right;
+ case 'and':
+ return $left && $right;
+ case 'is':
+ return $left == $right;
+ case 'is-not':
+ return $left != $right;
+ case 'in':
+ return $right->isNumberIn( $left );
+ case 'not-in':
+ return !$right->isNumberIn( $left );
+ case 'within':
+ return $right->isNumberWithin( $left );
+ case 'not-within':
+ return !$right->isNumberWithin( $left );
+ case 'mod':
+ if ( is_int( $left ) ) {
+ return (int) fmod( $left, $right );
+ }
+ return fmod( $left, $right );
+ case ',':
+ if ( $left instanceof CLDRPluralRuleEvaluator_Range ) {
+ $range = $left;
+ } else {
+ $range = new CLDRPluralRuleEvaluator_Range( $left );
+ }
+ $range->add( $right );
+ return $range;
+ case '..':
+ return new CLDRPluralRuleEvaluator_Range( $left, $right );
+ default:
+ throw new CLDRPluralRuleError( "Invalid RPN token" );
+ }
+ }
+}
+
+/**
+ * Evaluator helper class representing a range list.
+ */
+class CLDRPluralRuleEvaluator_Range {
+ var $parts = array();
+
+ function __construct( $start, $end = false ) {
+ if ( $end === false ) {
+ $this->parts[] = $start;
+ } else {
+ $this->parts[] = array( $start, $end );
+ }
+ }
+
+ /**
+ * Determine if the given number is inside the range. If $integerConstraint
+ * is true, the number must additionally be an integer if it is to match
+ * any interval part.
+ */
+ function isNumberIn( $number, $integerConstraint = true ) {
+ foreach ( $this->parts as $part ) {
+ if ( is_array( $part ) ) {
+ if ( ( !$integerConstraint || floor( $number ) === (float)$number )
+ && $number >= $part[0] && $number <= $part[1] )
+ {
+ return true;
+ }
+ } else {
+ if ( $number == $part ) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Readable alias for isNumberIn( $number, false ), and the implementation
+ * of the "within" operator.
+ */
+ function isNumberWithin( $number ) {
+ return $this->isNumberIn( $number, false );
+ }
+
+ /**
+ * Add another part to this range. The supplied new part may either be a
+ * range object itself, or a single number.
+ */
+ function add( $other ) {
+ if ( $other instanceof self ) {
+ $this->parts = array_merge( $this->parts, $other->parts );
+ } else {
+ $this->parts[] = $other;
+ }
+ }
+
+ /**
+ * For debugging
+ */
+ function __toString() {
+ $s = 'Range(';
+ foreach ( $this->parts as $i => $part ) {
+ if ( $i ) {
+ $s .= ', ';
+ }
+ if ( is_array( $part ) ) {
+ $s .= $part[0] . '..' . $part[1];
+ } else {
+ $s .= $part;
+ }
+ }
+ $s .= ')';
+ return $s;
+ }
+
+}
+
+/**
+ * Helper class for converting rules to reverse polish notation (RPN).
+ */
+class CLDRPluralRuleConverter {
+ var $rule, $pos, $end;
+ var $operators = array();
+ var $operands = array();
+
+ /**
+ * Precedence levels. Note that there's no need to worry about associativity
+ * for the level 4 operators, since they return boolean and don't accept
+ * boolean inputs.
+ */
+ static $precedence = array(
+ 'or' => 2,
+ 'and' => 3,
+ 'is' => 4,
+ 'is-not' => 4,
+ 'in' => 4,
+ 'not-in' => 4,
+ 'within' => 4,
+ 'not-within' => 4,
+ 'mod' => 5,
+ ',' => 6,
+ '..' => 7,
+ );
+
+ /**
+ * A character list defining whitespace, for use in strspn() etc.
+ */
+ const WHITESPACE_CLASS = " \t\r\n";
+
+ /**
+ * Same for digits. Note that the grammar given in UTS #35 doesn't allow
+ * negative numbers or decimals.
+ */
+ const NUMBER_CLASS = '0123456789';
+
+ /**
+ * An anchored regular expression which matches a word at the current offset.
+ */
+ const WORD_REGEX = '/[a-zA-Z]+/A';
+
+ /**
+ * Convert a rule to RPN. This is the only public entry point.
+ */
+ public static function convert( $rule ) {
+ $parser = new self( $rule );
+ return $parser->doConvert();
+ }
+
+ /**
+ * Private constructor.
+ */
+ protected function __construct( $rule ) {
+ $this->rule = $rule;
+ $this->pos = 0;
+ $this->end = strlen( $rule );
+ }
+
+ /**
+ * Do the operation.
+ */
+ protected function doConvert() {
+ $expectOperator = true;
+
+ // Iterate through all tokens, saving the operators and operands to a
+ // stack per Dijkstra's shunting yard algorithm.
+ while ( false !== ( $token = $this->nextToken() ) ) {
+ // In this grammar, there are only binary operators, so every valid
+ // rule string will alternate between operator and operand tokens.
+ $expectOperator = !$expectOperator;
+
+ if ( $token instanceof CLDRPluralRuleConverter_Expression ) {
+ // Operand
+ if ( $expectOperator ) {
+ $token->error( 'unexpected operand' );
+ }
+ $this->operands[] = $token;
+ continue;
+ } else {
+ // Operator
+ if ( !$expectOperator ) {
+ $token->error( 'unexpected operator' );
+ }
+ // Resolve higher precedence levels
+ $lastOp = end( $this->operators );
+ while ( $lastOp && self::$precedence[$token->name] <= self::$precedence[$lastOp->name] ) {
+ $this->doOperation( $lastOp, $this->operands );
+ array_pop( $this->operators );
+ $lastOp = end( $this->operators );
+ }
+ $this->operators[] = $token;
+ }
+ }
+
+ // Finish off the stack
+ while ( $op = array_pop( $this->operators ) ) {
+ $this->doOperation( $op, $this->operands );
+ }
+
+ // Make sure the result is sane. The first case is possible for an empty
+ // string input, the second should be unreachable.
+ if ( !count( $this->operands ) ) {
+ $this->error( 'condition expected' );
+ } elseif ( count( $this->operands ) > 1 ) {
+ $this->error( 'missing operator or too many operands' );
+ }
+
+ $value = $this->operands[0];
+ if ( $value->type !== 'boolean' ) {
+ $this->error( 'the result must have a boolean type' );
+ }
+
+ return $this->operands[0]->rpn;
+ }
+
+ /**
+ * Fetch the next token from the input string. Return it as a
+ * CLDRPluralRuleConverter_Fragment object.
+ */
+ protected function nextToken() {
+ if ( $this->pos >= $this->end ) {
+ return false;
+ }
+
+ // Whitespace
+ $length = strspn( $this->rule, self::WHITESPACE_CLASS, $this->pos );
+ $this->pos += $length;
+
+ if ( $this->pos >= $this->end ) {
+ return false;
+ }
+
+ // Number
+ $length = strspn( $this->rule, self::NUMBER_CLASS, $this->pos );
+ if ( $length !== 0 ) {
+ $token = $this->newNumber( substr( $this->rule, $this->pos, $length ), $this->pos );
+ $this->pos += $length;
+ return $token;
+ }
+
+ // Comma
+ if ( $this->rule[$this->pos] === ',' ) {
+ $token = $this->newOperator( ',', $this->pos, 1 );
+ $this->pos ++;
+ return $token;
+ }
+
+ // Dot dot
+ if ( substr( $this->rule, $this->pos, 2 ) === '..' ) {
+ $token = $this->newOperator( '..', $this->pos, 2 );
+ $this->pos += 2;
+ return $token;
+ }
+
+ // Word
+ if ( !preg_match( self::WORD_REGEX, $this->rule, $m, 0, $this->pos ) ) {
+ $this->error( 'unexpected character "' . $this->rule[$this->pos] . '"' );
+ }
+ $word1 = strtolower( $m[0] );
+ $word2 = '';
+ $nextTokenPos = $this->pos + strlen( $word1 );
+ if ( $word1 === 'not' || $word1 === 'is' ) {
+ // Look ahead one word
+ $nextTokenPos += strspn( $this->rule, self::WHITESPACE_CLASS, $nextTokenPos );
+ if ( $nextTokenPos < $this->end
+ && preg_match( self::WORD_REGEX, $this->rule, $m, 0, $nextTokenPos ) )
+ {
+ $word2 = strtolower( $m[0] );
+ $nextTokenPos += strlen( $word2 );
+ }
+ }
+
+ // Two-word operators like "is not" take precedence over single-word operators like "is"
+ if ( $word2 !== '' ) {
+ $bothWords = "{$word1}-{$word2}";
+ if ( isset( self::$precedence[$bothWords] ) ) {
+ $token = $this->newOperator( $bothWords, $this->pos, $nextTokenPos - $this->pos );
+ $this->pos = $nextTokenPos;
+ return $token;
+ }
+ }
+
+ // Single-word operators
+ if ( isset( self::$precedence[$word1] ) ) {
+ $token = $this->newOperator( $word1, $this->pos, strlen( $word1 ) );
+ $this->pos += strlen( $word1 );
+ return $token;
+ }
+
+ // The special numerical keyword "n"
+ if ( $word1 === 'n' ) {
+ $token = $this->newNumber( 'n', $this->pos );
+ $this->pos ++;
+ return $token;
+ }
+
+ $this->error( 'unrecognised word' );
+ }
+
+ /**
+ * For the binary operator $op, pop its operands off the stack and push
+ * a fragment with rpn and type members describing the result of that
+ * operation.
+ */
+ protected function doOperation( $op ) {
+ if ( count( $this->operands ) < 2 ) {
+ $op->error( 'missing operand' );
+ }
+ $right = array_pop( $this->operands );
+ $left = array_pop( $this->operands );
+ $result = $op->operate( $left, $right );
+ $this->operands[] = $result;
+ }
+
+ /**
+ * Create a numerical expression object
+ */
+ protected function newNumber( $text, $pos ) {
+ return new CLDRPluralRuleConverter_Expression( $this, 'number', $text, $pos, strlen( $text ) );
+ }
+
+ /**
+ * Create a binary operator
+ */
+ protected function newOperator( $type, $pos, $length ) {
+ return new CLDRPluralRuleConverter_Operator( $this, $type, $pos, $length );
+ }
+
+ /**
+ * Throw an error
+ */
+ protected function error( $message ) {
+ throw new CLDRPluralRuleError( $message );
+ }
+}
+
+/**
+ * Helper for CLDRPluralRuleConverter.
+ * The base class for operators and expressions, describing a region of the input string.
+ */
+class CLDRPluralRuleConverter_Fragment {
+ var $parser, $pos, $length, $end;
+
+ function __construct( $parser, $pos, $length ) {
+ $this->parser = $parser;
+ $this->pos = $pos;
+ $this->length = $length;
+ $this->end = $pos + $length;
+ }
+
+ public function error( $message ) {
+ $text = $this->getText();
+ throw new CLDRPluralRuleError( "$message at position " . ( $this->pos + 1 ) . ": \"$text\"" );
+ }
+
+ public function getText() {
+ return substr( $this->parser->rule, $this->pos, $this->length );
+ }
+}
+
+/**
+ * Helper for CLDRPluralRuleConverter.
+ * An expression object, representing a region of the input string (for error
+ * messages), the RPN notation used to evaluate it, and the result type for
+ * validation.
+ */
+class CLDRPluralRuleConverter_Expression extends CLDRPluralRuleConverter_Fragment {
+ var $type, $rpn;
+
+ function __construct( $parser, $type, $rpn, $pos, $length ) {
+ parent::__construct( $parser, $pos, $length );
+ $this->type = $type;
+ $this->rpn = $rpn;
+ }
+
+ public function isType( $type ) {
+ if ( $type === 'range' && ( $this->type === 'range' || $this->type === 'number' ) ) {
+ return true;
+ }
+ if ( $type === $this->type ) {
+ return true;
+ }
+ return false;
+ }
+}
+
+/**
+ * Helper for CLDRPluralRuleConverter.
+ * An operator object, representing a region of the input string (for error
+ * messages), and the binary operator at that location.
+ */
+class CLDRPluralRuleConverter_Operator extends CLDRPluralRuleConverter_Fragment {
+ var $name;
+
+ /**
+ * Each op type has three characters: left operand type, right operand type and result type
+ *
+ * b = boolean
+ * n = number
+ * r = range
+ *
+ * A number is a kind of range.
+ */
+ static $opTypes = array(
+ 'or' => 'bbb',
+ 'and' => 'bbb',
+ 'is' => 'nnb',
+ 'is-not' => 'nnb',
+ 'in' => 'nrb',
+ 'not-in' => 'nrb',
+ 'within' => 'nrb',
+ 'not-within' => 'nrb',
+ 'mod' => 'nnn',
+ ',' => 'rrr',
+ '..' => 'nnr',
+ );
+
+ /**
+ * Map converting from the abbrevation to the full form.
+ */
+ static $typeSpecMap = array(
+ 'b' => 'boolean',
+ 'n' => 'number',
+ 'r' => 'range',
+ );
+
+ function __construct( $parser, $name, $pos, $length ) {
+ parent::__construct( $parser, $pos, $length );
+ $this->name = $name;
+ }
+
+ public function operate( $left, $right ) {
+ $typeSpec = self::$opTypes[$this->name];
+
+ $leftType = self::$typeSpecMap[$typeSpec[0]];
+ $rightType = self::$typeSpecMap[$typeSpec[1]];
+ $resultType = self::$typeSpecMap[$typeSpec[2]];
+
+ $start = min( $this->pos, $left->pos, $right->pos );
+ $end = max( $this->end, $left->end, $right->end );
+ $length = $end - $start;
+
+ $newExpr = new CLDRPluralRuleConverter_Expression( $this->parser, $resultType,
+ "{$left->rpn} {$right->rpn} {$this->name}",
+ $start, $length );
+
+ if ( !$left->isType( $leftType ) ) {
+ $newExpr->error( "invalid type for left operand: expected $leftType, got {$left->type}" );
+ }
+
+ if ( !$right->isType( $rightType ) ) {
+ $newExpr->error( "invalid type for right operand: expected $rightType, got {$right->type}" );
+ }
+ return $newExpr;
+ }
+}
+
+/**
+ * The exception class for all the classes in this file. This will be thrown
+ * back to the caller if there is any validation error.
+ */
+class CLDRPluralRuleError extends MWException {
+ function __construct( $message ) {
+ parent::__construct( 'CLDR plural rule error: ' . $message );
+ }
+}
// Bail if PHP is too low
if ( !function_exists( 'version_compare' ) || version_compare( phpversion(), '5.3.2' ) < 0 ) {
+ // We need to use dirname( __FILE__ ) here cause __DIR__ is PHP5.3+
require( dirname( __FILE__ ) . '/includes/PHPVersionError.php' );
wfPHPVersionError( 'load.php' );
}
* @defgroup Maintenance Maintenance
*/
-// Make sure we're on PHP5 or better
+// Make sure we're on PHP5.3.2 or better
if ( !function_exists( 'version_compare' ) || version_compare( PHP_VERSION, '5.3.2' ) < 0 ) {
+ // We need to use dirname( __FILE__ ) here cause __DIR__ is PHP5.3+
require_once( dirname( __FILE__ ) . '/../includes/PHPVersionError.php' );
wfPHPVersionError( 'cli' );
}
$maintClass = false;
-
/**
* Abstract maintenance class for quickly writing and churning out
* maintenance scripts with minimal effort. All that _must_ be defined
'wantedtemplates-summary',
'activeusers-summary',
'search-summary',
+ 'editpage-head-copy-warn',
'editpage-tos-summary',
'addsection-preload',
'addsection-editintro',
'yourdiff',
'copyrightwarning',
'copyrightwarning2',
+ 'editpage-head-copy-warn',
'editpage-tos-summary',
'longpage-hint',
'longpageerror',
'scripts' => 'resources/jquery/jquery.spinner.js',
'styles' => 'resources/jquery/jquery.spinner.css',
),
+ 'jquery.jStorage' => array(
+ 'scripts' => 'resources/jquery/jquery.jStorage.js',
+ 'dependencies' => 'jquery.json',
+ ),
'jquery.suggestions' => array(
'scripts' => 'resources/jquery/jquery.suggestions.js',
'styles' => 'resources/jquery/jquery.suggestions.css',
--- /dev/null
+/*
+ * ----------------------------- JSTORAGE -------------------------------------
+ * Simple local storage wrapper to save data on the browser side, supporting
+ * all major browsers - IE6+, Firefox2+, Safari4+, Chrome4+ and Opera 10.5+
+ *
+ * Copyright (c) 2010 Andris Reinman, andris.reinman@gmail.com
+ * Project homepage: www.jstorage.info
+ *
+ * Taken from Github with slight modifications by Hoo man
+ * https://raw.github.com/andris9/jStorage/master/jstorage.js
+ *
+ * Licensed under MIT-style license:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/**
+ * $.jStorage
+ *
+ * USAGE:
+ *
+ * jStorage requires Prototype, MooTools or jQuery! If jQuery is used, then
+ * jQuery-JSON (http://code.google.com/p/jquery-json/) is also needed.
+ * (jQuery-JSON needs to be loaded BEFORE jStorage!)
+ *
+ * Methods:
+ *
+ * -set(key, value[, options])
+ * $.jStorage.set(key, value) -> saves a value
+ *
+ * -get(key[, default])
+ * value = $.jStorage.get(key [, default]) ->
+ * retrieves value if key exists, or default if it doesn't
+ *
+ * -deleteKey(key)
+ * $.jStorage.deleteKey(key) -> removes a key from the storage
+ *
+ * -flush()
+ * $.jStorage.flush() -> clears the cache
+ *
+ * -storageObj()
+ * $.jStorage.storageObj() -> returns a read-ony copy of the actual storage
+ *
+ * -storageSize()
+ * $.jStorage.storageSize() -> returns the size of the storage in bytes
+ *
+ * -index()
+ * $.jStorage.index() -> returns the used keys as an array
+ *
+ * -storageAvailable()
+ * $.jStorage.storageAvailable() -> returns true if storage is available
+ *
+ * -reInit()
+ * $.jStorage.reInit() -> reloads the data from browser storage
+ *
+ * <value> can be any JSON-able value, including objects and arrays.
+ *
+ **/
+
+(function($){
+ if(!$ || !($.toJSON || Object.toJSON || window.JSON)){
+ throw new Error("jQuery, MooTools or Prototype needs to be loaded before jStorage!");
+ }
+
+ var
+ /* This is the object, that holds the cached values */
+ _storage = {},
+
+ /* Actual browser storage (localStorage or globalStorage['domain']) */
+ _storage_service = {jStorage:"{}"},
+
+ /* DOM element for older IE versions, holds userData behavior */
+ _storage_elm = null,
+
+ /* How much space does the storage take */
+ _storage_size = 0,
+
+ /* function to encode objects to JSON strings */
+ json_encode = $.toJSON || Object.toJSON || (window.JSON && (JSON.encode || JSON.stringify)),
+
+ /* function to decode objects from JSON strings */
+ json_decode = $.evalJSON || (window.JSON && (JSON.decode || JSON.parse)) || function(str){
+ return String(str).evalJSON();
+ },
+
+ /* which backend is currently used */
+ _backend = false,
+
+ /* Next check for TTL */
+ _ttl_timeout,
+
+ /**
+ * XML encoding and decoding as XML nodes can't be JSON'ized
+ * XML nodes are encoded and decoded if the node is the value to be saved
+ * but not if it's as a property of another object
+ * Eg. -
+ * $.jStorage.set("key", xmlNode); // IS OK
+ * $.jStorage.set("key", {xml: xmlNode}); // NOT OK
+ */
+ _XMLService = {
+
+ /**
+ * Validates a XML node to be XML
+ * based on jQuery.isXML function
+ */
+ isXML: function(elm){
+ var documentElement = (elm ? elm.ownerDocument || elm : 0).documentElement;
+ return documentElement ? documentElement.nodeName !== "HTML" : false;
+ },
+
+ /**
+ * Encodes a XML node to string
+ * based on http://www.mercurytide.co.uk/news/article/issues-when-working-ajax/
+ */
+ encode: function(xmlNode) {
+ if(!this.isXML(xmlNode)){
+ return false;
+ }
+ try{ // Mozilla, Webkit, Opera
+ return new XMLSerializer().serializeToString(xmlNode);
+ }catch(E1) {
+ try { // IE
+ return xmlNode.xml;
+ }catch(E2){}
+ }
+ return false;
+ },
+
+ /**
+ * Decodes a XML node from string
+ * loosely based on http://outwestmedia.com/jquery-plugins/xmldom/
+ */
+ decode: function(xmlString){
+ var dom_parser = ("DOMParser" in window && (new DOMParser()).parseFromString) ||
+ (window.ActiveXObject && function(_xmlString) {
+ var xml_doc = new ActiveXObject('Microsoft.XMLDOM');
+ xml_doc.async = 'false';
+ xml_doc.loadXML(_xmlString);
+ return xml_doc;
+ }),
+ resultXML;
+ if(!dom_parser){
+ return false;
+ }
+ resultXML = dom_parser.call("DOMParser" in window && (new DOMParser()) || window, xmlString, 'text/xml');
+ return this.isXML(resultXML)?resultXML:false;
+ }
+ };
+
+ ////////////////////////// PRIVATE METHODS ////////////////////////
+
+ /**
+ * Initialization function. Detects if the browser supports DOM Storage
+ * or userData behavior and behaves accordingly.
+ * @returns undefined
+ */
+ function _init(){
+ /* Check if browser supports localStorage */
+ var localStorageReallyWorks = false;
+ if("localStorage" in window){
+ try {
+ window.localStorage.setItem('_tmptest', 'tmpval');
+ localStorageReallyWorks = true;
+ window.localStorage.removeItem('_tmptest');
+ } catch(BogusQuotaExceededErrorOnIos5) {
+ // Thanks be to iOS5 Private Browsing mode which throws
+ // QUOTA_EXCEEDED_ERRROR DOM Exception 22.
+ }
+ }
+ if(localStorageReallyWorks){
+ try {
+ if(window.localStorage) {
+ _storage_service = window.localStorage;
+ _backend = "localStorage";
+ }
+ } catch(E3) {/* Firefox fails when touching localStorage and cookies are disabled */}
+ }
+ /* Check if browser supports globalStorage */
+ else if("globalStorage" in window){
+ try {
+ if(window.globalStorage) {
+ _storage_service = window.globalStorage[window.location.hostname];
+ _backend = "globalStorage";
+ }
+ } catch(E4) {/* Firefox fails when touching localStorage and cookies are disabled */}
+ }
+ /* Check if browser supports userData behavior */
+ else {
+ _storage_elm = document.createElement('link');
+ if(_storage_elm.addBehavior){
+
+ /* Use a DOM element to act as userData storage */
+ _storage_elm.style.behavior = 'url(#default#userData)';
+
+ /* userData element needs to be inserted into the DOM! */
+ document.getElementsByTagName('head')[0].appendChild(_storage_elm);
+
+ _storage_elm.load("jStorage");
+ var data = "{}";
+ try{
+ data = _storage_elm.getAttribute("jStorage");
+ }catch(E5){}
+ _storage_service.jStorage = data;
+ _backend = "userDataBehavior";
+ }else{
+ _storage_elm = null;
+ return;
+ }
+ }
+
+ _load_storage();
+
+ // remove dead keys
+ _handleTTL();
+ }
+
+ /**
+ * Loads the data from the storage based on the supported mechanism
+ * @returns undefined
+ */
+ function _load_storage(){
+ /* if jStorage string is retrieved, then decode it */
+ if(_storage_service.jStorage){
+ try{
+ _storage = json_decode(String(_storage_service.jStorage));
+ }catch(E6){_storage_service.jStorage = "{}";}
+ }else{
+ _storage_service.jStorage = "{}";
+ }
+ _storage_size = _storage_service.jStorage?String(_storage_service.jStorage).length:0;
+ }
+
+ /**
+ * This functions provides the "save" mechanism to store the jStorage object
+ * @returns undefined
+ */
+ function _save(){
+ try{
+ _storage_service.jStorage = json_encode(_storage);
+ // If userData is used as the storage engine, additional
+ if(_storage_elm) {
+ _storage_elm.setAttribute("jStorage",_storage_service.jStorage);
+ _storage_elm.save("jStorage");
+ }
+ _storage_size = _storage_service.jStorage?String(_storage_service.jStorage).length:0;
+ }catch(E7){/* probably cache is full, nothing is saved this way*/}
+ }
+
+ /**
+ * Function checks if a key is set and is string or numberic
+ */
+ function _checkKey(key){
+ if(!key || (typeof key !== "string" && typeof key !== "number")){
+ throw new TypeError('Key name must be string or numeric');
+ }
+ if(key === "__jstorage_meta"){
+ throw new TypeError('Reserved key name');
+ }
+ return true;
+ }
+
+ /**
+ * Removes expired keys
+ */
+ function _handleTTL(){
+ var curtime, i, TTL, nextExpire = Infinity, changed = false;
+
+ clearTimeout(_ttl_timeout);
+
+ if(!_storage.__jstorage_meta || typeof _storage.__jstorage_meta.TTL !== "object"){
+ // nothing to do here
+ return;
+ }
+
+ curtime = +new Date();
+ TTL = _storage.__jstorage_meta.TTL;
+ for(i in TTL){
+ if(TTL.hasOwnProperty(i)){
+ if(TTL[i] <= curtime){
+ delete TTL[i];
+ delete _storage[i];
+ changed = true;
+ }else if(TTL[i] < nextExpire){
+ nextExpire = TTL[i];
+ }
+ }
+ }
+
+ // set next check
+ if(nextExpire != Infinity){
+ _ttl_timeout = setTimeout(_handleTTL, nextExpire - curtime);
+ }
+
+ // save changes
+ if(changed){
+ _save();
+ }
+ }
+
+ ////////////////////////// PUBLIC INTERFACE /////////////////////////
+
+ $.jStorage = {
+ /* Version number */
+ version: "0.1.7.0",
+
+ /**
+ * Sets a key's value.
+ *
+ * @param {String} key - Key to set. If this value is not set or not
+ * a string an exception is raised.
+ * @param {Mixed} value - Value to set. This can be any value that is JSON
+ * compatible (Numbers, Strings, Objects etc.).
+ * @param {Object} [options] - possible options to use
+ * @param {Number} [options.TTL] - optional TTL value
+ * @returns the used value
+ */
+ set: function(key, value, options){
+ _checkKey(key);
+
+ options = options || {};
+
+ if(_XMLService.isXML(value)){
+ value = {_is_xml:true,xml:_XMLService.encode(value)};
+ }else if(typeof value === "function"){
+ value = null; // functions can't be saved!
+ }else if(value && typeof value === "object"){
+ // clone the object before saving to _storage tree
+ value = json_decode(json_encode(value));
+ }
+ _storage[key] = value;
+
+ if(!isNaN(options.TTL)){
+ this.setTTL(key, options.TTL);
+ // also handles saving
+ }else{
+ _save();
+ }
+ return value;
+ },
+
+ /**
+ * Looks up a key in cache
+ *
+ * @param {String} key - Key to look up.
+ * @param {mixed} def - Default value to return, if key didn't exist.
+ * @returns the key value, default value or <null>
+ */
+ get: function(key, def){
+ _checkKey(key);
+ if(key in _storage){
+ if(_storage[key] && typeof _storage[key] === "object" &&
+ _storage[key]._is_xml &&
+ _storage[key]._is_xml){
+ return _XMLService.decode(_storage[key].xml);
+ }else{
+ return _storage[key];
+ }
+ }
+ return typeof(def) === 'undefined' ? null : def;
+ },
+
+ /**
+ * Deletes a key from cache.
+ *
+ * @param {String} key - Key to delete.
+ * @returns true if key existed or false if it didn't
+ */
+ deleteKey: function(key){
+ _checkKey(key);
+ if(key in _storage){
+ delete _storage[key];
+ // remove from TTL list
+ if(_storage.__jstorage_meta &&
+ typeof _storage.__jstorage_meta.TTL === "object" &&
+ key in _storage.__jstorage_meta.TTL){
+ delete _storage.__jstorage_meta.TTL[key];
+ }
+ _save();
+ return true;
+ }
+ return false;
+ },
+
+ /**
+ * Sets a TTL for a key, or remove it if ttl value is 0 or below
+ *
+ * @param {String} key - key to set the TTL for
+ * @param {Number} ttl - TTL timeout in milliseconds
+ * @returns true if key existed or false if it didn't
+ */
+ setTTL: function(key, ttl){
+ var curtime = +new Date();
+ _checkKey(key);
+ ttl = Number(ttl) || 0;
+ if(key in _storage){
+
+ if(!_storage.__jstorage_meta){
+ _storage.__jstorage_meta = {};
+ }
+ if(!_storage.__jstorage_meta.TTL){
+ _storage.__jstorage_meta.TTL = {};
+ }
+
+ // Set TTL value for the key
+ if(ttl>0){
+ _storage.__jstorage_meta.TTL[key] = curtime + ttl;
+ }else{
+ delete _storage.__jstorage_meta.TTL[key];
+ }
+
+ _save();
+
+ _handleTTL();
+ return true;
+ }
+ return false;
+ },
+
+ /**
+ * Deletes everything in cache.
+ *
+ * @return true
+ */
+ flush: function(){
+ _storage = {};
+ _save();
+ return true;
+ },
+
+ /**
+ * Returns a read-only copy of _storage
+ *
+ * @returns Object
+ */
+ storageObj: function(){
+ function F() {}
+ F.prototype = _storage;
+ return new F();
+ },
+
+ /**
+ * Returns an index of all used keys as an array
+ * ['key1', 'key2',..'keyN']
+ *
+ * @returns Array
+ */
+ index: function(){
+ var index = [], i;
+ for(i in _storage){
+ if(_storage.hasOwnProperty(i) && i !== "__jstorage_meta"){
+ index.push(i);
+ }
+ }
+ return index;
+ },
+
+ /**
+ * How much space in bytes does the storage take?
+ *
+ * @returns Number
+ */
+ storageSize: function(){
+ return _storage_size;
+ },
+
+ /**
+ * Which backend is currently in use?
+ *
+ * @returns String
+ */
+ currentBackend: function(){
+ return _backend;
+ },
+
+ /**
+ * Test if storage is available
+ *
+ * @returns Boolean
+ */
+ storageAvailable: function(){
+ return !!_backend;
+ },
+
+ /**
+ * Reloads the data from browser storage
+ *
+ * @returns undefined
+ */
+ reInit: function(){
+ var new_storage_elm, data;
+ if(_storage_elm && _storage_elm.addBehavior){
+ new_storage_elm = document.createElement('link');
+
+ _storage_elm.parentNode.replaceChild(new_storage_elm, _storage_elm);
+ _storage_elm = new_storage_elm;
+
+ /* Use a DOM element to act as userData storage */
+ _storage_elm.style.behavior = 'url(#default#userData)';
+
+ /* userData element needs to be inserted into the DOM! */
+ document.getElementsByTagName('head')[0].appendChild(_storage_elm);
+
+ _storage_elm.load("jStorage");
+ data = "{}";
+ try{
+ data = _storage_elm.getAttribute("jStorage");
+ }catch(E5){}
+ _storage_service.jStorage = data;
+ _backend = "userDataBehavior";
+ }
+
+ _load_storage();
+ }
+ };
+
+ // Initialize jStorage
+ _init();
+
+})(window.$ || window.jQuery);
--- /dev/null
+/**
+ * CLDR related utility methods
+ */
+( function( mw ) {
+ "use strict";
+
+ var cldr = {
+ /**
+ * For the number, get the plural for index
+ * In case none of the rules passed, we return pluralRules.length
+ * That means it is the "other" form.
+ * @param number
+ * @param pluralRules
+ * @return plural form index
+ */
+ getPluralForm: function( number, pluralRules ) {
+ var pluralFormIndex = 0;
+ for ( pluralFormIndex = 0; pluralFormIndex < pluralRules.length; pluralFormIndex++ ) {
+ if ( mw.libs.pluralRuleParser( pluralRules[pluralFormIndex], number ) ) {
+ break;
+ }
+ }
+ return pluralFormIndex;
+ }
+ };
+
+ mw.cldr = cldr;
+} )( mediaWiki );
--- /dev/null
+/* This is cldrpluralparser 1.0, ported to MediaWiki ResourceLoader */
+
+/**
+* cldrpluralparser.js
+* A parser engine for CLDR plural rules.
+*
+* Copyright 2012 GPLV3+, Santhosh Thottingal
+*
+* @version 0.1.0-alpha
+* @source https://github.com/santhoshtr/CLDRPluralRuleParser
+* @author Santhosh Thottingal <santhosh.thottingal@gmail.com>
+* @author Timo Tijhof
+* @author Amir Aharoni
+*/
+
+/**
+ * Evaluates a plural rule in CLDR syntax for a number
+ * @param rule
+ * @param number
+ * @return true|false|null
+ */
+( function( mw ) {
+
+function pluralRuleParser(rule, number) {
+ /*
+ Syntax: see http://unicode.org/reports/tr35/#Language_Plural_Rules
+ -----------------------------------------------------------------
+
+ condition = and_condition ('or' and_condition)*
+ and_condition = relation ('and' relation)*
+ relation = is_relation | in_relation | within_relation | 'n' <EOL>
+ is_relation = expr 'is' ('not')? value
+ in_relation = expr ('not')? 'in' range_list
+ within_relation = expr ('not')? 'within' range_list
+ expr = 'n' ('mod' value)?
+ range_list = (range | value) (',' range_list)*
+ value = digit+
+ digit = 0|1|2|3|4|5|6|7|8|9
+ range = value'..'value
+
+ */
+ // Indicates current position in the rule as we parse through it.
+ // Shared among all parsing functions below.
+ var pos = 0;
+
+ var whitespace = makeRegexParser(/^\s+/);
+ var digits = makeRegexParser(/^\d+/);
+
+ var _n_ = makeStringParser('n');
+ var _is_ = makeStringParser('is');
+ var _mod_ = makeStringParser('mod');
+ var _not_ = makeStringParser('not');
+ var _in_ = makeStringParser('in');
+ var _within_ = makeStringParser('within');
+ var _range_ = makeStringParser('..');
+ var _comma_ = makeStringParser(',');
+ var _or_ = makeStringParser('or');
+ var _and_ = makeStringParser('and');
+
+ function debug() {
+ /* console.log.apply(console, arguments);*/
+ }
+
+ debug('pluralRuleParser', rule, number);
+
+ // Try parsers until one works, if none work return null
+ function choice(parserSyntax) {
+ return function () {
+ for (var i = 0; i < parserSyntax.length; i++) {
+ var result = parserSyntax[i]();
+ if (result !== null) {
+ return result;
+ }
+ }
+ return null;
+ };
+ }
+
+ // Try several parserSyntax-es in a row.
+ // All must succeed; otherwise, return null.
+ // This is the only eager one.
+ function sequence(parserSyntax) {
+ var originalPos = pos;
+ var result = [];
+ for (var i = 0; i < parserSyntax.length; i++) {
+ var res = parserSyntax[i]();
+ if (res === null) {
+ pos = originalPos;
+ return null;
+ }
+ result.push(res);
+ }
+ return result;
+ }
+
+ // Run the same parser over and over until it fails.
+ // Must succeed a minimum of n times; otherwise, return null.
+ function nOrMore(n, p) {
+ return function () {
+ var originalPos = pos;
+ var result = [];
+ var parsed = p();
+ while (parsed !== null) {
+ result.push(parsed);
+ parsed = p();
+ }
+ if (result.length < n) {
+ pos = originalPos;
+ return null;
+ }
+ return result;
+ };
+ }
+
+ // Helpers -- just make parserSyntax out of simpler JS builtin types
+
+ function makeStringParser(s) {
+ var len = s.length;
+ return function () {
+ var result = null;
+ if (rule.substr(pos, len) === s) {
+ result = s;
+ pos += len;
+ }
+ return result;
+ };
+ }
+
+ function makeRegexParser(regex) {
+ return function () {
+ var matches = rule.substr(pos).match(regex);
+ if (matches === null) {
+ return null;
+ }
+ pos += matches[0].length;
+ return matches[0];
+ };
+ }
+
+ function n() {
+ var result = _n_();
+ if (result === null) {
+ debug(" -- failed n");
+ return result;
+ }
+ result = parseInt(number, 10);
+ debug(" -- passed n ", result);
+ return result;
+ }
+
+ var expression = choice([mod, n]);
+
+ function mod() {
+ var result = sequence([n, whitespace, _mod_, whitespace, digits]);
+ if (result === null) {
+ debug(" -- failed mod");
+ return null;
+ }
+ debug(" -- passed mod");
+ return parseInt(result[0], 10) % parseInt(result[4], 10);
+ }
+
+ function not() {
+ var result = sequence([whitespace, _not_]);
+ if (result === null) {
+ debug(" -- failed not");
+ return null;
+ } else {
+ return result[1];
+ }
+ }
+
+ function is() {
+ var result = sequence([expression, whitespace, _is_, nOrMore(0, not), whitespace, digits]);
+ if (result !== null) {
+ debug(" -- passed is");
+ if (result[3][0] === 'not') {
+ return result[0] !== parseInt(result[5], 10);
+ } else {
+ return result[0] === parseInt(result[5], 10);
+ }
+ }
+ debug(" -- failed is");
+ return null;
+ }
+
+ function rangeList() {
+ // range_list = (range | value) (',' range_list)*
+ var result = sequence([choice([range, digits]), nOrMore(0, rangeTail)]);
+ var resultList = [];
+ if (result !== null) {
+ resultList = resultList.concat(result[0], result[1][0]);
+ return resultList;
+ }
+ debug(" -- failed rangeList");
+ return null;
+ }
+
+ function rangeTail() {
+ // ',' range_list
+ var result = sequence([_comma_, rangeList]);
+ if (result !== null) {
+ return result[1];
+ }
+ debug(" -- failed rangeTail");
+ return null;
+ }
+
+ function range() {
+ var result = sequence([digits, _range_, digits]);
+ if (result !== null) {
+ debug(" -- passed range");
+ var array = [];
+ var left = parseInt(result[0], 10);
+ var right = parseInt(result[2], 10);
+ for ( i = left; i <= right; i++) {
+ array.push(i);
+ }
+ return array;
+ }
+ debug(" -- failed range");
+ return null;
+ }
+
+ function _in() {
+ // in_relation = expr ('not')? 'in' range_list
+ var result = sequence([expression, nOrMore(0, not), whitespace, _in_, whitespace, rangeList]);
+ if (result !== null) {
+ debug(" -- passed _in");
+ var range_list = result[5];
+ for (var i = 0; i < range_list.length; i++) {
+ if (parseInt(range_list[i], 10) === result[0]) {
+ return (result[1][0] !== 'not');
+ }
+ }
+ return (result[1][0] === 'not');
+ }
+ debug(" -- failed _in ");
+ return null;
+ }
+
+ function within() {
+ var result = sequence([expression, whitespace, _within_, whitespace, rangeList]);
+ if (result !== null) {
+ debug(" -- passed within ");
+ var range_list = result[4];
+ return (parseInt( range_list[0],10 )<= result[0] && result[0] <= parseInt( range_list[1], 10));
+ }
+ debug(" -- failed within ");
+ return null;
+ }
+
+
+ var relation = choice([is, _in, within]);
+
+ function and() {
+ var result = sequence([relation, whitespace, _and_, whitespace, condition]);
+ if (result) {
+ debug(" -- passed and");
+ return result[0] && result[4];
+ }
+ debug(" -- failed and");
+ return null;
+ }
+
+ function or() {
+ var result = sequence([relation, whitespace, _or_, whitespace, condition]);
+ if (result) {
+ debug(" -- passed or");
+ return result[0] || result[4];
+ }
+ debug(" -- failed or");
+ return null;
+ }
+
+ var condition = choice([and, or, relation]);
+
+ function start() {
+ var result = condition();
+ return result;
+ }
+
+
+ var result = start();
+
+ /*
+ * For success, the pos must have gotten to the end of the rule
+ * and returned a non-null.
+ * n.b. This is part of language infrastructure, so we do not throw an internationalizable message.
+ */
+ if (result === null || pos !== rule.length) {
+ // throw new Error("Parse error at position " + pos.toString() + " in input: " + rule + " result is " + result);
+ }
+
+ return result;
+}
+
+/* For module loaders, e.g. NodeJS, NPM */
+if (typeof module !== 'undefined' && module.exports) {
+ module.exports = pluralRuleParser;
+}
+
+/* pluralRuleParser ends here */
+mw.libs.pluralRuleParser = pluralRuleParser;
+
+} )( mediaWiki );
wfProfileOut( __METHOD__ . '-1' );
wfProfileIn( __METHOD__ . '-2' );
$l = $this->getSkin()->getLanguage()->alignStart();
- $s .= "<td class='bottom' align='$l' valign='top'>";
+ $s .= "<td class='bottom' style='text-align: $l; vertical-align: top;'>";
$s .= $this->bottomLinks();
$s .= "\n<br />" . $this->getSkin()->getLanguage()->pipeList( array(
--- /dev/null
+<?php
+
+$result = array( 'xmp-exif' =>
+ array(
+ 'GPSAltitude' => -3.14159265301,
+ 'GPSDOP' => '5/1',
+ 'GPSLatitude' => 88.51805555,
+ 'GPSLongitude' => -21.12356945,
+ 'GPSVersionID' => '2.2.0.0'
+ )
+);
+
--- /dev/null
+<?xpacket begin='' id='W5M0MpCehiHzreSzNTczkc9d'?>
+<x:xmpmeta xmlns:x='adobe:ns:meta/' x:xmptk='Image::ExifTool 7.30'>
+<rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'>
+
+ <rdf:Description rdf:about=''
+ xmlns:exif='http://ns.adobe.com/exif/1.0/'>
+ <exif:GPSAltitude>103993/33102</exif:GPSAltitude>
+ <exif:GPSAltitudeRef>1</exif:GPSAltitudeRef>
+ <exif:GPSDOP>5/1</exif:GPSDOP>
+ <exif:GPSLatitude>88,31.083333N</exif:GPSLatitude>
+ <exif:GPSLongitude>21,7.414167W</exif:GPSLongitude>
+ <exif:GPSVersionID>2.2.0.0</exif:GPSVersionID>
+ </rdf:Description>
+
+</rdf:RDF>
+</x:xmpmeta>
+<?xpacket end='w'?>
}
/** Clear log before each test */
MWDebug::clearLog();
+ wfSuppressWarnings();
+ }
+
+ function tearDown() {
+ wfRestoreWarnings();
}
function testAddLog() {
"Correct file size of '$path'" );
$this->assertTrue( abs( time() - wfTimestamp( TS_UNIX, $time ) ) < 10,
"Correct file timestamp of '$path'" );
+
+ $this->backend->clearCache( array( $path ) );
+
+ $size = $this->backend->getFileSize( array( 'src' => $path ) );
+
+ $this->assertEquals( strlen( $content ), $size,
+ "Correct file size of '$path'" );
+
+ $this->backend->preloadCache( array( $path ) );
+
+ $size = $this->backend->getFileSize( array( 'src' => $path ) );
+
+ $this->assertEquals( strlen( $content ), $size,
+ "Correct file size of '$path'" );
} else {
$size = $this->backend->getFileSize( array( 'src' => $path ) );
$time = $this->backend->getFileTimestamp( array( 'src' => $path ) );
--- /dev/null
+<?php
+/**
+ * @author Niklas Laxström
+ * @file
+ */
+
+class CLDRPluralRuleEvaluatorTest extends MediaWikiTestCase {
+ /**
+ * @dataProvider validTestCases
+ */
+ function testValidRules( $expected, $rules, $number, $comment ) {
+ $result = CLDRPluralRuleEvaluator::evaluate( $number, (array) $rules );
+ $this->assertEquals( $expected, $result, $comment );
+ }
+
+ /**
+ * @dataProvider invalidTestCases
+ * @expectedException CLDRPluralRuleError
+ */
+ function testInvalidRules( $rules, $comment ) {
+ CLDRPluralRuleEvaluator::evaluate( 1, (array) $rules );
+ }
+
+ function validTestCases() {
+ $tests = array(
+ # expected, number, rule, comment
+ array( 0, 'n is 1', 1, 'integer number and is' ),
+ array( 0, 'n is 1', "1", 'string integer number and is' ),
+ array( 0, 'n is 1', 1.0, 'float number and is' ),
+ array( 0, 'n is 1', "1.0", 'string float number and is' ),
+ array( 1, 'n is 1', 1.1, 'float number and is' ),
+ array( 1, 'n is 1', 2, 'float number and is' ),
+
+ array( 0, 'n in 1,3,5', 3, '' ),
+ array( 1, 'n not in 1,3,5', 5, '' ),
+
+ array( 1, 'n in 1,3,5', 2, '' ),
+ array( 0, 'n not in 1,3,5', 4, '' ),
+
+ array( 0, 'n in 1..3', 2, '' ),
+ array( 0, 'n in 1..3', 3, 'in is inclusive' ),
+ array( 1, 'n in 1..3', 0, '' ),
+
+ array( 1, 'n not in 1..3', 2, '' ),
+ array( 1, 'n not in 1..3', 3, 'in is inclusive' ),
+ array( 0, 'n not in 1..3', 0, '' ),
+
+ array( 1, 'n is not 1 and n is not 2 and n is not 3', 1, 'and relation' ),
+ array( 0, 'n is not 1 and n is not 2 and n is not 4', 3, 'and relation' ),
+
+ array( 0, 'n is not 1 or n is 1', 1, 'or relation' ),
+ array( 1, 'n is 1 or n is 2', 3, 'or relation' ),
+
+ array( 0, 'n is 1', 1, 'extra whitespace' ),
+
+ array( 0, 'n mod 3 is 1', 7, 'mod' ),
+ array( 0, 'n mod 3 is not 1', 4.3, 'mod with floats' ),
+
+ array( 0, 'n within 1..3', 2, 'within with integer' ),
+ array( 0, 'n within 1..3', 2.5, 'within with float' ),
+ array( 0, 'n in 1..3', 2, 'in with integer' ),
+ array( 1, 'n in 1..3', 2.5, 'in with float' ),
+
+ array( 0, 'n in 3 or n is 4 and n is 5', 3, 'and binds more tightly than or' ),
+ array( 1, 'n is 3 or n is 4 and n is 5', 4, 'and binds more tightly than or' ),
+
+ array( 0, 'n mod 10 in 3..4,9 and n mod 100 not in 10..19,70..79,90..99', 24, 'breton rule' ),
+ array( 1, 'n mod 10 in 3..4,9 and n mod 100 not in 10..19,70..79,90..99', 25, 'breton rule' ),
+
+ array( 0, 'n within 0..2 and n is not 2', 0, 'french rule' ),
+ array( 0, 'n within 0..2 and n is not 2', 1, 'french rule' ),
+ array( 0, 'n within 0..2 and n is not 2', 1.2, 'french rule' ),
+ array( 1, 'n within 0..2 and n is not 2', 2, 'french rule' ),
+
+ array( 1, 'n in 3..10,13..19', 2, 'scottish rule - ranges with comma' ),
+ array( 0, 'n in 3..10,13..19', 4, 'scottish rule - ranges with comma' ),
+ array( 1, 'n in 3..10,13..19', 12.999, 'scottish rule - ranges with comma' ),
+ array( 0, 'n in 3..10,13..19', 13, 'scottish rule - ranges with comma' ),
+
+ array( 0, '5 mod 3 is n', 2, 'n as result of mod - no need to pass' ),
+ );
+
+ return $tests;
+ }
+
+ function invalidTestCases() {
+ $tests = array(
+ array( 'n mod mod 5 is 1', 'mod mod' ),
+ array( 'n', 'just n' ),
+ array( 'n is in 5', 'is in' ),
+ );
+ return $tests;
+ }
+
+}
--- /dev/null
+module( 'mediawiki.cldr' );
+
+test( '-- Initial check', function() {
+ expect( 1 );
+ ok( mw.cldr, 'mw.cldr defined' );
+} );
+
+var pluralTestcases = {
+ /*
+ * Sample:
+ *"languagecode" : [
+ * [ number, [ "form1", "form2", ... ], "expected", "description" ],
+ * ]
+ */
+ "en": [
+ [ 0, [ "one", "other" ], "other", "English plural test- 0 is other" ],
+ [ 1, [ "one", "other" ], "one", "English plural test- 1 is one" ]
+ ],
+ "hi": [
+ [ 0, [ "one", "other" ], "one", "Hindi plural test- 0 is one" ],
+ [ 1, [ "one", "other" ], "one", "Hindi plural test- 1 is one" ],
+ [ 2, [ "one", "other" ], "other", "Hindi plural test- 2 is other" ]
+ ],
+ "he": [
+ [ 0, [ "one", "other" ], "other", "Hebrew plural test- 0 is other" ],
+ [ 1, [ "one", "other" ], "one", "Hebrew plural test- 1 is one" ],
+ [ 2, [ "one", "other" ], "other", "Hebrew plural test- 2 is other with 2 forms" ],
+ [ 2, [ "one", "dual", "other" ], "dual", "Hebrew plural test- 2 is dual with 3 forms" ]
+ ],
+ "ar": [
+ [ 0, [ "zero", "one", "two", "few", "many", "other" ], "zero", "Arabic plural test - 0 is zero" ],
+ [ 1, [ "zero", "one", "two", "few", "many", "other" ], "one", "Arabic plural test - 1 is one" ],
+ [ 2, [ "zero", "one", "two", "few", "many", "other" ], "two", "Arabic plural test - 2 is two" ],
+ [ 3, [ "zero", "one", "two", "few", "many", "other" ], "few", "Arabic plural test - 3 is few" ],
+ [ 9, [ "zero", "one", "two", "few", "many", "other" ], "few", "Arabic plural test - 9 is few" ],
+ [ "9", [ "zero", "one", "two", "few", "many", "other" ], "few", "Arabic plural test - 9 is few" ],
+ [ 110, [ "zero", "one", "two", "few", "many", "other" ], "few", "Arabic plural test - 110 is few" ],
+ [ 11, [ "zero", "one", "two", "few", "many", "other" ], "many", "Arabic plural test - 11 is many" ],
+ [ 15, [ "zero", "one", "two", "few", "many", "other" ], "many", "Arabic plural test - 15 is many" ],
+ [ 99, [ "zero", "one", "two", "few", "many", "other" ], "many", "Arabic plural test - 99 is many" ],
+ [ 9999, [ "zero", "one", "two", "few", "many", "other" ], "many", "Arabic plural test - 9999 is many" ],
+ [ 100, [ "zero", "one", "two", "few", "many", "other" ], "other", "Arabic plural test - 100 is other" ],
+ [ 102, [ "zero", "one", "two", "few", "many", "other" ], "other", "Arabic plural test - 102 is other" ],
+ [ 1000, [ "zero", "one", "two", "few", "many", "other" ], "other", "Arabic plural test - 1000 is other" ]
+ // FIXME plural rules for decimal numbers does not work
+ // [ 1.7, [ "zero", "one", "two", "few", "many", "other" ], "other", "Arabic plural test - 1.7 is other" ],
+ ]
+};
+
+function pluralTest( langCode, tests ) {
+ QUnit.test('-- Plural Test for ' + langCode, function( assert ) {
+ QUnit.expect( tests.length );
+ for ( var i = 0; i < tests.length; i++ ) {
+ assert.equal(
+ mw.language.convertPlural( tests[i][0], tests[i][1] ),
+ tests[i][2], // Expected plural form
+ tests[i][3] // Description
+ );
+ }
+ } );
+}
+
+$.each( pluralTestcases, function( langCode, tests ) {
+ if ( langCode === mw.config.get( 'wgUserLanguage' ) ) {
+ pluralTest( langCode, tests );
+ }
+} );