Merge "ChangesListSpecialPage: Stop mutating $opts in buildMainQueryConds()"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Thu, 30 Jan 2014 20:00:59 +0000 (20:00 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Thu, 30 Jan 2014 20:00:59 +0000 (20:00 +0000)
65 files changed:
CREDITS
RELEASE-NOTES-1.23
includes/AutoLoader.php
includes/Collation.php
includes/Defines.php
includes/EditPage.php
includes/Exception.php
includes/Wiki.php
includes/api/ApiFormatXml.php
includes/api/ApiQueryRevisions.php
includes/htmlform/HTMLForm.php
includes/installer/Installer.php
includes/installer/PostgresUpdater.php
includes/installer/WebInstallerPage.php
includes/job/Job.php
includes/job/JobQueueDB.php
includes/job/JobQueueGroup.php
includes/job/JobQueueRedis.php
includes/job/JobSpecification.php [new file with mode: 0644]
includes/job/jobs/RefreshLinksJob.php
includes/libs/CSSMin.php
includes/logging/LogEventsList.php
includes/media/Bitmap.php
includes/media/DjVu.php
includes/media/ImageHandler.php
includes/parser/CoreParserFunctions.php
includes/specials/SpecialContributions.php
includes/specials/SpecialPrefixindex.php
includes/specials/SpecialRevisiondelete.php
includes/specials/SpecialSearch.php
includes/templates/Usercreate.php
includes/templates/Userlogin.php
includes/upload/UploadStash.php
includes/utils/ArrayUtils.php
maintenance/checkSyntax.php
maintenance/doMaintenance.php
maintenance/eval.php
maintenance/postgres/tables.sql
resources/mediawiki.action/mediawiki.action.edit.editWarning.js
resources/mediawiki.language/languages/he.js
resources/mediawiki.less/mediawiki.mixins.less
resources/mediawiki.special/mediawiki.special.css
resources/mediawiki.special/mediawiki.special.preferences.js
resources/mediawiki.ui/components/default/buttons.less
resources/mediawiki.ui/mixins/effects.less
resources/mediawiki.ui/mixins/forms.less
resources/mediawiki.ui/settings/colors.less
resources/mediawiki.ui/settings/typography.less
resources/mediawiki/mediawiki.inspect.js
resources/mediawiki/mediawiki.jqueryMsg.js
resources/mediawiki/mediawiki.js
resources/mediawiki/mediawiki.searchSuggest.js
skins/vector/components/watchstar.less
skins/vector/images/unwatch-icon-hl.png [new file with mode: 0644]
skins/vector/images/unwatch-icon-hl.svg [new file with mode: 0644]
skins/vector/images/unwatch-icon.png [new file with mode: 0644]
skins/vector/images/unwatch-icon.svg [new file with mode: 0644]
skins/vector/images/watch-icon-hl.png [new file with mode: 0644]
skins/vector/images/watch-icon-hl.svg [new file with mode: 0644]
skins/vector/images/watch-icon-loading.gif [deleted file]
skins/vector/images/watch-icon-loading.png [new file with mode: 0644]
skins/vector/images/watch-icon-loading.svg [new file with mode: 0644]
skins/vector/images/watch-icon.png [new file with mode: 0644]
skins/vector/images/watch-icon.svg [new file with mode: 0644]
skins/vector/images/watch-icons.png [deleted file]

diff --git a/CREDITS b/CREDITS
index 21db850..eee4794 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -216,6 +216,7 @@ following names for their contribution to the product.
 * Salvatore Ingala
 * Santhosh Thottingal
 * Scott Colcord
+* se4598
 * Sébastien Santoro
 * Simon Walker
 * Solitarius
index 57080b7..cd602a1 100644 (file)
@@ -32,6 +32,8 @@ production.
   the system message 'emailsender' should be modified (default: "{{SITENAME}}").
 * $wgDBAhandler was removed as the only class using it was also removed
 * The 'max threads' setting was removed from $wgDBservers.
+* Support for AdminSettings.php has been completely removed. All configuration
+  belongs in LocalSettings.php.
 
 === New features in 1.23 ===
 * ResourceLoader can utilize the Web Storage API to cache modules client-side.
@@ -114,6 +116,7 @@ production.
   compiled without support for MySQL yet with support for another DBMS.
 * (bug 56199) Raw option of parser functions must now match complete word,
   to take effect.
+* (bug 60543) Special:PrefixIndex forgot stripprefix=1 for "Next page" link
 
 === Web API changes in 1.23 ===
 * (bug 54884) action=parse&prop=categories now indicates hidden and missing
index a3b0dd9..0f1cded 100644 (file)
@@ -640,6 +640,7 @@ $wgAutoloadLocalClasses = array(
        'WebInstallerPage' => 'includes/installer/WebInstallerPage.php',
 
        # includes/job
+       'IJobSpecification' => 'includes/job/JobSpecification.php',
        'Job' => 'includes/job/Job.php',
        'JobQueue' => 'includes/job/JobQueue.php',
        'JobQueueAggregator' => 'includes/job/aggregator/JobQueueAggregator.php',
@@ -651,6 +652,7 @@ $wgAutoloadLocalClasses = array(
        'JobQueueGroup' => 'includes/job/JobQueueGroup.php',
        'JobQueueFederated' => 'includes/job/JobQueueFederated.php',
        'JobQueueRedis' => 'includes/job/JobQueueRedis.php',
+       'JobSpecification' => 'includes/job/JobSpecification.php',
 
        # includes/job/jobs
        'DoubleRedirectJob' => 'includes/job/jobs/DoubleRedirectJob.php',
index b51256b..d2a5797 100644 (file)
@@ -514,7 +514,7 @@ class IcuCollation extends Collation {
         * Do a binary search, and return the index of the largest item that sorts
         * less than or equal to the target value.
         *
-        * @deprecated in 1.22; use ArrayUtils::findLowerBound() instead
+        * @deprecated in 1.23; use ArrayUtils::findLowerBound() instead
         *
         * @param array $valueCallback A function to call to get the value with
         *     a given array index.
@@ -528,7 +528,7 @@ class IcuCollation extends Collation {
         *     sorts before all items.
         */
        function findLowerBound( $valueCallback, $valueCount, $comparisonCallback, $target ) {
-               wfDeprecated( __METHOD__, '1.22' );
+               wfDeprecated( __METHOD__, '1.23' );
                return ArrayUtils::findLowerBound( $valueCallback, $valueCount, $comparisonCallback, $target );
        }
 
index 015ea9c..19801ec 100644 (file)
@@ -165,11 +165,6 @@ define( 'ALF_NO_BLOCK_LOCK', 8 );
  * Date format selectors; used in user preference storage and by
  * Language::date() and co.
  */
-/*define( 'MW_DATE_DEFAULT', '0' );
-define( 'MW_DATE_MDY', '1' );
-define( 'MW_DATE_DMY', '2' );
-define( 'MW_DATE_YMD', '3' );
-define( 'MW_DATE_ISO', 'ISO 8601' );*/
 define( 'MW_DATE_DEFAULT', 'default' );
 define( 'MW_DATE_MDY', 'mdy' );
 define( 'MW_DATE_DMY', 'dmy' );
index aefadbe..fbfe3ed 100644 (file)
@@ -930,10 +930,6 @@ class EditPage {
                                $undo = $wgRequest->getInt( 'undo' );
 
                                if ( $undo > 0 && $undoafter > 0 ) {
-                                       if ( $undo < $undoafter ) {
-                                               # If they got undoafter and undo round the wrong way, switch them
-                                               list( $undo, $undoafter ) = array( $undoafter, $undo );
-                                       }
 
                                        $undorev = Revision::newFromId( $undo );
                                        $oldrev = Revision::newFromId( $undoafter );
@@ -942,8 +938,6 @@ class EditPage {
                                        # the revisions exist and they were not deleted.
                                        # Otherwise, $content will be left as-is.
                                        if ( !is_null( $undorev ) && !is_null( $oldrev ) &&
-                                               $undorev->getPage() == $oldrev->getPage() &&
-                                               $undorev->getPage() == $this->mTitle->getArticleID() &&
                                                !$undorev->isDeleted( Revision::DELETED_TEXT ) &&
                                                !$oldrev->isDeleted( Revision::DELETED_TEXT ) ) {
 
@@ -3316,7 +3310,7 @@ HTML
         */
        static function getEditToolbar() {
                global $wgStylePath, $wgContLang, $wgLang, $wgOut;
-               global $wgUseTeX, $wgEnableUploads, $wgForeignFileRepos;
+               global $wgEnableUploads, $wgForeignFileRepos;
 
                $imagesAvailable = $wgEnableUploads || count( $wgForeignFileRepos );
 
@@ -3397,7 +3391,7 @@ HTML
                                'tip'    => wfMessage( 'media_tip' )->text(),
                                'key'    => 'M'
                        ) : false,
-                       $wgUseTeX ? array(
+                       class_exists( 'MathRenderer' ) ? array(
                                'image'  => $wgLang->getImageFile( 'button-math' ),
                                'id'     => 'mw-editbutton-math',
                                'open'   => "<math>",
index 5377add..d8c7b6a 100644 (file)
@@ -317,7 +317,7 @@ class ErrorPageError extends MWException {
         * @param string|Message $msg Message key (string) for error text, or a Message object
         * @param array $params with parameters to wfMessage()
         */
-       function __construct( $title, $msg, $params = null ) {
+       function __construct( $title, $msg, $params = array() ) {
                $this->title = $title;
                $this->msg = $msg;
                $this->params = $params;
@@ -356,7 +356,7 @@ class BadTitleError extends ErrorPageError {
         * @param string|Message $msg A message key (default: 'badtitletext')
         * @param array $params parameter to wfMessage()
         */
-       function __construct( $msg = 'badtitletext', $params = null ) {
+       function __construct( $msg = 'badtitletext', $params = array() ) {
                parent::__construct( 'badtitle', $msg, $params );
        }
 
@@ -426,7 +426,7 @@ class ReadOnlyError extends ErrorPageError {
                parent::__construct(
                        'readonly',
                        'readonlytext',
-                       wfReadOnlyReason()
+                       wfReadOnlyReason() ?: array()
                );
        }
 }
@@ -501,12 +501,12 @@ class UserNotLoggedIn extends ErrorPageError {
         * @param string $titleMsg A message key to set the page title.
         *        Optional, default: 'exception-nologin'
         * @param array $params Parameters to wfMessage().
-        *        Optional, default: null
+        *        Optional, default: array()
         */
        public function __construct(
                $reasonMsg = 'exception-nologin-text',
                $titleMsg = 'exception-nologin',
-               $params = null
+               $params = array()
        ) {
                parent::__construct( $titleMsg, $reasonMsg, $params );
        }
index 9a6e6b9..56d23b1 100644 (file)
@@ -637,12 +637,19 @@ class MediaWiki {
                }
 
                if ( !wfShellExecDisabled() && is_executable( $wgPhpCli ) ) {
-                       // Start a background process to run some of the jobs.
-                       // This will be asynchronous on *nix though not on Windows.
+                       // Start a background process to run some of the jobs
                        wfProfileIn( __METHOD__ . '-exec' );
                        $retVal = 1;
                        $cmd = wfShellWikiCmd( "$IP/maintenance/runJobs.php", array( '--maxjobs', $n ) );
-                       wfShellExec( "$cmd &", $retVal );
+                       $cmd .= " >" . wfGetNull() . " 2>&1"; // don't hang PHP on pipes
+                       if ( wfIsWindows() ) {
+                               // Using START makes this async and also works around a bug where using
+                               // wfShellExec() with a quoted script name causes a filename syntax error.
+                               $cmd = "START /B \"bg\" $cmd";
+                       } else {
+                               $cmd = "$cmd &";
+                       }
+                       wfShellExec( $cmd, $retVal );
                        wfProfileOut( __METHOD__ . '-exec' );
                } else {
                        try {
index e707eb4..764e609 100644 (file)
@@ -156,6 +156,13 @@ class ApiFormatXml extends ApiFormatBase {
                                } elseif ( is_array( $subElemValue ) ) {
                                        $subElements[$subElemId] = $subElemValue;
                                        unset( $elemValue[$subElemId] );
+                               } elseif ( is_bool( $subElemValue ) ) {
+                                       // treat true as empty string, skip false in xml format
+                                       if ( $subElemValue === true ) {
+                                               $subElemValue = '';
+                                       } else {
+                                               unset( $elemValue[$subElemId] );
+                                       }
                                }
                        }
 
index bd5c512..65cb16d 100644 (file)
@@ -147,7 +147,7 @@ class ApiQueryRevisions extends ApiQueryBase {
                                if ( !$difftoRev ) {
                                        $this->dieUsageMsg( array( 'nosuchrevid', $params['diffto'] ) );
                                }
-                               if ( !$diffToRev->userCan( Revision::DELETED_TEXT, $this->getUser() ) ) {
+                               if ( !$difftoRev->userCan( Revision::DELETED_TEXT, $this->getUser() ) ) {
                                        $this->setWarning( "Couldn't diff to r{$difftoRev->getID()}: content is hidden" );
                                        $params['diffto'] = null;
                                }
index 0aea070..1e8da45 100644 (file)
@@ -812,7 +812,8 @@ class HTMLForm extends ContextSource {
                        if ( $this->isVForm() ) {
                                // mw-ui-block is necessary because the buttons aren't necessarily in an
                                // immediate child div of the vform.
-                               array_push( $attribs['class'], 'mw-ui-button', 'mw-ui-big', 'mw-ui-primary', 'mw-ui-block' );
+                               // TODO Let client specify if the primary submit button is progressive or destructive
+                               array_push( $attribs['class'], 'mw-ui-button', 'mw-ui-big', 'mw-ui-constructive', 'mw-ui-block' );
                        }
 
                        $html .= Xml::submitButton( $this->getSubmitText(), $attribs ) . "\n";
index 8b15f0c..79a42ba 100644 (file)
@@ -505,8 +505,7 @@ abstract class Installer {
        }
 
        /**
-        * Determine if LocalSettings.php exists. If it does, return its variables,
-        * merged with those from AdminSettings.php, as an array.
+        * Determine if LocalSettings.php exists. If it does, return its variables.
         *
         * @return Array
         */
@@ -524,9 +523,6 @@ abstract class Installer {
 
                require "$IP/includes/DefaultSettings.php";
                require "$IP/LocalSettings.php";
-               if ( file_exists( "$IP/AdminSettings.php" ) ) {
-                       require "$IP/AdminSettings.php";
-               }
 
                return get_defined_vars();
        }
index fa58a62..cd5a8ad 100644 (file)
@@ -169,6 +169,7 @@ class PostgresUpdater extends DatabaseUpdater {
                                "INTEGER NOT NULL PRIMARY KEY DEFAULT nextval('archive_ar_id_seq')" ),
                        array( 'addPgField', 'externallinks', 'el_id',
                                "INTEGER NOT NULL PRIMARY KEY DEFAULT nextval('externallinks_el_id_seq')" ),
+                       array( 'addPgField', 'uploadstash', 'us_props', "BYTEA" ),
 
                        # type changes
                        array( 'changeField', 'archive', 'ar_deleted', 'smallint', '' ),
index d4b3d65..67236e5 100644 (file)
@@ -380,7 +380,7 @@ class WebInstaller_ExistingWiki extends WebInstallerPage {
 
        /**
         * Initiate an upgrade of the existing database
-        * @param array $vars Variables from LocalSettings.php and AdminSettings.php
+        * @param array $vars Variables from LocalSettings.php
         * @return Status
         */
        protected function handleExistingUpgrade( $vars ) {
index 067ede1..5fc1e06 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * Job queue base code.
+ * Job queue task base code.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
 /**
  * Class to both describe a background job and handle jobs.
  * The queue aspects of this class are now deprecated.
+ * Using the class to push jobs onto queues is deprecated (use JobSpecification).
  *
  * @ingroup JobQueue
  */
-abstract class Job {
+abstract class Job implements IJobSpecification {
        /** @var string */
        public $command;
 
index e7a269a..6097d31 100644 (file)
@@ -723,10 +723,10 @@ class JobQueueDB extends JobQueue {
        }
 
        /**
-        * @param Job $job
+        * @param IJobSpecification $job
         * @return array
         */
-       protected function insertFields( Job $job ) {
+       protected function insertFields( IJobSpecification $job ) {
                $dbw = $this->getMasterDB();
 
                return array(
index d71df15..90742ce 100644 (file)
@@ -116,7 +116,7 @@ class JobQueueGroup {
 
                $jobsByType = array(); // (job type => list of jobs)
                foreach ( $jobs as $job ) {
-                       if ( $job instanceof Job ) {
+                       if ( $job instanceof IJobSpecification ) {
                                $jobsByType[$job->getType()][] = $job;
                        } else {
                                throw new MWException( "Attempted to push a non-Job object into a queue." );
index 9b9fe2d..212871e 100644 (file)
@@ -765,10 +765,10 @@ LUA;
        }
 
        /**
-        * @param Job $job
+        * @param IJobSpecification $job
         * @return array
         */
-       protected function getNewJobFields( Job $job ) {
+       protected function getNewJobFields( IJobSpecification $job ) {
                return array(
                        // Fields that describe the nature of the job
                        'type' => $job->getType(),
@@ -780,8 +780,8 @@ LUA;
                        // Additional job metadata
                        'uuid' => UIDGenerator::newRawUUIDv4( UIDGenerator::QUICK_RAND ),
                        'sha1' => $job->ignoreDuplicates()
-                                       ? wfBaseConvert( sha1( serialize( $job->getDeduplicationInfo() ) ), 16, 36, 31 )
-                                       : '',
+                               ? wfBaseConvert( sha1( serialize( $job->getDeduplicationInfo() ) ), 16, 36, 31 )
+                               : '',
                        'timestamp' => time() // UNIX timestamp
                );
        }
diff --git a/includes/job/JobSpecification.php b/includes/job/JobSpecification.php
new file mode 100644 (file)
index 0000000..e074e5c
--- /dev/null
@@ -0,0 +1,189 @@
+<?php
+/**
+ * Job queue task description base code.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup JobQueue
+ */
+
+/**
+ * Job queue task description interface
+ *
+ * @ingroup JobQueue
+ * @since 1.23
+ */
+interface IJobSpecification {
+       /**
+        * @return string Job type
+        */
+       public function getType();
+
+       /**
+        * @return array
+        */
+       public function getParams();
+
+       /**
+        * @return int|null UNIX timestamp to delay running this job until, otherwise null
+        */
+       public function getReleaseTimestamp();
+
+       /**
+        * @return bool Whether only one of each identical set of jobs should be run
+        */
+       public function ignoreDuplicates();
+
+       /**
+        * Subclasses may need to override this to make duplication detection work.
+        * The resulting map conveys everything that makes the job unique. This is
+        * only checked if ignoreDuplicates() returns true, meaning that duplicate
+        * jobs are supposed to be ignored.
+        *
+        * @return array Map of key/values
+        */
+       public function getDeduplicationInfo();
+
+       /**
+        * @return Title Descriptive title (this can simply be informative)
+        */
+       public function getTitle();
+}
+
+/**
+ * Job queue task description base code
+ *
+ * Example usage:
+ * <code>
+ * $job = new JobSpecification(
+ *             'null',
+ *             array( 'lives' => 1, 'usleep' => 100, 'pi' => 3.141569 ),
+ *             array( 'removeDuplicates' => 1 ),
+ *             Title::makeTitle( NS_SPECIAL, 'nullity' )
+ * );
+ * JobQueueGroup::singleton()->push( $job )
+ * </code>
+ *
+ * @ingroup JobQueue
+ * @since 1.23
+ */
+class JobSpecification implements IJobSpecification {
+       /** @var string */
+       protected $type;
+
+       /** @var array Array of job parameters or false if none */
+       protected $params;
+
+       /** @var Title */
+       protected $title;
+
+       /** @var bool Expensive jobs may set this to true */
+       protected $ignoreDuplicates;
+
+       /**
+        * @param string $type
+        * @param array $params Map of key/values
+        * @param array $opts Map of key/values
+        * @param Title $title Optional descriptive title
+        */
+       public function __construct(
+               $type, array $params, array $opts = array(), Title $title = null
+       ) {
+               $this->validateParams( $params );
+
+               $this->type = $type;
+               $this->params = $params;
+               $this->title = $title ?: Title::newMainPage();
+               $this->ignoreDuplicates = !empty( $opts['removeDuplicates'] );
+       }
+
+       /**
+        * @param array $params
+        */
+       protected function validateParams( array $params ) {
+               foreach ( $params as $p => $v ) {
+                       if ( is_array( $v ) ) {
+                               $this->validateParams( $v );
+                       } elseif ( !is_scalar( $v ) && $v !== null ) {
+                               throw new UnexpectedValueException( 'Job parameters are not JSON serializable.' );
+                       }
+               }
+       }
+
+       /**
+        * @return string
+        */
+       public function getType() {
+               return $this->type;
+       }
+
+       /**
+        * @return Title
+        */
+       public function getTitle() {
+               return $this->title;
+       }
+
+       /**
+        * @return array
+        */
+       public function getParams() {
+               return $this->params;
+       }
+
+       /**
+        * @return int|null UNIX timestamp to delay running this job until, otherwise null
+        */
+       public function getReleaseTimestamp() {
+               return isset( $this->params['jobReleaseTimestamp'] )
+                       ? wfTimestampOrNull( TS_UNIX, $this->params['jobReleaseTimestamp'] )
+                       : null;
+       }
+
+       /**
+        * @return bool Whether only one of each identical set of jobs should be run
+        */
+       public function ignoreDuplicates() {
+               return $this->ignoreDuplicates;
+       }
+
+       /**
+        * Subclasses may need to override this to make duplication detection work.
+        * The resulting map conveys everything that makes the job unique. This is
+        * only checked if ignoreDuplicates() returns true, meaning that duplicate
+        * jobs are supposed to be ignored.
+        *
+        * @return array Map of key/values
+        */
+       public function getDeduplicationInfo() {
+               $info = array(
+                       'type' => $this->getType(),
+                       'namespace' => $this->getTitle()->getNamespace(),
+                       'title' => $this->getTitle()->getDBkey(),
+                       'params' => $this->getParams()
+               );
+               if ( is_array( $info['params'] ) ) {
+                       // Identical jobs with different "root" jobs should count as duplicates
+                       unset( $info['params']['rootJobSignature'] );
+                       unset( $info['params']['rootJobTimestamp'] );
+                       // Likewise for jobs with different delay times
+                       unset( $info['params']['jobReleaseTimestamp'] );
+               }
+
+               return $info;
+       }
+}
index 78ac84d..3bcb4fc 100644 (file)
@@ -35,6 +35,8 @@
  * @ingroup JobQueue
  */
 class RefreshLinksJob extends Job {
+       const PARSE_THRESHOLD_SEC = 1.0;
+
        function __construct( $title, $params = '' ) {
                parent::__construct( 'refreshLinks', $title, $params );
                // Base backlink update jobs and per-title update jobs can be de-duplicated.
@@ -114,6 +116,8 @@ class RefreshLinksJob extends Job {
                        wfGetLB()->waitFor( $this->params['masterPos'] );
                }
 
+               $page = WikiPage::factory( $title );
+
                // Fetch the current revision...
                $revision = Revision::newFromTitle( $title, false, Revision::READ_NORMAL );
                if ( !$revision ) {
@@ -127,18 +131,17 @@ class RefreshLinksJob extends Job {
                }
 
                $parserOutput = false;
+               $parserOptions = $page->makeParserOptions( 'canonical' );
                // If page_touched changed after this root job (with a good slave lag skew factor),
                // then it is likely that any views of the pages already resulted in re-parses which
                // are now in cache. This can be reused to avoid expensive parsing in some cases.
                if ( isset( $this->params['rootJobTimestamp'] ) ) {
-                       $page = WikiPage::factory( $title );
                        $skewedTimestamp = wfTimestamp( TS_UNIX, $this->params['rootJobTimestamp'] ) + 5;
                        if ( $page->getLinksTimestamp() > wfTimestamp( TS_MW, $skewedTimestamp ) ) {
                                // Something already updated the backlinks since this job was made
                                return true;
                        }
                        if ( $page->getTouched() > wfTimestamp( TS_MW, $skewedTimestamp ) ) {
-                               $parserOptions = $page->makeParserOptions( 'canonical' );
                                $parserOutput = ParserCache::singleton()->getDirty( $page, $parserOptions );
                                if ( $parserOutput && $parserOutput->getCacheTime() <= $skewedTimestamp ) {
                                        $parserOutput = false; // too stale
@@ -147,8 +150,21 @@ class RefreshLinksJob extends Job {
                }
                // Fetch the current revision and parse it if necessary...
                if ( $parserOutput == false ) {
+                       $start = microtime( true );
                        // Revision ID must be passed to the parser output to get revision variables correct
-                       $parserOutput = $content->getParserOutput( $title, $revision->getId(), null, false );
+                       $parserOutput = $content->getParserOutput(
+                               $title, $revision->getId(), $parserOptions, false );
+                       $ellapsed = microtime( true ) - $start;
+                       // If it took a long time to render, then save this back to the cache to avoid
+                       // wasted CPU by other apaches or job runners. We don't want to always save to
+                       // cache as this cause cause high cache I/O and LRU churn when a template changes.
+                       if ( $ellapsed >= self::PARSE_THRESHOLD_SEC
+                               && $page->isParserCacheUsed( $parserOptions, $revision->getId() )
+                               && $parserOutput->isCacheable()
+                       ) {
+                               $ctime = wfTimestamp( TS_MW, (int)$start ); // cache time
+                               ParserCache::singleton()->save( $parserOutput, $page, $parserOptions, $ctime );
+                       }
                }
 
                $updates = $content->getSecondaryDataUpdates( $title, null, false, $parserOutput );
index 318448a..3b79684 100644 (file)
@@ -223,8 +223,6 @@ class CSSMin {
                                return $ruleWithRemapped;
                        }
                }, $source );
-
-               return $source;
        }
 
        /**
index ead418d..f0f297f 100644 (file)
@@ -502,6 +502,7 @@ class LogEventsList extends ContextSource {
         * - wrap String Wrap the message in html (usually something like "<div ...>$1</div>").
         * - flags Integer display flags (NO_ACTION_LINK,NO_EXTRA_USER_LINKS)
         * - useRequestParams boolean Set true to use Pager-related parameters in the WebRequest
+        * - useMaster boolean Use master DB
         * @return int Number of total log items (not limited by $lim)
         */
        public static function showLogExtract(
@@ -515,6 +516,7 @@ class LogEventsList extends ContextSource {
                        'wrap' => "$1",
                        'flags' => 0,
                        'useRequestParams' => false,
+                       'useMaster' => false,
                );
                # The + operator appends elements of remaining keys from the right
                # handed array to the left handed, whereas duplicated keys are NOT overwritten.
@@ -548,6 +550,9 @@ class LogEventsList extends ContextSource {
                        $pager->mIsBackwards = false;
                }
 
+               if ( $param['useMaster'] ) {
+                       $pager->mDb = wfGetDB( DB_MASTER );
+               }
                if ( isset( $param['offset'] ) ) { # Tell pager to ignore WebRequest offset
                        $pager->setOffset( $param['offset'] );
                }
index 7f108c8..a608657 100644 (file)
@@ -307,27 +307,27 @@ class BitmapHandler extends ImageHandler {
                global $wgSharpenReductionThreshold, $wgSharpenParameter, $wgMaxAnimatedGifArea,
                        $wgImageMagickTempDir, $wgImageMagickConvertCommand;
 
-               $quality = '';
-               $sharpen = '';
+               $quality = array();
+               $sharpen = array();
                $scene = false;
-               $animation_pre = '';
-               $animation_post = '';
-               $decoderHint = '';
+               $animation_pre = array();
+               $animation_post = array();
+               $decoderHint = array();
                if ( $params['mimeType'] == 'image/jpeg' ) {
-                       $quality = "-quality 80"; // 80%
+                       $quality = array( '-quality', '80' ); // 80%
                        # Sharpening, see bug 6193
                        if ( ( $params['physicalWidth'] + $params['physicalHeight'] )
                                / ( $params['srcWidth'] + $params['srcHeight'] )
                                < $wgSharpenReductionThreshold
                        ) {
-                               $sharpen = "-sharpen " . wfEscapeShellArg( $wgSharpenParameter );
+                               $sharpen = array( '-sharpen', $wgSharpenParameter );
                        }
                        if ( version_compare( $this->getMagickVersion(), "6.5.6" ) >= 0 ) {
                                // JPEG decoder hint to reduce memory, available since IM 6.5.6-2
-                               $decoderHint = "-define jpeg:size={$params['physicalDimensions']}";
+                               $decoderHint = array( '-define', "jpeg:size={$params['physicalDimensions']}" );
                        }
                } elseif ( $params['mimeType'] == 'image/png' ) {
-                       $quality = "-quality 95"; // zlib 9, adaptive filtering
+                       $quality = array( '-quality', '95' ); // zlib 9, adaptive filtering
 
                } elseif ( $params['mimeType'] == 'image/gif' ) {
                        if ( $this->getImageArea( $image ) > $wgMaxAnimatedGifArea ) {
@@ -336,15 +336,15 @@ class BitmapHandler extends ImageHandler {
                                $scene = 0;
                        } elseif ( $this->isAnimatedImage( $image ) ) {
                                // Coalesce is needed to scale animated GIFs properly (bug 1017).
-                               $animation_pre = '-coalesce';
+                               $animation_pre = array( '-coalesce' );
                                // We optimize the output, but -optimize is broken,
                                // use optimizeTransparency instead (bug 11822)
                                if ( version_compare( $this->getMagickVersion(), "6.3.5" ) >= 0 ) {
-                                       $animation_post = '-fuzz 5% -layers optimizeTransparency';
+                                       $animation_post = array( '-fuzz', '5%', '-layers', 'optimizeTransparency' );
                                }
                        }
                } elseif ( $params['mimeType'] == 'image/x-xcf' ) {
-                       $animation_post = '-layers merge';
+                       $animation_post = array( '-layers', 'merge' );
                }
 
                // Use one thread only, to avoid deadlock bugs on OOM
@@ -356,26 +356,28 @@ class BitmapHandler extends ImageHandler {
                $rotation = $this->getRotation( $image );
                list( $width, $height ) = $this->extractPreRotationDimensions( $params, $rotation );
 
-               $cmd =
-                       wfEscapeShellArg( $wgImageMagickConvertCommand ) .
+               $cmd = call_user_func_array( 'wfEscapeShellArg', array_merge(
+                       array( $wgImageMagickConvertCommand ),
+                       $quality,
                        // Specify white background color, will be used for transparent images
                        // in Internet Explorer/Windows instead of default black.
-                       " {$quality} -background white" .
-                       " {$decoderHint} " .
-                       wfEscapeShellArg( $this->escapeMagickInput( $params['srcPath'], $scene ) ) .
-                       " {$animation_pre}" .
+                       array( '-background', 'white' ),
+                       $decoderHint,
+                       array( $this->escapeMagickInput( $params['srcPath'], $scene ) ),
+                       $animation_pre,
                        // For the -thumbnail option a "!" is needed to force exact size,
                        // or ImageMagick may decide your ratio is wrong and slice off
                        // a pixel.
-                       " -thumbnail " . wfEscapeShellArg( "{$width}x{$height}!" ) .
+                       array( '-thumbnail', "{$width}x{$height}!" ),
                        // Add the source url as a comment to the thumb, but don't add the flag if there's no comment
                        ( $params['comment'] !== ''
-                               ? " -set comment " . wfEscapeShellArg( $this->escapeMagickProperty( $params['comment'] ) )
-                               : '' ) .
-                       " -depth 8 $sharpen " .
-                       " -rotate -$rotation " .
-                       " {$animation_post} " .
-                       wfEscapeShellArg( $this->escapeMagickOutput( $params['dstPath'] ) );
+                               ? array( '-set', 'comment', $this->escapeMagickProperty( $params['comment'] ) )
+                               : array() ),
+                       array( '-depth', 8 ),
+                       $sharpen,
+                       array( '-rotate', "-$rotation" ),
+                       $animation_post,
+                       array( $this->escapeMagickOutput( $params['dstPath'] ) ) ) );
 
                wfDebug( __METHOD__ . ": running ImageMagick: $cmd\n" );
                wfProfileIn( 'convert' );
@@ -485,8 +487,8 @@ class BitmapHandler extends ImageHandler {
                $dst = wfEscapeShellArg( $params['dstPath'] );
                $cmd = $wgCustomConvertCommand;
                $cmd = str_replace( '%s', $src, str_replace( '%d', $dst, $cmd ) ); # Filenames
-               $cmd = str_replace( '%h', $params['physicalHeight'],
-                       str_replace( '%w', $params['physicalWidth'], $cmd ) ); # Size
+               $cmd = str_replace( '%h', wfEscapeShellArg( $params['physicalHeight'] ),
+                       str_replace( '%w', wfEscapeShellArg( $params['physicalWidth'] ), $cmd ) ); # Size
                wfDebug( __METHOD__ . ": Running custom convert command $cmd\n" );
                wfProfileIn( 'convert' );
                $retval = 0;
@@ -790,7 +792,7 @@ class BitmapHandler extends ImageHandler {
                        case 'im':
                                $cmd = wfEscapeShellArg( $wgImageMagickConvertCommand ) . " " .
                                        wfEscapeShellArg( $this->escapeMagickInput( $params['srcPath'], $scene ) ) .
-                                       " -rotate -$rotation " .
+                                       " -rotate " . wfEscapeShellArg( "-$rotation" ) . " " .
                                        wfEscapeShellArg( $this->escapeMagickOutput( $params['dstPath'] ) );
                                wfDebug( __METHOD__ . ": running ImageMagick: $cmd\n" );
                                wfProfileIn( 'convert' );
index 7da5a4c..1202c9a 100644 (file)
@@ -180,9 +180,12 @@ class DjVuHandler extends ImageHandler {
                $srcPath = $image->getLocalRefPath();
                # Use a subshell (brackets) to aggregate stderr from both pipeline commands
                # before redirecting it to the overall stdout. This works in both Linux and Windows XP.
-               $cmd = '(' . wfEscapeShellArg( $wgDjvuRenderer ) . " -format=ppm -page={$page}" .
-                       " -size={$params['physicalWidth']}x{$params['physicalHeight']} " .
-                       wfEscapeShellArg( $srcPath );
+               $cmd = '(' . wfEscapeShellArg(
+                       $wgDjvuRenderer,
+                       "-format=ppm",
+                       "-page={$page}",
+                       "-size={$params['physicalWidth']}x{$params['physicalHeight']}",
+                       $srcPath );
                if ( $wgDjvuPostProcessor ) {
                        $cmd .= " | {$wgDjvuPostProcessor}";
                }
index 4dd79a8..8a12e7e 100644 (file)
@@ -93,6 +93,7 @@ abstract class ImageHandler extends MediaHandler {
                if ( !isset( $params['page'] ) ) {
                        $params['page'] = 1;
                } else {
+                       $params['page'] = intval( $params['page'] );
                        if ( $params['page'] > $image->pageCount() ) {
                                $params['page'] = $image->pageCount();
                        }
index 3966b9e..67b1c66 100644 (file)
@@ -209,7 +209,12 @@ class CoreParserFunctions {
        }
 
        static function localurle( $parser, $s = '', $arg = null ) {
-               return htmlspecialchars( self::urlFunction( 'getLocalURL', $s, $arg ) );
+               $temp = self::urlFunction( 'getLocalURL', $s, $arg );
+               if( !is_string( $temp ) ) {
+                       return $temp;
+               } else {
+                       return htmlspecialchars( $temp );
+               }
        }
 
        static function fullurl( $parser, $s = '', $arg = null ) {
@@ -217,7 +222,12 @@ class CoreParserFunctions {
        }
 
        static function fullurle( $parser, $s = '', $arg = null ) {
-               return htmlspecialchars( self::urlFunction( 'getFullURL', $s, $arg ) );
+               $temp = self::urlFunction( 'getFullURL', $s, $arg );
+               if( !is_string( $temp ) ) {
+                       return $temp;
+               } else {
+                       return htmlspecialchars( $temp );
+               }
        }
 
        static function canonicalurl( $parser, $s = '', $arg = null ) {
index 08768b6..b5b67c9 100644 (file)
@@ -605,6 +605,9 @@ class ContribsPager extends ReverseChronologicalPager {
        public $mDb;
        public $preventClickjacking = false;
 
+       /** @var DatabaseBase */
+       public $mDbSecondary;
+
        /**
         * @var array
         */
@@ -641,6 +644,10 @@ class ContribsPager extends ReverseChronologicalPager {
                $month = isset( $options['month'] ) ? $options['month'] : false;
                $this->getDateCond( $year, $month );
 
+               // Most of this code will use the 'contributions' group DB, which can map to slaves
+               // with extra user based indexes or partioning by user. The additional metadata
+               // queries should use a regular slave since the lookup pattern is not all by user.
+               $this->mDbSecondary = wfGetDB( DB_SLAVE ); // any random slave
                $this->mDb = wfGetDB( DB_SLAVE, 'contributions' );
        }
 
@@ -860,7 +867,7 @@ class ContribsPager extends ReverseChronologicalPager {
                                $batch->add( $row->page_namespace, $row->page_title );
                        }
                }
-               $this->mParentLens = Revision::getParentLengths( $this->getDatabase(), $revIds );
+               $this->mParentLens = Revision::getParentLengths( $this->mDbSecondary, $revIds );
                $batch->execute();
                $this->mResult->seek( 0 );
        }
index c6307f0..4548b63 100644 (file)
@@ -264,6 +264,7 @@ class SpecialPrefixindex extends SpecialAllpages {
                                        'from' => $s->page_title,
                                        'prefix' => $prefix,
                                        'hideredirects' => $this->hideRedirects,
+                                       'stripprefix' => $this->stripPrefix,
                                );
 
                                if ( $namespace || $prefix == '' ) {
index 804f7a9..3599dc6 100644 (file)
@@ -55,6 +55,9 @@ class SpecialRevisionDelete extends UnlistedSpecialPage {
        /** The RevDel_List object, storing the list of items to be deleted/undeleted */
        var $list;
 
+       /** Was the DB modified in this request */
+       protected $wasSaved = false;
+
        /**
         * UI labels for each type.
         */
@@ -177,14 +180,24 @@ class SpecialRevisionDelete extends UnlistedSpecialPage {
                # Show relevant lines from the deletion log
                $deleteLogPage = new LogPage( 'delete' );
                $output->addHTML( "<h2>" . $deleteLogPage->getName()->escaped() . "</h2>\n" );
-               LogEventsList::showLogExtract( $output, 'delete',
-                       $this->targetObj, '', array( 'lim' => 25, 'conds' => $qc ) );
+               LogEventsList::showLogExtract(
+                       $output,
+                       'delete',
+                       $this->targetObj,
+                       '', /* user */
+                       array( 'lim' => 25, 'conds' => $qc, 'useMaster' => $this->wasSaved )
+               );
                # Show relevant lines from the suppression log
                if ( $user->isAllowed( 'suppressionlog' ) ) {
                        $suppressLogPage = new LogPage( 'suppress' );
                        $output->addHTML( "<h2>" . $suppressLogPage->getName()->escaped() . "</h2>\n" );
-                       LogEventsList::showLogExtract( $output, 'suppress',
-                               $this->targetObj, '', array( 'lim' => 25, 'conds' => $qc ) );
+                       LogEventsList::showLogExtract(
+                               $output,
+                               'suppress',
+                               $this->targetObj,
+                               '',
+                               array( 'lim' => 25, 'conds' => $qc, 'useMaster' => $this->wasSaved )
+                       );
                }
        }
 
@@ -523,6 +536,7 @@ class SpecialRevisionDelete extends UnlistedSpecialPage {
        protected function success() {
                $this->getOutput()->setPageTitle( $this->msg( 'actioncomplete' ) );
                $this->getOutput()->wrapWikiMsg( "<span class=\"success\">\n$1\n</span>", $this->typeLabels['success'] );
+               $this->wasSaved = true;
                $this->list->reloadFromMaster();
                $this->showForm();
        }
index 18e777a..9e5143f 100644 (file)
@@ -1109,7 +1109,7 @@ class SpecialSearch extends SpecialPage {
                $out .= Html::hidden( 'fulltext', 'Search' ) . "\n";
                $out .= Xml::submitButton(
                        $this->msg( 'searchbutton' )->text(),
-                       array( 'class' => array( 'mw-ui-button', 'mw-ui-primary' ) )
+                       array( 'class' => array( 'mw-ui-button', 'mw-ui-progressive' ) )
                ) . "\n";
                return $out . $this->didYouMeanHtml;
        }
index 12210d0..0cb83d5 100644 (file)
@@ -254,7 +254,7 @@ class UsercreateTemplate extends BaseTemplate {
                                        $this->getMsg( $this->data['loggedin'] ? 'createacct-another-submit' : 'createacct-submit' ),
                                        'submit',
                                        array(
-                                               'class' => "mw-ui-button mw-ui-big mw-ui-block mw-ui-primary",
+                                               'class' => "mw-ui-button mw-ui-big mw-ui-block mw-ui-constructive",
                                                'id' => 'wpCreateaccount',
                                                'tabindex' => $tabIndex++
                                        )
index 38e071c..e5d8a1c 100644 (file)
@@ -149,7 +149,7 @@ class UserloginTemplate extends BaseTemplate {
                                echo Html::input( 'wpLoginAttempt', $this->getMsg( 'login' )->text(), 'submit', array(
                                        'id' => 'wpLoginAttempt',
                                        'tabindex' => '6',
-                                       'class' => 'mw-ui-button mw-ui-big mw-ui-block mw-ui-primary'
+                                       'class' => 'mw-ui-button mw-ui-big mw-ui-block mw-ui-constructive'
                                ) );
                                ?>
                        </div>
@@ -164,7 +164,7 @@ class UserloginTemplate extends BaseTemplate {
                                        </div>
                                <?php } else { ?>
                                        <div id="mw-createaccount-cta">
-                                               <h3 id="mw-userloginlink"><?php $this->msg( 'userlogin-noaccount' ); ?><a href="<?php $this->text( 'createOrLoginHref' ); ?>" id="mw-createaccount-join" tabindex="7"  class="mw-ui-button mw-ui-constructive"><?php $this->msg( 'userlogin-joinproject' ); ?></a></h3>
+                                               <h3 id="mw-userloginlink"><?php $this->msg( 'userlogin-noaccount' ); ?><a href="<?php $this->text( 'createOrLoginHref' ); ?>" id="mw-createaccount-join" tabindex="7"  class="mw-ui-button mw-ui-progressive"><?php $this->msg( 'userlogin-joinproject' ); ?></a></h3>
                                        </div>
                                <?php } ?>
                        <?php } ?>
index 7db6c64..ea11737 100644 (file)
@@ -260,7 +260,7 @@ class UploadStash {
                        'us_key' => $key,
                        'us_orig_path' => $path,
                        'us_path' => $stashPath, // virtual URL
-                       'us_props' => serialize( $fileProps ),
+                       'us_props' => $dbw->encodeBlob( serialize( $fileProps ) ),
                        'us_size' => $fileProps['size'],
                        'us_sha1' => $fileProps['sha1'],
                        'us_mime' => $fileProps['mime'],
index 802cdbc..037663c 100644 (file)
@@ -22,6 +22,8 @@
 
 /**
  * A collection of static methods to play with arrays.
+ *
+ * @since 1.21
  */
 class ArrayUtils {
        /**
@@ -94,13 +96,15 @@ class ArrayUtils {
         * Do a binary search, and return the index of the largest item that sorts
         * less than or equal to the target value.
         *
+        * @since 1.23
+        *
         * @param array $valueCallback A function to call to get the value with
         *     a given array index.
-        * @param $valueCount int The number of items accessible via $valueCallback,
+        * @param int $valueCount The number of items accessible via $valueCallback,
         *     indexed from 0 to $valueCount - 1
-        * @param $comparisonCallback array A callback to compare two values, returning
+        * @param array $comparisonCallback A callback to compare two values, returning
         *     -1, 0 or 1 in the style of strcmp().
-        * @param $target string The target value to find.
+        * @param string $target The target value to find.
         *
         * @return int|bool The item index of the lower bound, or false if the target value
         *     sorts before all items.
@@ -142,9 +146,11 @@ class ArrayUtils {
         *
         * Note: empty arrays are removed.
         *
-        * @param $array1 array The array to compare from
-        * @param $array2 array An array to compare against
-        * @param ... array More arrays to compare against
+        * @since 1.23
+        *
+        * @param array $array1 The array to compare from
+        * @param array $array2 An array to compare against
+        * @param array ... More arrays to compare against
         * @return array An array containing all the values from array1
         *               that are not present in any of the other arrays.
         */
index dc8626d..297c972 100644 (file)
@@ -153,9 +153,6 @@ class CheckSyntax extends Maintenance {
                if ( file_exists( "$IP/LocalSettings.php" ) ) {
                        $this->mFiles[] = "$IP/LocalSettings.php";
                }
-               if ( file_exists( "$IP/AdminSettings.php" ) ) {
-                       $this->mFiles[] = "$IP/AdminSettings.php";
-               }
 
                $this->output( 'done.', 'listfiles' );
        }
index 3dcf12c..978d44f 100644 (file)
@@ -90,12 +90,6 @@ if ( defined( 'MW_CONFIG_CALLBACK' ) ) {
        require $maintenance->loadSettings();
 }
 
-if ( $maintenance->getDbType() === Maintenance::DB_ADMIN
-       && is_readable( "$IP/AdminSettings.php" )
-) {
-       require "$IP/AdminSettings.php";
-}
-
 if ( $maintenance->getDbType() === Maintenance::DB_NONE ) {
        if ( $wgLocalisationCacheConf['storeClass'] === false && ( $wgLocalisationCacheConf['store'] == 'db' || ( $wgLocalisationCacheConf['store'] == 'detect' && !$wgCacheDirectory ) ) ) {
                $wgLocalisationCacheConf['storeClass'] = 'LCStoreNull';
index f56e91f..51f2cac 100644 (file)
@@ -55,7 +55,7 @@ if ( isset( $options['d'] ) ) {
 }
 
 $useReadline = function_exists( 'readline_add_history' )
-                       && Maintenance::posix_isatty( 0 /*STDIN*/ );
+       && Maintenance::posix_isatty( 0 /*STDIN*/ );
 
 if ( $useReadline ) {
        $historyFile = isset( $_ENV['HOME'] ) ?
@@ -63,12 +63,27 @@ if ( $useReadline ) {
        readline_read_history( $historyFile );
 }
 
+$e = null; // PHP exception
 while ( ( $line = Maintenance::readconsole() ) !== false ) {
+       if ( $e && !preg_match( '/^(exit|die);?$/', $line ) ) {
+               // Internal state may be corrupted or fatals may occur later due
+               // to some object not being set. Don't drop out of eval in case
+               // lines were being pasted in (which would then get dumped to the shell).
+               // Instead, just absorb the remaning commands. Let "exit" through per DWIM.
+               echo "Exception was thrown before; please restart eval.php\n";
+               continue;
+       }
        if ( $useReadline ) {
                readline_add_history( $line );
                readline_write_history( $historyFile );
        }
-       $val = eval( $line . ";" );
+       try {
+               $val = eval( $line . ";" );
+       } catch ( Exception $e ) {
+               echo "Caught exception " . get_class( $e ) .
+                       ": {$e->getMessage()}\n" . $e->getTraceAsString() . "\n";
+               continue;
+       }
        if ( wfIsHHVM() || is_null( $val ) ) {
                echo "\n";
        } elseif ( is_string( $val ) || is_numeric( $val ) ) {
index 93e931c..3aa8109 100644 (file)
@@ -377,6 +377,7 @@ CREATE TABLE uploadstash (
   us_key          TEXT,
   us_orig_path    TEXT,
   us_path         TEXT,
+  us_props        BYTEA,
   us_source_type  TEXT,
   us_timestamp    TIMESTAMPTZ,
   us_status       TEXT,
index 89bb64d..2bab5b3 100644 (file)
@@ -10,7 +10,7 @@
                // Get the original values of some form elements
                $( '#wpTextbox1, #wpSummary' ).each( function () {
                        $( this ).data( 'origtext', $( this ).val() );
-               });
+               } );
                var savedWindowOnBeforeUnload;
                $( window )
                        .on( 'beforeunload.editwarning', function () {
index c7e9bd9..486e993 100644 (file)
@@ -2,7 +2,7 @@
  * Hebrew (עברית) language functions
  */
 
-mediaWiki.language.convertGrammar = function( word, form ) {
+mediaWiki.language.convertGrammar = function ( word, form ) {
        var grammarForms = mediaWiki.language.getData( 'he', 'grammarForms' );
        if ( grammarForms && grammarForms[form] ) {
                return grammarForms[form][word];
index 5d2c271..e21d21b 100644 (file)
        -webkit-transition: @string;
        transition: @string;
 }
+
+@-webkit-keyframes rotate {
+       from {
+               -webkit-transform:rotate(0deg);
+       }
+       to {
+               -webkit-transform:rotate(360deg);
+       }
+}
+
+@keyframes rotate {
+       from {
+               transform: rotate(0deg);
+       }
+       to {
+               transform: rotate(360deg);
+       }
+}
+
+.rotation(@time) {
+       -webkit-animation-name: rotate;
+       -webkit-animation-duration: @time;
+       -webkit-animation-iteration-count: infinite;
+       -webkit-animation-timing-function: linear;
+       animation-name: rotate;
+       animation-duration: @time;
+       animation-iteration-count: infinite;
+       animation-timing-function: linear;
+}
index 7b1d416..b02e8f2 100644 (file)
@@ -96,7 +96,7 @@ div.searchresult {
        margin-left: 0.4em;
 }
 .mw-search-results li {
-       padding-bottom: 1em;
+       padding-bottom: 1.2em;
        list-style: none;
        list-style-image: none;
 }
index 3842064..0078724 100644 (file)
@@ -111,7 +111,7 @@ jQuery( function ( $ ) {
                        keyRight = 39,
                        $el;
 
-               if( event.keyCode === keyLeft ) {
+               if ( event.keyCode === keyLeft ) {
                        $el = $( '#preftoc li.selected' ).prev().find( 'a' );
                } else if ( event.keyCode === keyRight ) {
                        $el = $( '#preftoc li.selected' ).next().find( 'a' );
index 8276b1c..553d077 100644 (file)
@@ -1,25 +1,32 @@
+@import "mediawiki.mixins";
 @import "../../settings/typography";
 @import "../../mixins/effects";
 
 // Buttons
 //
+// All buttons start with mw-ui-button class, modified by other classes.
+// It can be any element.  Due to a lack of a CSS reset, the exact styling of
+// the button depends on what type of element is used.
+// There are two kinds of buttons, the default is a "Call to Action" with an obvious border
+// and there is a quiet kind without a border.
+//
 // Styleguide 2.
 
 @buttonBorderRadius: 3px;
+@transitionDuration: .1s;
+@transitionFunction: ease-in-out;
 
-// Button styling
-//
-// Basic button styling to be used on desktop skins. Can be any element.
-// Due to a lack of a CSS reset, the exact styling of the button depends on what type of element is used.
+// Neutral button styling
 //
 // Markup:
-// <div class="mw-ui-button">button.mw-ui-button</div>
+// <button class="mw-ui-button">.mw-ui-button</button>
+// <button class="mw-ui-button" disabled>.mw-ui-button</button>
 //
 // Styleguide 2.1.
 .mw-ui-button {
        // Container layout
        display: inline-block;
-       padding: 0.4em 1em 0.4em 1em;
+       padding: .5em 1em;
        margin: 0;
 
        // IE6/IE7 hack
@@ -28,7 +35,7 @@
        zoom: 1;
 
        // Container styling
-       .buttonColors();
+       .button-colors(@colorWhite);
        border-radius: @buttonBorderRadius;
 
        // Ensure that buttons and inputs are nicely aligned when they have differing heights
 
        // Content styling
        text-align: center;
-       text-decoration: none;
-
        font-weight: bold;
+       white-space: nowrap;
+       text-shadow: 0 1px rgba(0, 0, 0, .1);
 
        // Interaction styling
        cursor: pointer;
 
-       &:disabled,
-       &.mw-ui-disabled {
+       &:disabled {
+               text-shadow: none;
                cursor: default;
        }
 
+       .transition(background @transitionDuration @transitionFunction, color @transitionDuration @transitionFunction, box-shadow @transitionDuration @transitionFunction;);
+
        // Styling for specific button types
        // -----------------------------------------
+
+       // Big buttons
+       //
+       // Not all buttons are equal. You can emphasise certain actions over others
+       // using the mw-ui-big class.
+       //
+       // Markup:
+       // <button class="mw-ui-button mw-ui-big">.mw-ui-button</button>
+       // <button class="mw-ui-button mw-ui-progressive mw-ui-big">.mw-ui-progressive</button>
+       // <button class="mw-ui-button mw-ui-constructive mw-ui-big">.mw-ui-constructive</button>
+       // <button class="mw-ui-button mw-ui-destructive mw-ui-big">.mw-ui-destructive</button>
+       //
+       // Styleguide 2.1.6.
        &.mw-ui-big {
                font-size: @baseFontSize * 1.3;
        }
 
+       // Block buttons
+       //
+       // Some buttons might need to be stacked.
+       //
+       // Markup:
+       // <button class="mw-ui-button mw-ui-block">.mw-ui-button</button>
+       // <button class="mw-ui-button mw-ui-progressive mw-ui-block">.mw-ui-progressive</button>
+       // <button class="mw-ui-button mw-ui-constructive mw-ui-block">.mw-ui-constructive</button>
+       // <button class="mw-ui-button mw-ui-destructive mw-ui-block">.mw-ui-destructive</button>
+       //
+       // Styleguide 2.1.5.
        &.mw-ui-block {
                display: block;
                width: 100%;
        }
 
-       // Primary buttons
+       // Progressive buttons
        //
-       // Do not use the mw-ui-primary class use mw-ui-constructive instead. For blue buttons
-       // use mw-ui-progressive (coming soon)
+       // Use progressive buttons for actions which lead to a next step in the process.
+       // .mw-ui-primary is deprecated, kept for compatibility.
        //
        // Markup:
-       // <button class="mw-ui-button mw-ui-primary">mw-ui-primary</button>
+       // <button class="mw-ui-button mw-ui-progressive">.mw-ui-progressive</button>
+       // <button class="mw-ui-button mw-ui-progressive" disabled>.mw-ui-progressive</button>
        //
        // Styleguide 2.1.1.
+       &.mw-ui-progressive,
        &.mw-ui-primary {
-               .buttonColors(@agoraBlue);
+               .button-colors(@colorProgressive);
+
+               &.mw-ui-quiet {
+                       .button-colors-quiet(@colorProgressive);
+               }
        }
 
        // Constructive buttons
        // e.g. save changes button
        //
        // Markup:
-       // <button class="mw-ui-button mw-ui-constructive">mw-ui-constructive</button>
+       // <button class="mw-ui-button mw-ui-constructive">.mw-ui-constructive</button>
+       // <button class="mw-ui-button mw-ui-constructive" disabled>.mw-ui-constructive</button>
        //
        // Styleguide 2.1.2.
        &.mw-ui-constructive {
-               .buttonColors(@agoraGreen);
+               .button-colors(@colorConstructive);
+
+               &.mw-ui-quiet {
+                       .button-colors-quiet(@colorConstructive);
+               }
        }
 
        // Destructive buttons
        // This should not be used for cancel buttons.
        //
        // Markup:
-       // <button class="mw-ui-button mw-ui-destructive">mw-ui-destructive</button>
+       // <button class="mw-ui-button mw-ui-destructive">.mw-ui-destructive</button>
+       // <button class="mw-ui-button mw-ui-destructive" disabled>.mw-ui-destructive</button>
        //
        // Styleguide 2.1.3.
        &.mw-ui-destructive {
-               .buttonColors(@agoraRed);
+               .button-colors(@colorDestructive);
+
+               &.mw-ui-quiet {
+                       .button-colors-quiet(@colorDestructive);
+               }
+       }
+
+       // Quiet buttons
+       //
+       // Use quiet buttons when they are less important and alongisde other progressive/destructive/progressive buttons.
+       //
+       // Markup:
+       // <button class="mw-ui-button mw-ui-quiet">.mw-ui-button</button>
+       // <button class="mw-ui-button mw-ui-constructive mw-ui-quiet">.mw-ui-constructive</button>
+       // <button class="mw-ui-button mw-ui-constructive mw-ui-quiet" disabled>.mw-ui-constructive</button>
+       // <button class="mw-ui-button mw-ui-destructive mw-ui-quiet">.mw-ui-destructive</button>
+       // <button class="mw-ui-button mw-ui-destructive mw-ui-quiet" disabled>.mw-ui-destructive</button>
+       // <button class="mw-ui-button mw-ui-progressive mw-ui-quiet">.mw-ui-progressive</button>
+       // <button class="mw-ui-button mw-ui-progressive mw-ui-quiet" disabled>.mw-ui-progressive</button>
+       //
+       // Styleguide 2.1.4.
+       &.mw-ui-quiet {
+               background: transparent;
+               border: none;
+               text-shadow: none;
+               .button-colors-quiet(@colorGrayDark);
+
+               &:hover,
+               &:focus {
+                       box-shadow: none;
+               }
+
+               &:active,
+               &:disabled {
+                       background: transparent;
+               }
        }
 }
 
-// This overrides an underline declaration on a:hover and a:focus in commonElements.css, which the
-// class alone isn't specific enough to do
 a.mw-ui-button {
        text-decoration: none;
+
+       // This overrides an underline declaration on a:hover and a:focus in
+       // commonElements.css, which the class alone isn't specific enough to do.
+       &:hover,
+       &:focus {
+               text-decoration: none;
+       }
 }
 
 // Button groups
index d168e5f..eb8c203 100644 (file)
@@ -1,52 +1,81 @@
-/* Mixins for visual effects in CSS3 */
-
 @import "../settings/colors";
-
-// ----------------------------------------------------------------------------
-// Gradients
-// ----------------------------------------------------------------------------
-.vertical-gradient(@startColor: lighten(@agoraGray, 95%), @endColor: @agoraGray, @startPos: 0, @endPos: 100%) {
-       background-color: @endColor;
-       background-image: -moz-linear-gradient( top, @startColor @startPos, @endColor @endPos ); // Firefox 3.6+
-       background-image: -webkit-gradient( linear, left top, left bottom, color-stop( @startPos, @startColor ), color-stop( @endPos, @endColor ) ); // Safari 4+, Chrome 2+
-       background-image: -webkit-linear-gradient( top, @startColor @startPos, @endColor @endPos ); // Safari 5.1+, Chrome 10+
-       background-image: linear-gradient( @startColor @startPos, @endColor @endPos ); // Standard
-}
-
 // ----------------------------------------------------------------------------
 // Button styling
 // ----------------------------------------------------------------------------
 
-.buttonColors(@baseColor: @agoraGray) {
-       // Background color
-       .vertical-gradient(lighten(@baseColor, 7.5%), @baseColor);
-
-       border: 1px solid darken(@baseColor, 2%);
+.button-colors(@bgColor) {
+       background: @bgColor;
 
        &:hover,
-       &.mw-ui-hover {
-               .vertical-gradient(lighten(@baseColor, 12.5%), lighten(@baseColor, 7.5%));
-               text-decoration: none;
+       &:focus {
+               // The inner bottom bevel should match the active background color.
+               box-shadow: 0 1px rgba(0, 0, 0, 10%), inset 0 -3px rgba(0, 0, 0, 20%);
+               outline: none;
+               // remove outline in Firefox
+               &::-moz-focus-inner {
+                       border-color: transparent;
+               }
        }
 
-       &:active,
-       &.mw-ui-active {
-               background-image: none;
-               background-color: darken(@baseColor, 3%);
+       &:active {
+               // lessphp doesn't implement shade (https://github.com/leafo/lessphp/issues/528);
+               // it passes it through, then ResourceLoader drops it.
+               // background: shade(@bgColor, 20%);
+               background: mix(#000, @bgColor, 20%);
+               box-shadow: none;
        }
+}
+
+.button-colors(@bgColor) when (lightness(@bgColor) >= 70%) {
+       color: @colorGrayDark;
+       border: 1px solid @colorGrayLight;
+
+       &:disabled {
+               color: @colorGrayLight;
 
-       &:disabled,
-       &.mw-ui-disabled {
-               background-image: none;
-               background-color: @baseColor;
-               opacity: 0.5;
+               // make sure disabled buttons don't have hover and active states
+               &:hover,
+               &:active {
+                       background: @bgColor;
+                       box-shadow: none;
+               }
        }
 }
 
-.buttonColors(@baseColor: @agoraGray) when (lightness(@baseColor) >= 50%) {
-       color: #000;
+.button-colors(@bgColor) when (lightness(@bgColor) < 70%) {
+       color: @colorWhite;
+       border: none;
+
+       &:disabled {
+               background: @colorGrayLight;
+
+               // make sure disabled buttons don't have hover and active states
+               &:hover,
+               &:active {
+                       box-shadow: none;
+               }
+       }
 }
 
-.buttonColors(@baseColor: @agoraGray) when (lightness(@baseColor) < 50%) {
-       color: #fff;
+.button-colors-quiet(@textColor) {
+       // Quiet buttons all start gray, and reveal
+       // constructive/progressive/destructive color on hover and active.
+       color: @colorGrayDark;
+
+       &:hover,
+       &:focus {
+               // lessphp doesn't implement tint, see above
+               // color: tint(@textColor, 20%);
+               color: mix(#fff, @textColor, 20%);
+       }
+
+       &:active {
+               // lessphp doesn't implement shade, see above
+               // color: shade(@textColor, 20%);
+               color: mix(#000, @textColor, 20%);
+       }
+
+       &:disabled {
+               color: @colorGrayLight;
+       }
 }
index 1b177ee..b32e4e7 100644 (file)
@@ -4,7 +4,7 @@
 // For Vector, that should be layered on top with vector-type
 .agora-field-styling() {
 
-       border: 1px solid @agoraGray;
+       border: 1px solid @colorGrayLight;
 
        &:focus {
                // Styling focus of native checkboxes etc on Mac is almost impossible.
                        outline: 0; // Removes OS field focus
                }
 
-               box-shadow: @agoraBlueShadow 0px 0px 5px;
+               box-shadow: @colorProgressiveShadow 0px 0px 5px;
 
-               border-color: @agoraBlueShadow;
+               border-color: @colorProgressiveShadow;
        }
 
-       color: @agoraTextColor;
+       color: @colorText;
        padding: 0.35em 0.5em 0.35em 0.5em;
 
        // Ensure that buttons and inputs are nicely aligned when they have differing heights
@@ -27,7 +27,7 @@
 .agora-label-styling() {
        //font-weight: bold;
        font-size: 0.9em;
-       color: darken(@agoraGray, 50%);
+       color: darken(@colorGrayLight, 50%);
 
        * {
                font-weight: normal;
@@ -48,7 +48,7 @@
                height: auto;
                margin: 0 0.1em 0em 0;
                padding: 0;
-               border: 1px solid @agoraGray;
+               border: 1px solid @colorGrayLight;
                cursor: pointer;
        }
 }
index 208d6f6..18661de 100644 (file)
@@ -1,17 +1,10 @@
-// Grays
-// -----------------------------------------
-@agoraGray: #c9c9c9;
-@agoraTextColor: #252525;
-
-// Blues
-// -----------------------------------------
-@agoraBlue: #3366bb;
-@agoraBlueShadow: #4091ed;
-
-// Greens
-// -----------------------------------------
-@agoraGreen: #27aa65;
-
-// Reds
-// -----------------------------------------
-@agoraRed: #cc0000;
+@colorWhite: #fff;
+@colorGrayLight: #ccc;
+@colorGrayDark: #898989;
+@colorText: #252525;
+@colorProgressive: #347bff;
+// FIXME: remove @colorProgressiveShadow (shadows should be generated
+// in LESS by dimming the original colors)
+@colorProgressiveShadow: #4091ed;
+@colorConstructive: #00af89;
+@colorDestructive: #d11d13;
index 76c398c..83651ed 100644 (file)
@@ -1,5 +1,5 @@
 @baseFontSize: 1em;
 @baseLineHeight: 1.4 * @baseFontSize;
-@baseFontColor: @agoraTextColor;
+@baseFontColor: @colorText;
 
 @smallFontSize: 0.75em;
index d93254b..e76141d 100644 (file)
                                // try to invoke it.
                                console.table.call( console, data );
                                return;
-                       } catch (e) {}
+                       } catch ( e ) {}
                        try {
                                console.log( $.toJSON( data, null, 2 ) );
                                return;
-                       } catch (e) {}
+                       } catch ( e ) {}
                        mw.log( data );
                },
 
 
                                        try {
                                                css = module.style.css.join();
-                                       } catch (e) { return; } // skip
+                                       } catch ( e ) { return; } // skip
 
                                        stats = inspect.auditSelectors( css );
                                        modules.push( {
                                        try {
                                                raw = localStorage.getItem( mw.loader.store.getStoreKey() );
                                                stats.totalSize = humanSize( $.byteLength( raw ) );
-                                       } catch (e) {}
+                                       } catch ( e ) {}
                                }
                                return [stats];
                        }
index 3322ad9..e286d32 100644 (file)
         * the current selector. Bindings to passed-in jquery elements are preserved. Functions become click handlers for [$1 linktext] links.
         * e.g.
         *        $.fn.msg = mediaWiki.parser.getJqueryPlugin( options );
-        *        var userlink = $( '<a>' ).click( function () { alert( "hello!!") } );
+        *        var userlink = $( '<a>' ).click( function () { alert( "hello!!" ) } );
         *        $( 'p#headline' ).msg( 'hello-user', userlink );
         *
         * @param {Array} parser options
index e3aae9e..d282a56 100644 (file)
@@ -1274,7 +1274,7 @@ var mw = ( function ( $, undefined ) {
                                mw.loader.work();
                        }
 
-                       function sortQuery(o) {
+                       function sortQuery( o ) {
                                var sorted = {}, key, a = [];
                                for ( key in o ) {
                                        if ( hasOwn.call( o, key ) ) {
@@ -1903,7 +1903,7 @@ var mw = ( function ( $, undefined ) {
                                                        mw.config.get( 'skin' ),
                                                        mw.config.get( 'wgResourceLoaderStorageVersion' ),
                                                        mw.config.get( 'wgUserLanguage' )
-                                               ].join(':');
+                                               ].join( ':' );
                                        },
 
                                        /**
@@ -1951,7 +1951,7 @@ var mw = ( function ( $, undefined ) {
                                                                mw.loader.store.items = data.items;
                                                                return;
                                                        }
-                                               } catch (e) {}
+                                               } catch ( e ) {}
 
                                                if ( raw === undefined ) {
                                                        // localStorage failed; disable store
@@ -2031,7 +2031,7 @@ var mw = ( function ( $, undefined ) {
                                                        return;
                                                }
 
-                                               mw.loader.store.items[key] = 'mw.loader.implement(' + args.join(',') + ');';
+                                               mw.loader.store.items[key] = 'mw.loader.implement(' + args.join( ',' ) + ');';
                                                mw.loader.store.update();
                                        },
 
index 0713541..50915b6 100644 (file)
@@ -38,7 +38,7 @@
                        $form = context.config.$region.closest( 'form' );
 
                        formAction = $form.attr( 'action' );
-                       baseHref = formAction + ( formAction.match(/\?/) ? '&' : '?' );
+                       baseHref = formAction + ( formAction.match( /\?/ ) ? '&' : '?' );
 
                        linkParams = {};
                        $.each( $form.serializeArray(), function ( idx, obj ) {
                        // Generic selector for skins with multiple searchboxes (used by CologneBlue)
                        '.mw-searchInput'
                ];
-               $( searchboxesSelectors.join(', ') )
+               $( searchboxesSelectors.join( ', ' ) )
                        .suggestions( {
                                fetch: function ( query ) {
                                        var $el;
index 6f93215..6e4a7b3 100644 (file)
        margin-top: -0.8em !ie;
        height: 0;
        overflow: hidden;
-       .background-image('images/watch-icons.png');
+       background-position: 5px 60%;
 }
 #ca-unwatch.icon a {
-       background-position: -43px 60%;
+       .background-image-svg('images/unwatch-icon.svg', 'images/unwatch-icon.png');
 }
 #ca-watch.icon a {
-       background-position: 5px 60%;
+       .background-image-svg('images/watch-icon.svg', 'images/watch-icon.png');
 }
 #ca-unwatch.icon a:hover,
 #ca-unwatch.icon a:focus {
-       background-position: -67px 60%;
+       .background-image-svg('images/unwatch-icon-hl.svg', 'images/unwatch-icon-hl.png');
 }
 #ca-watch.icon a:hover,
 #ca-watch.icon a:focus {
-       background-position: -19px 60%;
+       .background-image-svg('images/watch-icon-hl.svg', 'images/watch-icon-hl.png');
 }
 #ca-unwatch.icon a.loading,
 #ca-watch.icon a.loading {
-       .background-image('images/watch-icon-loading.gif');
-       background-position: 5px 60%;
+       .background-image-svg('images/watch-icon-loading.svg', 'images/watch-icon-loading.png');
+       .rotation(700ms);
+       background-position: 50% 60%;
+       -webkit-transform-origin: 50% 57%;
+       transform-origin: 50% 57%;
 }
 #ca-unwatch.icon a span,
 #ca-watch.icon a span {
diff --git a/skins/vector/images/unwatch-icon-hl.png b/skins/vector/images/unwatch-icon-hl.png
new file mode 100644 (file)
index 0000000..6b2b502
Binary files /dev/null and b/skins/vector/images/unwatch-icon-hl.png differ
diff --git a/skins/vector/images/unwatch-icon-hl.svg b/skins/vector/images/unwatch-icon-hl.svg
new file mode 100644 (file)
index 0000000..346e072
--- /dev/null
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="16"
+   height="16"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.48.4 r9939"
+   sodipodi:docname="watch-icon-hl.svg"
+   inkscape:export-filename="/home/m4tx/Pulpit/LOCAL/watch-icon-hl.png"
+   inkscape:export-xdpi="87.436836"
+   inkscape:export-ydpi="87.436836">
+  <defs
+     id="defs4">
+    <linearGradient
+       id="linearGradient3788">
+      <stop
+         style="stop-color:#c2edff;stop-opacity:1;"
+         offset="0"
+         id="stop3790" />
+      <stop
+         id="stop3796"
+         offset="0.5"
+         style="stop-color:#68bdff;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="1"
+         id="stop3792" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3788"
+       id="linearGradient3804"
+       x1="13.470111"
+       y1="14.363379"
+       x2="4.596477"
+       y2="3.3969929"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3788"
+       id="linearGradient3808"
+       gradientUnits="userSpaceOnUse"
+       x1="13.470111"
+       y1="14.363379"
+       x2="4.596477"
+       y2="3.3969929" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="8"
+     inkscape:cx="-3.451911"
+     inkscape:cy="5.714676"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0"
+     inkscape:window-width="1920"
+     inkscape:window-height="1041"
+     inkscape:window-x="0"
+     inkscape:window-y="0"
+     inkscape:window-maximized="1" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>Mateusz &quot;m4tx&quot; Maćkowski</dc:title>
+          </cc:Agent>
+        </dc:creator>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-693.14288,-698.64789)">
+    <path
+       inkscape:transform-center-y="-0.70705002"
+       transform="matrix(0.86225302,0,0,0.86225302,694.06156,700.58832)"
+       d="M 8.3337586,-0.92098331 10.85748,4.1926362 16.500695,5.0126434 12.417226,8.9930409 13.381202,14.613453 8.3337584,11.959854 3.286314,14.613452 4.2502902,8.9930409 0.16682217,5.012643 5.8100362,4.1926362 z"
+       inkscape:randomized="0"
+       inkscape:rounded="0"
+       inkscape:flatsided="false"
+       sodipodi:arg2="-0.9424778"
+       sodipodi:arg1="-1.5707963"
+       sodipodi:r2="4.2936125"
+       sodipodi:r1="8.587225"
+       sodipodi:cy="7.6662416"
+       sodipodi:cx="8.3337584"
+       sodipodi:sides="5"
+       id="path3768"
+       style="fill:url(#linearGradient3804);fill-opacity:1;stroke:#c8b250;stroke-width:1.15975237000000009;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       sodipodi:type="star" />
+  </g>
+</svg>
diff --git a/skins/vector/images/unwatch-icon.png b/skins/vector/images/unwatch-icon.png
new file mode 100644 (file)
index 0000000..9fd9436
Binary files /dev/null and b/skins/vector/images/unwatch-icon.png differ
diff --git a/skins/vector/images/unwatch-icon.svg b/skins/vector/images/unwatch-icon.svg
new file mode 100644 (file)
index 0000000..9e13769
--- /dev/null
@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="16"
+   height="16"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.48.4 r9939"
+   sodipodi:docname="watch-icon-fav.svg">
+  <defs
+     id="defs4">
+    <linearGradient
+       id="linearGradient3788">
+      <stop
+         style="stop-color:#c2edff;stop-opacity:1;"
+         offset="0"
+         id="stop3790" />
+      <stop
+         id="stop3796"
+         offset="0.5"
+         style="stop-color:#68bdff;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="1"
+         id="stop3792" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3788"
+       id="linearGradient3804"
+       x1="13.470111"
+       y1="14.363379"
+       x2="4.596477"
+       y2="3.3969929"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3788"
+       id="linearGradient3808"
+       gradientUnits="userSpaceOnUse"
+       x1="13.470111"
+       y1="14.363379"
+       x2="4.596477"
+       y2="3.3969929" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="8"
+     inkscape:cx="-6.951911"
+     inkscape:cy="5.714676"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0"
+     inkscape:window-width="1920"
+     inkscape:window-height="1014"
+     inkscape:window-x="0"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>Mateusz &quot;m4tx&quot; Maćkowski</dc:title>
+          </cc:Agent>
+        </dc:creator>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-693.14288,-698.64789)">
+    <path
+       inkscape:transform-center-y="-0.70705002"
+       transform="matrix(0.86225302,0,0,0.86225302,694.06156,700.58832)"
+       d="M 8.3337586,-0.92098331 10.85748,4.1926362 16.500695,5.0126434 12.417226,8.9930409 13.381202,14.613453 8.3337584,11.959854 3.286314,14.613452 4.2502902,8.9930409 0.16682217,5.012643 5.8100362,4.1926362 z"
+       inkscape:randomized="0"
+       inkscape:rounded="0"
+       inkscape:flatsided="false"
+       sodipodi:arg2="-0.9424778"
+       sodipodi:arg1="-1.5707963"
+       sodipodi:r2="4.2936125"
+       sodipodi:r1="8.587225"
+       sodipodi:cy="7.6662416"
+       sodipodi:cx="8.3337584"
+       sodipodi:sides="5"
+       id="path3768"
+       style="fill:url(#linearGradient3804);fill-opacity:1;stroke:#7cb5d1;stroke-width:1.15975237;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       sodipodi:type="star" />
+  </g>
+</svg>
diff --git a/skins/vector/images/watch-icon-hl.png b/skins/vector/images/watch-icon-hl.png
new file mode 100644 (file)
index 0000000..4cb87cd
Binary files /dev/null and b/skins/vector/images/watch-icon-hl.png differ
diff --git a/skins/vector/images/watch-icon-hl.svg b/skins/vector/images/watch-icon-hl.svg
new file mode 100644 (file)
index 0000000..b31e8b8
--- /dev/null
@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="16"
+   height="16"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.48.4 r9939"
+   sodipodi:docname="watch-icon-hl.svg">
+  <defs
+     id="defs4">
+    <linearGradient
+       id="linearGradient3788">
+      <stop
+         style="stop-color:#c2edff;stop-opacity:1;"
+         offset="0"
+         id="stop3790" />
+      <stop
+         id="stop3796"
+         offset="0.5"
+         style="stop-color:#68bdff;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="1"
+         id="stop3792" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3788"
+       id="linearGradient3804"
+       x1="13.470111"
+       y1="14.363379"
+       x2="4.596477"
+       y2="3.3969929"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3788"
+       id="linearGradient3808"
+       gradientUnits="userSpaceOnUse"
+       x1="13.470111"
+       y1="14.363379"
+       x2="4.596477"
+       y2="3.3969929" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="8"
+     inkscape:cx="-6.97632"
+     inkscape:cy="6.2476517"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0"
+     inkscape:window-width="1920"
+     inkscape:window-height="1014"
+     inkscape:window-x="0"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>Mateusz &quot;m4tx&quot; Maćkowski</dc:title>
+          </cc:Agent>
+        </dc:creator>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-693.14288,-698.64789)">
+    <path
+       sodipodi:type="star"
+       style="fill:#ffffff;fill-opacity:1;stroke:#c8b250;stroke-width:1.15975237;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       id="path3770"
+       sodipodi:sides="5"
+       sodipodi:cx="8.3337584"
+       sodipodi:cy="7.6662416"
+       sodipodi:r1="8.587225"
+       sodipodi:r2="4.2936125"
+       sodipodi:arg1="-1.5707963"
+       sodipodi:arg2="-0.9424778"
+       inkscape:flatsided="false"
+       inkscape:rounded="0"
+       inkscape:randomized="0"
+       d="M 8.3337586,-0.92098331 10.85748,4.1926362 16.500695,5.0126434 12.417226,8.9930409 13.381202,14.613453 8.3337584,11.959854 3.286314,14.613452 4.2502902,8.9930409 0.16682217,5.012643 5.8100362,4.1926362 z"
+       transform="matrix(0.86225302,0,0,0.86225302,694.06156,700.58832)"
+       inkscape:transform-center-y="-0.70705002" />
+  </g>
+</svg>
diff --git a/skins/vector/images/watch-icon-loading.gif b/skins/vector/images/watch-icon-loading.gif
deleted file mode 100644 (file)
index 618c308..0000000
Binary files a/skins/vector/images/watch-icon-loading.gif and /dev/null differ
diff --git a/skins/vector/images/watch-icon-loading.png b/skins/vector/images/watch-icon-loading.png
new file mode 100644 (file)
index 0000000..5f0c490
Binary files /dev/null and b/skins/vector/images/watch-icon-loading.png differ
diff --git a/skins/vector/images/watch-icon-loading.svg b/skins/vector/images/watch-icon-loading.svg
new file mode 100644 (file)
index 0000000..3423979
--- /dev/null
@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="16"
+   height="16"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.48.4 r9939"
+   sodipodi:docname="watch-icon-loading.svg">
+  <defs
+     id="defs4">
+    <linearGradient
+       id="linearGradient3788">
+      <stop
+         style="stop-color:#c2edff;stop-opacity:1;"
+         offset="0"
+         id="stop3790" />
+      <stop
+         id="stop3796"
+         offset="0.5"
+         style="stop-color:#68bdff;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="1"
+         id="stop3792" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3788"
+       id="linearGradient3804"
+       x1="13.470111"
+       y1="14.363379"
+       x2="4.596477"
+       y2="3.3969929"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3788"
+       id="linearGradient3808"
+       gradientUnits="userSpaceOnUse"
+       x1="13.470111"
+       y1="14.363379"
+       x2="4.596477"
+       y2="3.3969929" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="22.627417"
+     inkscape:cx="7.2850048"
+     inkscape:cy="6.4582896"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0"
+     inkscape:window-width="1920"
+     inkscape:window-height="1014"
+     inkscape:window-x="0"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>Mateusz &quot;m4tx&quot; Maćkowski</dc:title>
+          </cc:Agent>
+        </dc:creator>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-693.14288,-698.64789)">
+    <path
+       sodipodi:type="star"
+       style="fill:#ffffff;fill-opacity:1;stroke:#d1d1d1;stroke-width:1.15975237;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       id="path2998"
+       sodipodi:sides="5"
+       sodipodi:cx="8.3337584"
+       sodipodi:cy="7.6662416"
+       sodipodi:r1="8.587225"
+       sodipodi:r2="4.2936125"
+       sodipodi:arg1="-1.5707963"
+       sodipodi:arg2="-0.9424778"
+       inkscape:flatsided="false"
+       inkscape:rounded="0"
+       inkscape:randomized="0"
+       d="M 8.3337586,-0.92098331 10.85748,4.1926362 16.500695,5.0126434 12.417226,8.9930409 13.381202,14.613453 8.3337584,11.959854 3.286314,14.613452 4.2502902,8.9930409 0.16682217,5.012643 5.8100362,4.1926362 z"
+       transform="matrix(0.86225302,0,0,0.86225302,694.06156,700.58832)"
+       inkscape:transform-center-y="-0.70705002" />
+  </g>
+</svg>
diff --git a/skins/vector/images/watch-icon.png b/skins/vector/images/watch-icon.png
new file mode 100644 (file)
index 0000000..39daff2
Binary files /dev/null and b/skins/vector/images/watch-icon.png differ
diff --git a/skins/vector/images/watch-icon.svg b/skins/vector/images/watch-icon.svg
new file mode 100644 (file)
index 0000000..c7a5081
--- /dev/null
@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="16"
+   height="16"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.48.4 r9939"
+   sodipodi:docname="watch-icon.svg">
+  <defs
+     id="defs4">
+    <linearGradient
+       id="linearGradient3788">
+      <stop
+         style="stop-color:#c2edff;stop-opacity:1;"
+         offset="0"
+         id="stop3790" />
+      <stop
+         id="stop3796"
+         offset="0.5"
+         style="stop-color:#68bdff;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="1"
+         id="stop3792" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3788"
+       id="linearGradient3804"
+       x1="13.470111"
+       y1="14.363379"
+       x2="4.596477"
+       y2="3.3969929"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3788"
+       id="linearGradient3808"
+       gradientUnits="userSpaceOnUse"
+       x1="13.470111"
+       y1="14.363379"
+       x2="4.596477"
+       y2="3.3969929" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="8"
+     inkscape:cx="17.836203"
+     inkscape:cy="7.206728"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0"
+     inkscape:window-width="1920"
+     inkscape:window-height="1014"
+     inkscape:window-x="0"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>Mateusz &quot;m4tx&quot; Maćkowski</dc:title>
+          </cc:Agent>
+        </dc:creator>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-693.14288,-698.64789)">
+    <path
+       sodipodi:type="star"
+       style="fill:#ffffff;fill-opacity:1;stroke:#7cb5d1;stroke-width:1.15975237;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       id="path2998"
+       sodipodi:sides="5"
+       sodipodi:cx="8.3337584"
+       sodipodi:cy="7.6662416"
+       sodipodi:r1="8.587225"
+       sodipodi:r2="4.2936125"
+       sodipodi:arg1="-1.5707963"
+       sodipodi:arg2="-0.9424778"
+       inkscape:flatsided="false"
+       inkscape:rounded="0"
+       inkscape:randomized="0"
+       d="M 8.3337586,-0.92098331 10.85748,4.1926362 16.500695,5.0126434 12.417226,8.9930409 13.381202,14.613453 8.3337584,11.959854 3.286314,14.613452 4.2502902,8.9930409 0.16682217,5.012643 5.8100362,4.1926362 z"
+       transform="matrix(0.86225302,0,0,0.86225302,694.06156,700.58832)"
+       inkscape:transform-center-y="-0.70705002" />
+  </g>
+</svg>
diff --git a/skins/vector/images/watch-icons.png b/skins/vector/images/watch-icons.png
deleted file mode 100644 (file)
index 03aa7d5..0000000
Binary files a/skins/vector/images/watch-icons.png and /dev/null differ