Merge "mediawiki.action.edit.preview: Clean up and optimisation"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Tue, 8 Oct 2013 00:23:37 +0000 (00:23 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Tue, 8 Oct 2013 00:23:37 +0000 (00:23 +0000)
RELEASE-NOTES-1.22
includes/resourceloader/ResourceLoaderLanguageDataModule.php
includes/resourceloader/ResourceLoaderModule.php
maintenance/purgeChangedPages.php [new file with mode: 0644]
resources/mediawiki.less/mediawiki.mixins.less
resources/mediawiki.ui/mediawiki.ui.default.css
resources/mediawiki.ui/mediawiki.ui.vector.css
resources/mediawiki.ui/sourcefiles/scss/mixins/_forms.scss
resources/mediawiki/mediawiki.Title.js
skins/vector/screen.less
thumb.php

index 94d7828..171152a 100644 (file)
@@ -218,8 +218,8 @@ production.
   and Special:AllMyUploads respectively.
 * IPv6 addresses in X-Forwarded-For headers are now normalised before checking
   against allowed proxy lists.
-* Add deferrable update support for callback/closure
-* Add TitleMove hook before page renames
+* Add deferrable update support for callback/closure.
+* Add TitleMove hook before page renames.
 * Revision deletion backend code is moved out of SpecialRevisiondelete
 * Add a variable (wgRedactedFunctionArguments) to redact the values sent as certain function
   parameters from exception stack traces.
@@ -237,8 +237,8 @@ production.
    for more details regarding custom functions.
 ** $wgResourceLoaderLESSImportPaths is an array of file system paths. Files
    referenced in LESS '@import' statements are looked up here first.
-* Added meta=filerepoinfo API module for getting information about foreign
-  file repositories, and related ForeignAPIRepo methods getInfo and getApiUrl.
+* ResourceLoader supports hashes as module cache invalidation trigger (instead
+  of or in addition to timestamps).
 
 === Bug fixes in 1.22 ===
 * Disable Special:PasswordReset when $wgEnableEmail is false. Previously one
@@ -381,6 +381,8 @@ production.
   "exists-normalized" instead of filename to be uploaded to.
 * (bug 53884) action=edit will now return an error when the specified section
   does not exist in the page.
+* Added meta=filerepoinfo API module for getting information about foreign
+  file repositories, and related ForeignAPIRepo methods getInfo and getApiUrl.
 
 === Languages updated in 1.22===
 
index e840300..fa0fbf8 100644 (file)
@@ -75,9 +75,10 @@ class ResourceLoaderLanguageDataModule extends ResourceLoaderModule {
                return $this->language->separatorTransformTable();
        }
 
-
        /**
-        * Get all the dynamic data for the content language to an array
+        * Get all the dynamic data for the content language to an array.
+        *
+        * NOTE: Before calling this you HAVE to make sure $this->language is set.
         *
         * @return array
         */
@@ -105,26 +106,20 @@ class ResourceLoaderLanguageDataModule extends ResourceLoaderModule {
 
        /**
         * @param $context ResourceLoaderContext
-        * @return array|int|Mixed
+        * @return int: UNIX timestamp
         */
        public function getModifiedTime( ResourceLoaderContext $context ) {
-               $this->language = Language::factory( $context->getLanguage() );
-               $cache = wfGetCache( CACHE_ANYTHING );
-               $key = wfMemcKey( 'resourceloader', 'langdatamodule', 'changeinfo' );
+               return max( 1, $this->getHashMtime( $context ) );
+       }
 
-               $data = $this->getData();
-               $hash = md5( serialize( $data ) );
+       /**
+        * @param $context ResourceLoaderContext
+        * @return string: Hash
+        */
+       public function getModifiedHash( ResourceLoaderContext $context ) {
+               $this->language = Language::factory( $context->getLanguage() );
 
-               $result = $cache->get( $key );
-               if ( is_array( $result ) && $result['hash'] === $hash ) {
-                       return $result['timestamp'];
-               }
-               $timestamp = wfTimestamp();
-               $cache->set( $key, array(
-                       'hash' => $hash,
-                       'timestamp' => $timestamp,
-               ) );
-               return $timestamp;
+               return md5( serialize( $this->getData() ) );
        }
 
        /**
index 298f1fe..11264fc 100644 (file)
@@ -382,14 +382,59 @@ abstract class ResourceLoaderModule {
         * If you want this to happen, you'll need to call getMsgBlobMtime()
         * yourself and take its result into consideration.
         *
-        * @param ResourceLoaderContext $context
-        * @return int: UNIX timestamp
+        * NOTE: The mtime of the module's hash is NOT automatically included.
+        * If your module provides a getModifiedHash() method, you'll need to call getHashMtime()
+        * yourself and take its result into consideration.
+        *
+        * @param ResourceLoaderContext $context Context object
+        * @return integer UNIX timestamp
         */
        public function getModifiedTime( ResourceLoaderContext $context ) {
                // 0 would mean now
                return 1;
        }
 
+       /**
+        * Helper method for calculating when the module's hash (if it has one) changed.
+        *
+        * @param ResourceLoaderContext $context
+        * @return integer: UNIX timestamp or 0 if there is no hash provided
+        */
+       public function getHashMtime( ResourceLoaderContext $context ) {
+               $hash = $this->getModifiedHash( $context );
+               if ( !is_string( $hash ) ) {
+                       return 0;
+               }
+
+               $cache = wfGetCache( CACHE_ANYTHING );
+               $key = wfMemcKey( 'resourceloader', 'modulemodifiedhash', $this->getName() );
+
+               $data = $cache->get( $key );
+               if ( is_array( $data ) && $data['hash'] === $hash ) {
+                       // Hash is still the same, re-use the timestamp of when we first saw this hash.
+                       return $data['timestamp'];
+               }
+
+               $timestamp = wfTimestamp();
+               $cache->set( $key, array(
+                       'hash' => $hash,
+                       'timestamp' => $timestamp,
+               ) );
+
+               return $timestamp;
+       }
+
+       /**
+        * Get the last modification timestamp of the message blob for this
+        * module in a given language.
+        *
+        * @param ResourceLoaderContext $context
+        * @return string|null: Hash
+        */
+       public function getModifiedHash( ResourceLoaderContext $context ) {
+               return null;
+       }
+
        /**
         * Check whether this module is known to be empty. If a child class
         * has an easy and cheap way to determine that this module is
diff --git a/maintenance/purgeChangedPages.php b/maintenance/purgeChangedPages.php
new file mode 100644 (file)
index 0000000..e1c6ab6
--- /dev/null
@@ -0,0 +1,185 @@
+<?php
+/**
+ * Send purge requests for pages edited in date range to squid/varnish.
+ *
+ * @section LICENSE
+ * 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 Maintenance
+ */
+
+require_once __DIR__ . '/Maintenance.php';
+
+/**
+ * Maintenance script that sends purge requests for pages edited in a date
+ * range to squid/varnish.
+ *
+ * Can be used to recover from an HTCP message partition or other major cache
+ * layer interruption.
+ *
+ * @ingroup Maintenance
+ */
+class PurgeChangedPages extends Maintenance {
+
+       public function __construct() {
+               parent::__construct();
+               $this->mDescription = 'Send purge requests for edits in date range to squid/varnish';
+               $this->addOption( 'starttime', 'Starting timestamp', true, true );
+               $this->addOption( 'endtime', 'Ending timestamp', true, true );
+               $this->addOption( 'htcp-dest', 'HTCP announcement destination (IP:port)', false, true );
+               $this->addOption( 'dry-run', 'Do not send purge requests' );
+               $this->addOption( 'verbose', 'Show more output', false, false, 'v' );
+               $this->setBatchSize( 100 );
+       }
+
+       public function execute() {
+               global $wgHTCPRouting;
+
+               if ( $this->hasOption( 'htcp-dest' ) ) {
+                       $parts = explode( ':', $this->getOption( 'htcp-dest' ) );
+                       if ( count( $parts ) < 2 ) {
+                               // Add default htcp port
+                               $parts[] = '4827';
+                       }
+
+                       // Route all HTCP messages to provided host:port
+                       $wgHTCPRouting = array(
+                               '' => array( 'host' => $parts[0], 'port' => $parts[1] ),
+                       );
+                       if ( $this->hasOption( 'verbose' ) ) {
+                               $this->output( "HTCP broadcasts to {$parts[0]}:{$parts[1]}\n" );
+                       }
+               }
+
+               $dbr = $this->getDB( DB_SLAVE );
+               $minTime = $dbr->timestamp( $this->getOption( 'starttime' ) );
+               $maxTime = $dbr->timestamp( $this->getOption( 'endtime' ) );
+
+               if ( $maxTime < $minTime ) {
+                       $this->error( "\nERROR: starttime after endtime\n" );
+                       $this->maybeHelp( true );
+               }
+
+               $stuckCount = 0; // loop breaker
+               while ( true ) {
+                       // Adjust bach size if we are stuck in a second that had many changes
+                       $bSize = $this->mBatchSize + ( $stuckCount * $this->mBatchSize );
+
+                       $res = $dbr->select(
+                               array( 'page', 'revision' ),
+                               array(
+                                       'rev_timestamp',
+                                       'page_namespace',
+                                       'page_title',
+                               ),
+                               array(
+                                       "rev_timestamp > " . $dbr->addQuotes( $minTime ),
+                                       "rev_timestamp <= " . $dbr->addQuotes( $maxTime ),
+                                       // Only get rows where the revision is the latest for the page.
+                                       // Other revisions would be duplicate and we don't need to purge if
+                                       // there has been an edit after the interesting time window.
+                                       "page_latest = rev_id",
+                               ),
+                               __METHOD__,
+                               array( 'ORDER BY' => 'rev_timestamp', 'LIMIT' => $bSize ),
+                               array(
+                                       'page' => array( 'INNER JOIN', 'rev_page=page_id' ),
+                               )
+                       );
+
+                       if ( !$res->numRows() ) {
+                               // nothing more found so we are done
+                               break;
+                       }
+
+                       // Kludge to not get stuck in loops for batches with the same timestamp
+                       list( $rows, $lastTime ) = $this->pageableSortedRows( $res, 'rev_timestamp', $bSize );
+                       if ( !count( $rows ) ) {
+                               ++$stuckCount;
+                               continue;
+                       }
+                       // Reset suck counter
+                       $stuckCount = 0;
+
+                       $this->output( "Processing changes from {$minTime} to {$lastTime}.\n" );
+
+                       // Advance past the last row next time
+                       $minTime = $lastTime;
+
+                       // Create list of URLs from page_namespace + page_title
+                       $urls = array();
+                       foreach ( $rows as $row ) {
+                               $title = Title::makeTitle( $row->page_namespace, $row->page_title );
+                               $urls[] = $title->getInternalURL();
+                       }
+
+                       if ( $this->hasOption( 'dry-run' ) || $this->hasOption( 'verbose' ) ) {
+                               $this->output( implode( "\n", $urls ) . "\n" );
+                               if ( $this->hasOption( 'dry-run' ) ) {
+                                       continue;
+                               }
+                       }
+
+                       // Send batch of purge requests out to squids
+                       $squid = new SquidUpdate( $urls );
+                       $squid->doUpdate();
+               }
+
+               $this->output( "Done!\n" );
+       }
+
+       /**
+        * Remove all the rows in a result set with the highest value for column
+        * $column unless the number of rows is less $limit. This returns the new
+        * array of rows and the highest value of column $column for the rows left.
+        * The ordering of rows is maintained.
+        *
+        * This is useful for paging on mostly-unique values that may sometimes
+        * have large clumps of identical values. It should be safe to do the next
+        * query on items with a value higher than the highest of the rows returned here.
+        * If this returns an empty array for a non-empty query result, then all the rows
+        * had the same column value and the query should be repeated with a higher LIMIT.
+        *
+        * @TODO: move this elsewhere
+        *
+        * @param ResultWrapper $res Query result sorted by $column (ascending)
+        * @param string $column
+        * @return array (array of rows, string column value)
+        */
+       protected function pageableSortedRows( ResultWrapper $res, $column, $limit ) {
+               $rows = iterator_to_array( $res, false );
+               $count = count( $rows );
+               if ( !$count ) {
+                       return array( array(), null ); // nothing to do
+               } elseif ( $count < $limit ) {
+                       return array( $rows, $rows[$count - 1]->$column ); // no more rows left
+               }
+               $lastValue = $rows[$count - 1]->$column; // should be the highest
+               for ( $i = $count - 1; $i >= 0; --$i ) {
+                       if ( $rows[$i]->$column === $lastValue ) {
+                               unset( $rows[$i] );
+                       } else {
+                               break;
+                       }
+               }
+               $lastValueLeft = count( $rows ) ? $rows[count( $rows ) - 1]->$column : null;
+               return array( $rows, $lastValueLeft );
+       }
+}
+
+$maintClass = "PurgeChangedPages";
+require_once RUN_MAINTENANCE_IF_MAIN;
index cca5af4..19a715b 100644 (file)
@@ -38,9 +38,9 @@
        list-style-image: url(@url);
 }
 
-.transition( ... ) {
-       -moz-transition: @arguments;
-       -webkit-transition: @arguments;
-       -o-transition: @arguments;
-       transition: @arguments;
+.transition(@string) {
+       -webkit-transition: @string;
+       -moz-transition: @string;
+       -o-transition: @string;
+       transition: @string;
 }
index 89ca25a..498d134 100644 (file)
@@ -159,19 +159,22 @@ a.mw-ui-button {
 }
 /* line 36, sourcefiles/scss/components/default/_forms.scss */
 .mw-ui-vform > div input:not([type=button]):not([type=submit]):not([type=file]) {
-  outline: 0;
   border-style: solid;
   border-width: 1px;
   border-color: #c9c9c9;
   color: #252525;
   padding: 0.35em 0 0.35em 0.5em;
 }
-/* line 12, sourcefiles/scss/mixins/_forms.scss */
+/* line 11, sourcefiles/scss/mixins/_forms.scss */
 .mw-ui-vform > div input:not([type=button]):not([type=submit]):not([type=file]):focus {
   box-shadow: #4091ed 0px 0px 5px;
   border-color: #4091ed;
 }
-/* line 38, sourcefiles/scss/components/default/_forms.scss */
+/* line 13, sourcefiles/scss/mixins/_forms.scss */
+.mw-ui-vform > div input:not([type=button]):not([type=submit]):not([type=file]):focus:not([type=checkbox]):not([type=radio]) {
+  outline: 0;
+}
+/* line 40, sourcefiles/scss/components/default/_forms.scss */
 .mw-ui-vform > div label {
   display: block;
   -webkit-box-sizing: border-box;
@@ -183,11 +186,11 @@ a.mw-ui-button {
   margin: 0 0 0.2em 0;
   padding: 0;
 }
-/* line 34, sourcefiles/scss/mixins/_forms.scss */
+/* line 38, sourcefiles/scss/mixins/_forms.scss */
 .mw-ui-vform > div label * {
   font-weight: normal;
 }
-/* line 49, sourcefiles/scss/components/default/_forms.scss */
+/* line 51, sourcefiles/scss/components/default/_forms.scss */
 .mw-ui-vform > div input[type="checkbox"],
 .mw-ui-vform > div input[type="radio"] {
   display: inline;
@@ -197,32 +200,35 @@ a.mw-ui-button {
   width: auto;
 }
 
-/* line 65, sourcefiles/scss/components/default/_forms.scss */
+/* line 67, sourcefiles/scss/components/default/_forms.scss */
 .mw-ui-input {
-  outline: 0;
   border-style: solid;
   border-width: 1px;
   border-color: #c9c9c9;
   color: #252525;
   padding: 0.35em 0 0.35em 0.5em;
 }
-/* line 12, sourcefiles/scss/mixins/_forms.scss */
+/* line 11, sourcefiles/scss/mixins/_forms.scss */
 .mw-ui-input:focus {
   box-shadow: #4091ed 0px 0px 5px;
   border-color: #4091ed;
 }
+/* line 13, sourcefiles/scss/mixins/_forms.scss */
+.mw-ui-input:focus:not([type=checkbox]):not([type=radio]) {
+  outline: 0;
+}
 
-/* line 72, sourcefiles/scss/components/default/_forms.scss */
+/* line 74, sourcefiles/scss/components/default/_forms.scss */
 .mw-ui-label {
   font-size: 0.9em;
   color: #4a4a4a;
 }
-/* line 34, sourcefiles/scss/mixins/_forms.scss */
+/* line 38, sourcefiles/scss/mixins/_forms.scss */
 .mw-ui-label * {
   font-weight: normal;
 }
 
-/* line 81, sourcefiles/scss/components/default/_forms.scss */
+/* line 83, sourcefiles/scss/components/default/_forms.scss */
 .mw-ui-checkbox-label, .mw-ui-radio-label {
   margin-bottom: 0.5em;
   cursor: pointer;
@@ -230,7 +236,7 @@ a.mw-ui-button {
   line-height: normal;
   font-weight: normal;
 }
-/* line 50, sourcefiles/scss/mixins/_forms.scss */
+/* line 54, sourcefiles/scss/mixins/_forms.scss */
 .mw-ui-checkbox-label > input[type="checkbox"], .mw-ui-checkbox-label > input[type="radio"], .mw-ui-radio-label > input[type="checkbox"], .mw-ui-radio-label > input[type="radio"] {
   width: auto;
   height: auto;
index d55ddc5..6aa7f89 100644 (file)
@@ -287,19 +287,22 @@ a.mw-ui-button {
 }
 /* line 36, sourcefiles/scss/components/default/_forms.scss */
 .mw-ui-vform > div input:not([type=button]):not([type=submit]):not([type=file]) {
-  outline: 0;
   border-style: solid;
   border-width: 1px;
   border-color: #c9c9c9;
   color: #252525;
   padding: 0.35em 0 0.35em 0.5em;
 }
-/* line 12, sourcefiles/scss/mixins/_forms.scss */
+/* line 11, sourcefiles/scss/mixins/_forms.scss */
 .mw-ui-vform > div input:not([type=button]):not([type=submit]):not([type=file]):focus {
   box-shadow: #4091ed 0px 0px 5px;
   border-color: #4091ed;
 }
-/* line 38, sourcefiles/scss/components/default/_forms.scss */
+/* line 13, sourcefiles/scss/mixins/_forms.scss */
+.mw-ui-vform > div input:not([type=button]):not([type=submit]):not([type=file]):focus:not([type=checkbox]):not([type=radio]) {
+  outline: 0;
+}
+/* line 40, sourcefiles/scss/components/default/_forms.scss */
 .mw-ui-vform > div label {
   display: block;
   -webkit-box-sizing: border-box;
@@ -311,11 +314,11 @@ a.mw-ui-button {
   margin: 0 0 0.2em 0;
   padding: 0;
 }
-/* line 34, sourcefiles/scss/mixins/_forms.scss */
+/* line 38, sourcefiles/scss/mixins/_forms.scss */
 .mw-ui-vform > div label * {
   font-weight: normal;
 }
-/* line 49, sourcefiles/scss/components/default/_forms.scss */
+/* line 51, sourcefiles/scss/components/default/_forms.scss */
 .mw-ui-vform > div input[type="checkbox"],
 .mw-ui-vform > div input[type="radio"] {
   display: inline;
@@ -325,32 +328,35 @@ a.mw-ui-button {
   width: auto;
 }
 
-/* line 65, sourcefiles/scss/components/default/_forms.scss */
+/* line 67, sourcefiles/scss/components/default/_forms.scss */
 .mw-ui-input {
-  outline: 0;
   border-style: solid;
   border-width: 1px;
   border-color: #c9c9c9;
   color: #252525;
   padding: 0.35em 0 0.35em 0.5em;
 }
-/* line 12, sourcefiles/scss/mixins/_forms.scss */
+/* line 11, sourcefiles/scss/mixins/_forms.scss */
 .mw-ui-input:focus {
   box-shadow: #4091ed 0px 0px 5px;
   border-color: #4091ed;
 }
+/* line 13, sourcefiles/scss/mixins/_forms.scss */
+.mw-ui-input:focus:not([type=checkbox]):not([type=radio]) {
+  outline: 0;
+}
 
-/* line 72, sourcefiles/scss/components/default/_forms.scss */
+/* line 74, sourcefiles/scss/components/default/_forms.scss */
 .mw-ui-label {
   font-size: 0.9em;
   color: #4a4a4a;
 }
-/* line 34, sourcefiles/scss/mixins/_forms.scss */
+/* line 38, sourcefiles/scss/mixins/_forms.scss */
 .mw-ui-label * {
   font-weight: normal;
 }
 
-/* line 81, sourcefiles/scss/components/default/_forms.scss */
+/* line 83, sourcefiles/scss/components/default/_forms.scss */
 .mw-ui-checkbox-label, .mw-ui-radio-label {
   margin-bottom: 0.5em;
   cursor: pointer;
@@ -358,7 +364,7 @@ a.mw-ui-button {
   line-height: normal;
   font-weight: normal;
 }
-/* line 50, sourcefiles/scss/mixins/_forms.scss */
+/* line 54, sourcefiles/scss/mixins/_forms.scss */
 .mw-ui-checkbox-label > input[type="checkbox"], .mw-ui-checkbox-label > input[type="radio"], .mw-ui-radio-label > input[type="checkbox"], .mw-ui-radio-label > input[type="radio"] {
   width: auto;
   height: auto;
index 5db857a..0f3f6ad 100644 (file)
@@ -1,62 +1,66 @@
 // Font is not included.
 // For Vector, that should be layered on top with vector-type
 @mixin agora-field-styling() {
-    @include reset-focus;  // Removes OS field focus
-
-    border: {
-        style: solid;
-        width: 1px;
-        color: $agoraGray;
-    };
-
-    &:focus {
-        // @include box-shadow generates unneeded prefixes
-        // https://github.com/chriseppstein/compass/issues/1054 , so specify
-        // directly.
-        box-shadow: $agoraBlueShadow 0px 0px 5px;
-
-        border: {
-            color: $agoraBlueShadow;
-        };
-    }
-
-    color: $agoraTextColor;
-    padding: 0.35em 0 0.35em 0.5em;
+
+       border: {
+               style: solid;
+               width: 1px;
+               color: $agoraGray;
+       };
+
+       &:focus {
+               // Styling focus of native checkboxes etc on Mac is almost impossible.
+               &:not([type=checkbox]):not([type=radio]) {
+                       @include reset-focus;  // Removes OS field focus
+               };
+
+               // @include box-shadow generates unneeded prefixes
+               // https://github.com/chriseppstein/compass/issues/1054 , so specify
+               // directly.
+               box-shadow: $agoraBlueShadow 0px 0px 5px;
+
+               border: {
+                       color: $agoraBlueShadow;
+               };
+       }
+
+       color: $agoraTextColor;
+       padding: 0.35em 0 0.35em 0.5em;
 }
 
 @mixin agora-label-styling() {
-    font: {
-        //weight: bold;
-        size: 0.9em;
-    };
-    color: darken($agoraGray, 50%);
-
-    & * {
-        font-weight: normal;
-    }
+       font: {
+               //weight: bold;
+               size: 0.9em;
+       };
+       color: darken($agoraGray, 50%);
+
+       & * {
+               font-weight: normal;
+       }
 }
 
 @mixin agora-inline-label-styling() {
-    margin-bottom: 0.5em;
-    cursor: pointer;
-    vertical-align: bottom;
-    line-height: normal;
-
-    font: {
-        weight: normal;
-    };
-
-    & > input[type="checkbox"],
-    & > input[type="radio"] {
-        width: auto;
-        height: auto;
-        margin: 0 0.1em 0em 0;
-        padding: 0;
-        border: {
-            style: solid;
-            width: 1px;
-            color: $agoraGray;
-        }
-        cursor: pointer;
-    }
+       margin-bottom: 0.5em;
+       cursor: pointer;
+       vertical-align: bottom;
+       line-height: normal;
+
+       font: {
+               weight: normal;
+       };
+
+       & > input[type="checkbox"],
+       & > input[type="radio"] {
+               width: auto;
+               height: auto;
+               margin: 0 0.1em 0em 0;
+               padding: 0;
+               border: {
+                       style: solid;
+                       width: 1px;
+                       color: $agoraGray;
+               }
+               cursor: pointer;
+       }
 }
index 98277fc..4240f08 100644 (file)
@@ -9,9 +9,8 @@
         * @class mw.Title
         *
         * Parse titles into an object struture. Note that when using the constructor
-        * directly, passing invalid titles will result in an exception. Use
-        * #newFromText to use the logic directly and get null for invalid titles
-        * which is easier to work with.
+        * directly, passing invalid titles will result in an exception. Use #newFromText to use the
+        * logic directly and get null for invalid titles which is easier to work with.
         *
         * @constructor
         * @param {string} title Title of the page. If no second argument given,
index e00bcef..0b65627 100644 (file)
@@ -783,10 +783,10 @@ body.vector-animateLayout {
        div#content,
        div#footer,
        #left-navigation {
-               .transition( margin-left 250ms, padding 250ms );
+               .transition( margin-left 250ms, padding 250ms; );
        }
        #p-logo {
-               .transition( 'left 250ms' );
+               .transition( left 250ms );
        }
        #mw-panel {
                .transition( padding-right 250ms );
index ef2af24..4ffefb6 100644 (file)
--- a/thumb.php
+++ b/thumb.php
@@ -425,7 +425,7 @@ function wfExtractThumbParams( $file, $params ) {
        if ( $handler && $fileNamePos !== false ) {
                $paramString = substr( $thumbname, 0, $fileNamePos - 1 );
                $extraParams = $handler->parseParamString( $paramString );
-               if ( $handler !== false ) {
+               if ( $extraParams !== false ) {
                        return $params + $extraParams;
                }
        }