New more slick gallery display
authorBrian Wolff <bawolff+wn@gmail.com>
Sat, 8 Jun 2013 04:47:07 +0000 (01:47 -0300)
committerBrian Wolff <bawolff+wn@gmail.com>
Wed, 14 Aug 2013 23:56:05 +0000 (20:56 -0300)
This extension adds a "mode" parameter to the gallery
tag, allowing different formats for the gallery tag
(galleries in the ui can be controlled by a global)
The added modes are:
*traditional - The original gallery
*nolines - Like the original, no borders, less padding
*packed - All images aligned by having same height.
  JS also justifies the images.
  (I think this one is the one that will go over best
  with users.)
*packed-overlay - like packed, but caption goes over
  top the image in a transloucent box.
*packed-hover - like packed-overlay, but caption only
  visible on hover. Degrades gracefully on screen
  readers, and falls back to packed-overlay if
  you are using a touch screen. I kind of like
  this mode when the caption is not that important
  (ex a category where its just the file name).

This also adds a hook to allow people to make their
own gallery version. I believe there would be interest
in this, as different people have done different
experiments. For example:
* Wikia: http://community.wikia.com/wiki/Help:Galleries,_Slideshows,_and_Sliders/wikitext
* Wikinews: https://en.wikinews.org/wiki/Template:Picture_select

What I would like to see for this patch, is first it gets
enabled, with the default still "traditional". After
about a month or two we consult with users. If feedback
is positive, we change the default mode to one of the
others (probably "packed").

Adds a "mode" parameter to gallery for different
mode, including one 'height-constrained-overlay'
which looks much more like other modern websites.

Note: This makes one change to the old gallery format.
It makes Nonexistent files be rendered like thumbnails
(i.e. they are rendered with a little grey border).

One thing I'm slightly worried about with this patch,
is that I added an option to MediaTransformOutput::toHtml
to override the width attribute. I'm not sure if that
is the best approach, and would appreciate thoughts
on that.

This should be merged at the same time as Ie82c1548

Change-Id: I33462a8b52502ed76aeb163b66e3704c8618ba23

21 files changed:
RELEASE-NOTES-1.22
docs/hooks.txt
includes/AutoLoader.php
includes/CategoryViewer.php
includes/DefaultSettings.php
includes/ImageGallery.php [deleted file]
includes/ImageQueryPage.php
includes/filerepo/file/ForeignAPIFile.php
includes/gallery/ImageGalleryBase.php [new file with mode: 0644]
includes/gallery/NolinesImageGallery.php [new file with mode: 0644]
includes/gallery/PackedImageGallery.php [new file with mode: 0644]
includes/gallery/PackedOverlayImageGallery.php [new file with mode: 0644]
includes/gallery/TraditionalImageGallery.php [new file with mode: 0644]
includes/media/MediaTransformOutput.php
includes/parser/Parser.php
includes/specials/SpecialNewimages.php
includes/specials/SpecialUpload.php
resources/Resources.php
resources/mediawiki.page/mediawiki.page.gallery.js [new file with mode: 0644]
skins/common/shared.css
tests/parser/parserTests.txt

index cfe553e..780feb9 100644 (file)
@@ -43,6 +43,9 @@ production.
   an entry for 'bar.com' will still match 'foo.bar.com' but not 'foobar.com'.
 * $wgCopyUploadTimeout and $wgCopyUploadAsyncTimeout added to change the timeout times for
   fetching the file during upload by url.
+* New key added to $wgGalleryOptions - $wgGalleryOptions['mode'] to set
+  default gallery mode.
+* New hook 'GalleryGetModes' to allow extensions to make new gallery modes.
 
 === New features in 1.22 ===
 * (bug 44525) mediawiki.jqueryMsg can now parse (whitelisted) HTML elements and attributes.
@@ -173,6 +176,7 @@ production.
 * (bug 30713) New mw.hook "wikipage.content".
 * (bug 40430) jquery.placeholder gets a new parameter to set the attribute value
   to be used.
+<<<<<<< .merge_file_rX8GYk
 * $wgHTCPMulticastRouting renamed $wgHTCPRouting since it accepts unicast.
 * $wgHTCPRouting rules can now be passed an array of hosts/ports to send purge
   too. Can be used whenever several multicast group could be interested by a
@@ -184,6 +188,10 @@ production.
   setcookie() or setrawcookie() should begin using this instead.
 * New hook WebResponseSetCookie, called from WebResponse::setcookie().
 * New hook ResetSessionID, called when the session id is reset.
+=======
+* Add a mode parameter to <gallery> tag with potential options of "traditional",
+  "nolines", "packed", "packed-overlay", or "packed-hover".
+>>>>>>> .merge_file_gNstr1
 
 === Bug fixes in 1.22 ===
 * Disable Special:PasswordReset when $wgEnableEmail is false. Previously one
index 1f25b47..9d3da1c 100644 (file)
@@ -1117,6 +1117,12 @@ $reason: reason
  $title: An optional title object used to links to sections. Can be null.
  $local: Boolean indicating whether section links should refer to local page.
 
+'GalleryGetModes': Get list of classes that can render different modes of a
+ gallery
+$modeArray: An associative array mapping mode names to classes that implement
+ that mode. It is expected all registered classes are a subclass of
+ ImageGalleryBase.
+
 'GetAutoPromoteGroups': When determining which autopromote groups a user is
 entitled to be in.
 &$user: user to promote.
index 42d7d88..45ad168 100644 (file)
@@ -136,7 +136,6 @@ $wgAutoloadLocalClasses = array(
        'ICacheHelper' => 'includes/CacheHelper.php',
        'IcuCollation' => 'includes/Collation.php',
        'IdentityCollation' => 'includes/Collation.php',
-       'ImageGallery' => 'includes/ImageGallery.php',
        'ImageHistoryList' => 'includes/ImagePage.php',
        'ImageHistoryPseudoPager' => 'includes/ImagePage.php',
        'ImagePage' => 'includes/ImagePage.php',
@@ -721,6 +720,16 @@ $wgAutoloadLocalClasses = array(
        'RCDatabaseLogEntry' => 'includes/logging/LogEntry.php',
        'RightsLogFormatter' => 'includes/logging/RightsLogFormatter.php',
 
+       # Image gallery
+
+       'ImageGallery' => 'includes/gallery/TraditionalImageGallery.php',
+       'ImageGalleryBase' => 'includes/gallery/ImageGalleryBase.php',
+       'NolinesImageGallery' => 'includes/gallery/NolinesImageGallery.php',
+       'TraditionalImageGallery' => 'includes/gallery/TraditionalImageGallery.php',
+       'PackedImageGallery' => 'includes/gallery/PackedImageGallery.php',
+       'PackedHoverImageGallery' => 'includes/gallery/PackedOverlayImageGallery.php',
+       'PackedOverlayImageGallery' => 'includes/gallery/PackedOverlayImageGallery.php',
+
        # includes/media
        'BitmapHandler' => 'includes/media/Bitmap.php',
        'BitmapHandler_ClientOnly' => 'includes/media/Bitmap_ClientOnly.php',
index a98f792..562ca0c 100644 (file)
@@ -141,8 +141,9 @@ class CategoryViewer extends ContextSource {
                $this->children = array();
                $this->children_start_char = array();
                if ( $this->showGallery ) {
-                       $this->gallery = new ImageGallery();
+                       $this->gallery = ImageGalleryBase::factory();
                        $this->gallery->setHideBadImages();
+                       $this->gallery->setContext( $this->getContext() );
                } else {
                        $this->imgsNoGallery = array();
                        $this->imgsNoGallery_start_char = array();
index 0266659..87c1b6b 100644 (file)
@@ -1203,6 +1203,7 @@ $wgGalleryOptions = array(
        'imageHeight' => 120, // Height of the cells containing images in galleries (in "px")
        'captionLength' => 25, // Length of caption to truncate (in characters)
        'showBytes' => true, // Show the filesize in bytes in categories
+       'mode' => 'traditional',
 );
 
 /**
diff --git a/includes/ImageGallery.php b/includes/ImageGallery.php
deleted file mode 100644 (file)
index b4ef904..0000000
+++ /dev/null
@@ -1,425 +0,0 @@
-<?php
-/**
- * Image gallery.
- *
- * 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
- */
-
-/**
- * Image gallery
- *
- * Add images to the gallery using add(), then render that list to HTML using toHTML().
- *
- * @ingroup Media
- */
-class ImageGallery {
-       var $mImages, $mShowBytes, $mShowFilename;
-       var $mCaption = false;
-
-       /**
-        * Hide blacklisted images?
-        */
-       var $mHideBadImages;
-
-       /**
-        * Registered parser object for output callbacks
-        * @var Parser
-        */
-       var $mParser;
-
-       /**
-        * Contextual title, used when images are being screened
-        * against the bad image list
-        */
-       protected $contextTitle = false;
-
-       protected $mAttribs = array();
-
-       /**
-        * Fixed margins
-        */
-       const THUMB_PADDING = 30;
-       const GB_PADDING = 5;
-       // 2px borders on each side + 2px implied padding on each side
-       const GB_BORDERS = 8;
-
-       /**
-        * Create a new image gallery object.
-        */
-       function __construct() {
-               global $wgGalleryOptions;
-               $this->mImages = array();
-               $this->mShowBytes = $wgGalleryOptions['showBytes'];
-               $this->mShowFilename = true;
-               $this->mParser = false;
-               $this->mHideBadImages = false;
-               $this->mPerRow = $wgGalleryOptions['imagesPerRow'];
-               $this->mWidths = $wgGalleryOptions['imageWidth'];
-               $this->mHeights = $wgGalleryOptions['imageHeight'];
-               $this->mCaptionLength = $wgGalleryOptions['captionLength'];
-       }
-
-       /**
-        * Register a parser object
-        *
-        * @param $parser Parser
-        */
-       function setParser( $parser ) {
-               $this->mParser = $parser;
-       }
-
-       /**
-        * Set bad image flag
-        */
-       function setHideBadImages( $flag = true ) {
-               $this->mHideBadImages = $flag;
-       }
-
-       /**
-        * Set the caption (as plain text)
-        *
-        * @param string $caption Caption
-        */
-       function setCaption( $caption ) {
-               $this->mCaption = htmlspecialchars( $caption );
-       }
-
-       /**
-        * Set the caption (as HTML)
-        *
-        * @param string $caption Caption
-        */
-       public function setCaptionHtml( $caption ) {
-               $this->mCaption = $caption;
-       }
-
-       /**
-        * Set how many images will be displayed per row.
-        *
-        * @param $num Integer >= 0; If perrow=0 the gallery layout will adapt to screensize
-        * invalid numbers will be rejected
-        */
-       public function setPerRow( $num ) {
-               if ( $num >= 0 ) {
-                       $this->mPerRow = (int)$num;
-               }
-       }
-
-       /**
-        * Set how wide each image will be, in pixels.
-        *
-        * @param $num Integer > 0; invalid numbers will be ignored
-        */
-       public function setWidths( $num ) {
-               if ( $num > 0 ) {
-                       $this->mWidths = (int)$num;
-               }
-       }
-
-       /**
-        * Set how high each image will be, in pixels.
-        *
-        * @param $num Integer > 0; invalid numbers will be ignored
-        */
-       public function setHeights( $num ) {
-               if ( $num > 0 ) {
-                       $this->mHeights = (int)$num;
-               }
-       }
-
-       /**
-        * Instruct the class to use a specific skin for rendering
-        *
-        * @param $skin Skin object
-        * @deprecated since 1.18 Not used anymore
-        */
-       function useSkin( $skin ) {
-               wfDeprecated( __METHOD__, '1.18' );
-               /* no op */
-       }
-
-       /**
-        * Add an image to the gallery.
-        *
-        * @param $title Title object of the image that is added to the gallery
-        * @param $html  String: Additional HTML text to be shown. The name and size of the image are always shown.
-        * @param $alt   String: Alt text for the image
-        * @param $link  String: Override image link (optional)
-        * @param $handlerOpts Array: Array of options for image handler (aka page number)
-        */
-       function add( $title, $html = '', $alt = '', $link = '', $handlerOpts = array() ) {
-               if ( $title instanceof File ) {
-                       // Old calling convention
-                       $title = $title->getTitle();
-               }
-               $this->mImages[] = array( $title, $html, $alt, $link, $handlerOpts );
-               wfDebug( 'ImageGallery::add ' . $title->getText() . "\n" );
-       }
-
-       /**
-        * Add an image at the beginning of the gallery.
-        *
-        * @param $title Title object of the image that is added to the gallery
-        * @param $html  String: Additional HTML text to be shown. The name and size of the image are always shown.
-        * @param $alt   String: Alt text for the image
-        * @param $link  String: Override image link (optional)
-        * @param $handlerOpts Array: Array of options for image handler (aka page number)
-        */
-       function insert( $title, $html = '', $alt = '', $link = '', $handlerOpts = array() ) {
-               if ( $title instanceof File ) {
-                       // Old calling convention
-                       $title = $title->getTitle();
-               }
-               array_unshift( $this->mImages, array( &$title, $html, $alt, $link, $handlerOpts ) );
-       }
-
-       /**
-        * isEmpty() returns true if the gallery contains no images
-        * @return bool
-        */
-       function isEmpty() {
-               return empty( $this->mImages );
-       }
-
-       /**
-        * Enable/Disable showing of the file size of an image in the gallery.
-        * Enabled by default.
-        *
-        * @param $f Boolean: set to false to disable.
-        */
-       function setShowBytes( $f ) {
-               $this->mShowBytes = (bool)$f;
-       }
-
-       /**
-        * Enable/Disable showing of the filename of an image in the gallery.
-        * Enabled by default.
-        *
-        * @param $f Boolean: set to false to disable.
-        */
-       function setShowFilename( $f ) {
-               $this->mShowFilename = (bool)$f;
-       }
-
-       /**
-        * Set arbitrary attributes to go on the HTML gallery output element.
-        * Should be suitable for a <ul> element.
-        *
-        * Note -- if taking from user input, you should probably run through
-        * Sanitizer::validateAttributes() first.
-        *
-        * @param array $attribs of HTML attribute pairs
-        */
-       function setAttributes( $attribs ) {
-               $this->mAttribs = $attribs;
-       }
-
-       /**
-        * Return a HTML representation of the image gallery
-        *
-        * For each image in the gallery, display
-        * - a thumbnail
-        * - the image name
-        * - the additional text provided when adding the image
-        * - the size of the image
-        *
-        * @return string
-        */
-       function toHTML() {
-               if ( $this->mPerRow > 0 ) {
-                       $maxwidth = $this->mPerRow * ( $this->mWidths + self::THUMB_PADDING + self::GB_PADDING + self::GB_BORDERS );
-                       $oldStyle = isset( $this->mAttribs['style'] ) ? $this->mAttribs['style'] : '';
-                       # _width is ignored by any sane browser. IE6 doesn't know max-width so it uses _width instead
-                       $this->mAttribs['style'] = "max-width: {$maxwidth}px;_width: {$maxwidth}px;" . $oldStyle;
-               }
-
-               $attribs = Sanitizer::mergeAttributes(
-                       array( 'class' => 'gallery' ), $this->mAttribs );
-
-               $output = Xml::openElement( 'ul', $attribs );
-               if ( $this->mCaption ) {
-                       $output .= "\n\t<li class='gallerycaption'>{$this->mCaption}</li>";
-               }
-
-               $lang = $this->getLang();
-               $params = array(
-                       'width' => $this->mWidths,
-                       'height' => $this->mHeights
-               );
-               # Output each image...
-               foreach ( $this->mImages as $pair ) {
-                       $nt = $pair[0];
-                       $text = $pair[1]; # "text" means "caption" here
-                       $alt = $pair[2];
-                       $link = $pair[3];
-                       // $pair[4] is per image handler options
-                       $transformOptions = $params + $pair[4];
-
-                       $descQuery = false;
-                       if ( $nt->getNamespace() == NS_FILE ) {
-                               # Get the file...
-                               if ( $this->mParser instanceof Parser ) {
-                                       # Give extensions a chance to select the file revision for us
-                                       $options = array();
-                                       wfRunHooks( 'BeforeParserFetchFileAndTitle',
-                                               array( $this->mParser, $nt, &$options, &$descQuery ) );
-                                       # Fetch and register the file (file title may be different via hooks)
-                                       list( $img, $nt ) = $this->mParser->fetchFileAndTitle( $nt, $options );
-                               } else {
-                                       $img = wfFindFile( $nt );
-                               }
-                       } else {
-                               $img = false;
-                       }
-
-                       if ( !$img ) {
-                               # We're dealing with a non-image, spit out the name and be done with it.
-                               $thumbhtml = "\n\t\t\t" . '<div style="height: ' . ( self::THUMB_PADDING + $this->mHeights ) . 'px;">'
-                                       . htmlspecialchars( $nt->getText() ) . '</div>';
-
-                               if ( $this->mParser instanceof Parser ) {
-                                       $this->mParser->addTrackingCategory( 'broken-file-category' );
-                               }
-                       } elseif ( $this->mHideBadImages && wfIsBadImage( $nt->getDBkey(), $this->getContextTitle() ) ) {
-                               # The image is blacklisted, just show it as a text link.
-                               $thumbhtml = "\n\t\t\t" . '<div style="height: ' . ( self::THUMB_PADDING + $this->mHeights ) . 'px;">' .
-                                       Linker::link(
-                                               $nt,
-                                               htmlspecialchars( $nt->getText() ),
-                                               array(),
-                                               array(),
-                                               array( 'known', 'noclasses' )
-                                       ) .
-                                       '</div>';
-                       } elseif ( !( $thumb = $img->transform( $transformOptions ) ) ) {
-                               # Error generating thumbnail.
-                               $thumbhtml = "\n\t\t\t" . '<div style="height: ' . ( self::THUMB_PADDING + $this->mHeights ) . 'px;">'
-                                       . htmlspecialchars( $img->getLastError() ) . '</div>';
-                       } else {
-                               $vpad = ( self::THUMB_PADDING + $this->mHeights - $thumb->height ) / 2;
-
-                               $imageParameters = array(
-                                       'desc-link' => true,
-                                       'desc-query' => $descQuery,
-                                       'alt' => $alt,
-                                       'custom-url-link' => $link
-                               );
-                               # In the absence of both alt text and caption, fall back on providing screen readers with the filename as alt text
-                               if ( $alt == '' && $text == '' ) {
-                                       $imageParameters['alt'] = $nt->getText();
-                               }
-
-                               # Set both fixed width and min-height.
-                               $thumbhtml = "\n\t\t\t" .
-                                       '<div class="thumb" style="width: ' . ( $this->mWidths + self::THUMB_PADDING ) . 'px;">'
-                                       # Auto-margin centering for block-level elements. Needed now that we have video
-                                       # handlers since they may emit block-level elements as opposed to simple <img> tags.
-                                       # ref http://css-discuss.incutio.com/?page=CenteringBlockElement
-                                       . '<div style="margin:' . $vpad . 'px auto;">'
-                                       . $thumb->toHtml( $imageParameters ) . '</div></div>';
-
-                               // Call parser transform hook
-                               if ( $this->mParser && $img->getHandler() ) {
-                                       $img->getHandler()->parserTransformHook( $this->mParser, $img );
-                               }
-                       }
-
-                       //TODO
-                       // $linkTarget = Title::newFromText( $wgContLang->getNsText( MWNamespace::getUser() ) . ":{$ut}" );
-                       // $ul = Linker::link( $linkTarget, $ut );
-
-                       if ( $this->mShowBytes ) {
-                               if ( $img ) {
-                                       $fileSize = htmlspecialchars( $lang->formatSize( $img->getSize() ) );
-                               } else {
-                                       $fileSize = wfMessage( 'filemissing' )->escaped();
-                               }
-                               $fileSize = "$fileSize<br />\n";
-                       } else {
-                               $fileSize = '';
-                       }
-
-                       $textlink = $this->mShowFilename ?
-                               Linker::link(
-                                       $nt,
-                                       htmlspecialchars( $lang->truncate( $nt->getText(), $this->mCaptionLength ) ),
-                                       array(),
-                                       array(),
-                                       array( 'known', 'noclasses' )
-                               ) . "<br />\n" :
-                               '';
-
-                       # ATTENTION: The newline after <div class="gallerytext"> is needed to accommodate htmltidy which
-                       # in version 4.8.6 generated crackpot html in its absence, see:
-                       # http://bugzilla.wikimedia.org/show_bug.cgi?id=1765 -Ævar
-
-                       # Weird double wrapping (the extra div inside the li) needed due to FF2 bug
-                       # Can be safely removed if FF2 falls completely out of existence
-                       $output .=
-                               "\n\t\t" . '<li class="gallerybox" style="width: ' . ( $this->mWidths + self::THUMB_PADDING + self::GB_PADDING ) . 'px">'
-                                       . '<div style="width: ' . ( $this->mWidths + self::THUMB_PADDING + self::GB_PADDING ) . 'px">'
-                                       . $thumbhtml
-                                       . "\n\t\t\t" . '<div class="gallerytext">' . "\n"
-                                       . $textlink . $text . $fileSize
-                                       . "\n\t\t\t</div>"
-                                       . "\n\t\t</div></li>";
-               }
-               $output .= "\n</ul>";
-
-               return $output;
-       }
-
-       /**
-        * @return Integer: number of images in the gallery
-        */
-       public function count() {
-               return count( $this->mImages );
-       }
-
-       /**
-        * Set the contextual title
-        *
-        * @param $title Title: contextual title
-        */
-       public function setContextTitle( $title ) {
-               $this->contextTitle = $title;
-       }
-
-       /**
-        * Get the contextual title, if applicable
-        *
-        * @return mixed Title or false
-        */
-       public function getContextTitle() {
-               return is_object( $this->contextTitle ) && $this->contextTitle instanceof Title
-                       ? $this->contextTitle
-                       : false;
-       }
-
-       /**
-        * Determines the correct language to be used for this image gallery
-        * @return Language object
-        */
-       private function getLang() {
-               global $wgLang;
-               return $this->mParser
-                       ? $this->mParser->getTargetLanguage()
-                       : $wgLang;
-       }
-
-} //class
index eeec5cd..75f7ba6 100644 (file)
@@ -42,7 +42,8 @@ abstract class ImageQueryPage extends QueryPage {
         */
        protected function outputResults( $out, $skin, $dbr, $res, $num, $offset ) {
                if ( $num > 0 ) {
-                       $gallery = new ImageGallery();
+                       $gallery = ImageGalleryBase::factory();
+                       $gallery->setContext( $this->getContext() );
 
                        # $res might contain the whole 1,000 rows, so we read up to
                        # $num [should update this to use a Pager]
index 8829cd9..ed96d44 100644 (file)
@@ -86,7 +86,7 @@ class ForeignAPIFile extends File {
         * @return string
         */
        static function getProps() {
-               return 'timestamp|user|comment|url|size|sha1|metadata|mime';
+               return 'timestamp|user|comment|url|size|sha1|metadata|mime|mediatype';
        }
 
        // Dummy functions...
@@ -245,10 +245,12 @@ class ForeignAPIFile extends File {
        }
 
        /**
-        * @todo FIXME: May guess wrong on file types that can be eg audio or video
         * @return int|string
         */
        function getMediaType() {
+               if ( isset( $this->mInfo['mediatype'] ) ) {
+                       return $this->mInfo['mediatype'];
+               }
                $magic = MimeMagic::singleton();
                return $magic->getMediaType( null, $this->getMimeType() );
        }
diff --git a/includes/gallery/ImageGalleryBase.php b/includes/gallery/ImageGalleryBase.php
new file mode 100644 (file)
index 0000000..f8b8c50
--- /dev/null
@@ -0,0 +1,331 @@
+<?php
+/**
+ * Image gallery.
+ *
+ * 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
+ */
+
+/**
+ * Image gallery
+ *
+ * Add images to the gallery using add(), then render that list to HTML using toHTML().
+ *
+ * @ingroup Media
+ */
+abstract class ImageGalleryBase extends ContextSource {
+       var $mImages, $mShowBytes, $mShowFilename, $mMode;
+       var $mCaption = false;
+
+       /**
+        * Hide blacklisted images?
+        */
+       var $mHideBadImages;
+
+       /**
+        * Registered parser object for output callbacks
+        * @var Parser
+        */
+       var $mParser;
+
+       /**
+        * Contextual title, used when images are being screened
+        * against the bad image list
+        */
+       protected $contextTitle = false;
+
+       protected $mAttribs = array();
+
+       static private $modeMapping = false;
+
+       /**
+        * Get a new image gallery. This is the method other callers
+        * should use to get a gallery.
+        *
+        * @param String|bool $mode Mode to use. False to use the default.
+        */
+       static function factory( $mode = false ) {
+               global $wgGalleryOptions, $wgContLang;
+               self::loadModes();
+               if ( !$mode ) {
+                       $mode = $wgGalleryOptions['mode'];
+               }
+
+               $mode = $wgContLang->lc( $mode );
+
+               if ( isset( self::$modeMapping[$mode] ) ) {
+                       return new self::$modeMapping[$mode]( $mode );
+               } else {
+                       throw new MWException( "No gallery class registered for mode $mode" );
+               }
+       }
+
+       static private function loadModes() {
+               if ( self::$modeMapping === false ) {
+                       self::$modeMapping = array(
+                               'traditional' => 'TraditionalImageGallery',
+                               'nolines' => 'NolinesImageGallery',
+                               'packed' => 'PackedImageGallery',
+                               'packed-hover' => 'PackedHoverImageGallery',
+                               'packed-overlay' => 'PackedOverlayImageGallery',
+                       );
+                       // Allow extensions to make a new gallery format.
+                       wfRunHooks( 'GalleryGetModes', self::$modeMapping );
+               }
+       }
+
+       /**
+        * Create a new image gallery object.
+        *
+        * You should not call this directly, but instead use
+        * ImageGalleryBase::factory().
+        */
+       function __construct( $mode = 'traditional' ) {
+               global $wgGalleryOptions;
+               $this->mImages = array();
+               $this->mShowBytes = $wgGalleryOptions['showBytes'];
+               $this->mShowFilename = true;
+               $this->mParser = false;
+               $this->mHideBadImages = false;
+               $this->mPerRow = $wgGalleryOptions['imagesPerRow'];
+               $this->mWidths = $wgGalleryOptions['imageWidth'];
+               $this->mHeights = $wgGalleryOptions['imageHeight'];
+               $this->mCaptionLength = $wgGalleryOptions['captionLength'];
+               $this->mMode = $mode;
+       }
+
+       /**
+        * Register a parser object. If you do not set this
+        * and the output of this gallery ends up in parser
+        * cache, the javascript will break!
+        *
+        * @note This also triggers using the page's target
+        *  language instead of the user language.
+        *
+        * @param $parser Parser
+        */
+       function setParser( $parser ) {
+               $this->mParser = $parser;
+       }
+
+       /**
+        * Set bad image flag
+        */
+       function setHideBadImages( $flag = true ) {
+               $this->mHideBadImages = $flag;
+       }
+
+       /**
+        * Set the caption (as plain text)
+        *
+        * @param string $caption Caption
+        */
+       function setCaption( $caption ) {
+               $this->mCaption = htmlspecialchars( $caption );
+       }
+
+       /**
+        * Set the caption (as HTML)
+        *
+        * @param string $caption Caption
+        */
+       public function setCaptionHtml( $caption ) {
+               $this->mCaption = $caption;
+       }
+
+       /**
+        * Set how many images will be displayed per row.
+        *
+        * @param $num Integer >= 0; If perrow=0 the gallery layout will adapt to screensize
+        * invalid numbers will be rejected
+        */
+       public function setPerRow( $num ) {
+               if ( $num >= 0 ) {
+                       $this->mPerRow = (int)$num;
+               }
+       }
+
+       /**
+        * Set how wide each image will be, in pixels.
+        *
+        * @param $num Integer > 0; invalid numbers will be ignored
+        */
+       public function setWidths( $num ) {
+               if ( $num > 0 ) {
+                       $this->mWidths = (int)$num;
+               }
+       }
+
+       /**
+        * Set how high each image will be, in pixels.
+        *
+        * @param $num Integer > 0; invalid numbers will be ignored
+        */
+       public function setHeights( $num ) {
+               if ( $num > 0 ) {
+                       $this->mHeights = (int)$num;
+               }
+       }
+
+       /**
+        * Allow setting additional options. This is meant
+        * to allow extensions to add additional parameters to
+        * <gallery> parser tag.
+        *
+        * @param Array $options Attributes of gallery tag.
+        */
+       public function setAdditionalOptions( $options ) { }
+
+       /**
+        * Instruct the class to use a specific skin for rendering
+        *
+        * @param $skin Skin object
+        * @deprecated since 1.18 Not used anymore
+        */
+       function useSkin( $skin ) {
+               wfDeprecated( __METHOD__, '1.18' );
+               /* no op */
+       }
+
+       /**
+        * Add an image to the gallery.
+        *
+        * @param $title Title object of the image that is added to the gallery
+        * @param $html  String: Additional HTML text to be shown. The name and size of the image are always shown.
+        * @param $alt   String: Alt text for the image
+        * @param $link  String: Override image link (optional)
+        * @param $handlerOpts Array: Array of options for image handler (aka page number)
+        */
+       function add( $title, $html = '', $alt = '', $link = '', $handlerOpts = array() ) {
+               if ( $title instanceof File ) {
+                       // Old calling convention
+                       $title = $title->getTitle();
+               }
+               $this->mImages[] = array( $title, $html, $alt, $link, $handlerOpts );
+               wfDebug( 'ImageGallery::add ' . $title->getText() . "\n" );
+       }
+
+       /**
+        * Add an image at the beginning of the gallery.
+        *
+        * @param $title Title object of the image that is added to the gallery
+        * @param $html  String: Additional HTML text to be shown. The name and size of the image are always shown.
+        * @param $alt   String: Alt text for the image
+        * @param $link  String: Override image link (optional)
+        * @param $handlerOpts Array: Array of options for image handler (aka page number)
+        */
+       function insert( $title, $html = '', $alt = '', $link = '', $handlerOpts = array() ) {
+               if ( $title instanceof File ) {
+                       // Old calling convention
+                       $title = $title->getTitle();
+               }
+               array_unshift( $this->mImages, array( &$title, $html, $alt, $link, $handlerOpts ) );
+       }
+
+       /**
+        * isEmpty() returns true if the gallery contains no images
+        * @return bool
+        */
+       function isEmpty() {
+               return empty( $this->mImages );
+       }
+
+       /**
+        * Enable/Disable showing of the file size of an image in the gallery.
+        * Enabled by default.
+        *
+        * @param $f Boolean: set to false to disable.
+        */
+       function setShowBytes( $f ) {
+               $this->mShowBytes = (bool)$f;
+       }
+
+       /**
+        * Enable/Disable showing of the filename of an image in the gallery.
+        * Enabled by default.
+        *
+        * @param $f Boolean: set to false to disable.
+        */
+       function setShowFilename( $f ) {
+               $this->mShowFilename = (bool)$f;
+       }
+
+       /**
+        * Set arbitrary attributes to go on the HTML gallery output element.
+        * Should be suitable for a <ul> element.
+        *
+        * Note -- if taking from user input, you should probably run through
+        * Sanitizer::validateAttributes() first.
+        *
+        * @param array $attribs of HTML attribute pairs
+        */
+       function setAttributes( $attribs ) {
+               $this->mAttribs = $attribs;
+       }
+
+       /**
+        * Display an html representation of the gallery
+        *
+        * @return String The html
+        */
+       abstract public function toHTML();
+
+       /**
+        * @return Integer: number of images in the gallery
+        */
+       public function count() {
+               return count( $this->mImages );
+       }
+
+       /**
+        * Set the contextual title
+        *
+        * @param $title Title: contextual title
+        */
+       public function setContextTitle( $title ) {
+               $this->contextTitle = $title;
+       }
+
+       /**
+        * Get the contextual title, if applicable
+        *
+        * @return mixed Title or false
+        */
+       public function getContextTitle() {
+               return is_object( $this->contextTitle ) && $this->contextTitle instanceof Title
+                       ? $this->contextTitle
+                       : false;
+       }
+
+       /**
+        * Determines the correct language to be used for this image gallery
+        * @return Language object
+        */
+       protected function getRenderLang() {
+               return $this->mParser
+                       ? $this->mParser->getTargetLanguage()
+                       : $this->getLanguage();
+       }
+
+       /* Old constants no longer used.
+       const THUMB_PADDING = 30;
+       const GB_PADDING = 5;
+       const GB_BORDERS = 8;
+       */
+
+}
+
diff --git a/includes/gallery/NolinesImageGallery.php b/includes/gallery/NolinesImageGallery.php
new file mode 100644 (file)
index 0000000..9e0a494
--- /dev/null
@@ -0,0 +1,37 @@
+<?php
+/**
+ * Nolines image gallery. Like "traditional" but without borders and
+ * less padding.
+ *
+ * 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
+ */
+
+class NolinesImageGallery extends TraditionalImageGallery {
+
+       protected function getThumbPadding() {
+               return 0;
+       }
+
+       protected function getGBBorders() {
+               return 0;
+       }
+
+       protected function getVPad( $boxHeight, $thumbHeight ) {
+               return 0;
+       }
+}
diff --git a/includes/gallery/PackedImageGallery.php b/includes/gallery/PackedImageGallery.php
new file mode 100644 (file)
index 0000000..9149f8c
--- /dev/null
@@ -0,0 +1,91 @@
+<?php
+/**
+ * Packed image gallery. All images adjusted to be same height.
+ *
+ * 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
+ */
+
+class PackedImageGallery extends TraditionalImageGallery {
+
+       /**
+        * We artificially have 1.5 the resolution neccessary so that
+        * we can scale it up by that much on the client side, without
+        * worrying about requesting a new image.
+        */
+       const SCALE_FACTOR = 1.5;
+
+       protected function getVPad( $boxHeight, $thumbHeight ) {
+               return ( $this->getThumbPadding() + $boxHeight - $thumbHeight/ self::SCALE_FACTOR ) / 2;
+       }
+
+       protected function getThumbPadding() {
+               return 0;
+       }
+
+       protected function getGBPadding() {
+               return 2;
+       }
+
+       /**
+        * @param File $img The file being transformed. May be false
+        */
+       protected function getThumbParams( $img ) {
+               if ( $img && $img->getMediaType() === MEDIATYPE_AUDIO ) {
+                       $width = $this->mWidths;
+               } else {
+                       // We want the width not to be the constraining
+                       // factor, so use random big number.
+                       $width = $this->mHeights * 10 + 100;
+               }
+               // self::SCALE_FACTOR so the js has some room to manipulate sizes.
+               return array(
+                       'width' => $width * self::SCALE_FACTOR,
+                       'height' => $this->mHeights * self::SCALE_FACTOR,
+               );
+       }
+
+       protected function getThumbDivWidth( $thumbWidth ) {
+               // Require at least 60px wide, so caption is wide enough to work.
+               if ( $thumbWidth < 60 * self::SCALE_FACTOR ) {
+                       $thumbWidth = 60 * self::SCALE_FACTOR;
+               }
+               return $thumbWidth / self::SCALE_FACTOR + $this->getThumbPadding();
+       }
+
+       /**
+        * @param MediaTransformOutput|bool $thumb the thumbnail, or false if no thumb (which can happen)
+        */
+       protected function getGBWidth( $thumb ) {
+               $thumbWidth = $thumb ? $thumb->getWidth() : $this->mWidths * self::SCALE_FACTOR;
+               return $this->getThumbDivWidth( $thumbWidth ) + $this->getGBPadding();
+       }
+
+       protected function adjustImageParameters( $thumb, &$imageParameters ) {
+               // Re-adjust back to normal size.
+               $imageParameters['override-width'] = ceil( $thumb->getWidth() / self::SCALE_FACTOR );
+               $imageParameters['override-height'] = ceil( $thumb->getHeight() / self::SCALE_FACTOR );
+       }
+
+       /**
+        * Add javascript which auto-justifies the rows by manipulating the image sizes.
+        * Also ensures that the hover version of this degrades gracefully.
+        */
+       protected function getModules() {
+               return array( 'mediawiki.page.gallery' );
+       }
+}
diff --git a/includes/gallery/PackedOverlayImageGallery.php b/includes/gallery/PackedOverlayImageGallery.php
new file mode 100644 (file)
index 0000000..bba06fc
--- /dev/null
@@ -0,0 +1,60 @@
+<?php
+/**
+ * Packed overlay image gallery. All images adjusted to be same height and
+ * image caption being placed over top of image.
+ *
+ * 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
+ */
+
+class PackedOverlayImageGallery extends PackedImageGallery {
+
+       /**
+        * Add the wrapper html around the thumb's caption
+        *
+        * @param String $galleryText The caption
+        * @param MediaTransformOutput|boolean $thumb The thumb this caption is for or false for bad image.
+        */
+       protected function wrapGalleryText( $galleryText, $thumb ) {
+
+               // If we have no text, do not output anything to avoid
+               // ugly white overlay.
+               if ( trim( $galleryText ) === '' ) {
+                       return '';
+               }
+
+               # ATTENTION: The newline after <div class="gallerytext"> is needed to accommodate htmltidy which
+               # in version 4.8.6 generated crackpot html in its absence, see:
+               # http://bugzilla.wikimedia.org/show_bug.cgi?id=1765 -Ævar
+
+               $thumbWidth = $this->getGBWidth( $thumb ) - $this->getThumbPadding() - $this->getGBPadding();
+               $captionWidth = ceil( $thumbWidth - 20 );
+
+               $outerWrapper = '<div class="gallerytextwrapper" style="width: ' . $captionWidth . 'px">';
+               return "\n\t\t\t" . $outerWrapper . '<div class="gallerytext">' . "\n"
+                                       . $galleryText
+                                       . "\n\t\t\t</div>";
+       }
+}
+
+/**
+ * Same as Packed except different CSS is applied to make the
+ * caption only show up on hover. If a touch screen is detected,
+ * falls back to PackedHoverGallery. Degrades gracefully for
+ * screen readers.
+ */
+class PackedHoverImageGallery extends PackedOverlayImageGallery { }
diff --git a/includes/gallery/TraditionalImageGallery.php b/includes/gallery/TraditionalImageGallery.php
new file mode 100644 (file)
index 0000000..223fb07
--- /dev/null
@@ -0,0 +1,328 @@
+<?php
+/**
+ * Image gallery.
+ *
+ * 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
+ */
+
+class TraditionalImageGallery extends ImageGalleryBase {
+
+
+       /**
+        * Return a HTML representation of the image gallery
+        *
+        * For each image in the gallery, display
+        * - a thumbnail
+        * - the image name
+        * - the additional text provided when adding the image
+        * - the size of the image
+        *
+        * @return string
+        */
+       function toHTML() {
+               if ( $this->mPerRow > 0 ) {
+                       $maxwidth = $this->mPerRow * ( $this->mWidths + $this->getAllPadding() );
+                       $oldStyle = isset( $this->mAttribs['style'] ) ? $this->mAttribs['style'] : '';
+                       # _width is ignored by any sane browser. IE6 doesn't know max-width so it uses _width instead
+                       $this->mAttribs['style'] = "max-width: {$maxwidth}px;_width: {$maxwidth}px;" . $oldStyle;
+               }
+
+               $attribs = Sanitizer::mergeAttributes(
+                       array( 'class' => 'gallery mw-gallery-' . $this->mMode ), $this->mAttribs );
+
+               $modules = $this->getModules();
+
+               if ( $this->mParser ) {
+                       $this->mParser->getOutput()->addModules( $modules );
+               } else {
+                       $this->getOutput()->addModules( $modules );
+               }
+               $output = Xml::openElement( 'ul', $attribs );
+               if ( $this->mCaption ) {
+                       $output .= "\n\t<li class='gallerycaption'>{$this->mCaption}</li>";
+               }
+
+               $lang = $this->getRenderLang();
+               # Output each image...
+               foreach ( $this->mImages as $pair ) {
+                       $nt = $pair[0];
+                       $text = $pair[1]; # "text" means "caption" here
+                       $alt = $pair[2];
+                       $link = $pair[3];
+
+                       $descQuery = false;
+                       if ( $nt->getNamespace() === NS_FILE ) {
+                               # Get the file...
+                               if ( $this->mParser instanceof Parser ) {
+                                       # Give extensions a chance to select the file revision for us
+                                       $options = array();
+                                       wfRunHooks( 'BeforeParserFetchFileAndTitle',
+                                               array( $this->mParser, $nt, &$options, &$descQuery ) );
+                                       # Fetch and register the file (file title may be different via hooks)
+                                       list( $img, $nt ) = $this->mParser->fetchFileAndTitle( $nt, $options );
+                               } else {
+                                       $img = wfFindFile( $nt );
+                               }
+                       } else {
+                               $img = false;
+                       }
+
+                       $params = $this->getThumbParams( $img );
+                       // $pair[4] is per image handler options
+                       $transformOptions = $params + $pair[4];
+
+                       $thumb = false;
+
+                       if ( !$img ) {
+                               # We're dealing with a non-image, spit out the name and be done with it.
+                               $thumbhtml = "\n\t\t\t" . '<div class="thumb" style="height: ' . ( $this->getThumbPadding() + $this->mHeights ) . 'px;">'
+                                       . htmlspecialchars( $nt->getText() ) . '</div>';
+
+                               if ( $this->mParser instanceof Parser ) {
+                                       $this->mParser->addTrackingCategory( 'broken-file-category' );
+                               }
+                       } elseif ( $this->mHideBadImages && wfIsBadImage( $nt->getDBkey(), $this->getContextTitle() ) ) {
+                               # The image is blacklisted, just show it as a text link.
+                               $thumbhtml = "\n\t\t\t" . '<div class="thumb" style="height: ' . ( $this->getThumbPadding() + $this->mHeights ) . 'px;">' .
+                                       Linker::link(
+                                               $nt,
+                                               htmlspecialchars( $nt->getText() ),
+                                               array(),
+                                               array(),
+                                               array( 'known', 'noclasses' )
+                                       ) .
+                                       '</div>';
+                       } elseif ( !( $thumb = $img->transform( $transformOptions ) ) ) {
+                               # Error generating thumbnail.
+                               $thumbhtml = "\n\t\t\t" . '<div class="thumb" style="height: ' . ( $this->getThumbPadding() + $this->mHeights ) . 'px;">'
+                                       . htmlspecialchars( $img->getLastError() ) . '</div>';
+                       } else {
+                               $vpad = $this->getVPad( $this->mHeights, $thumb->getHeight() );
+
+                               $imageParameters = array(
+                                       'desc-link' => true,
+                                       'desc-query' => $descQuery,
+                                       'alt' => $alt,
+                                       'custom-url-link' => $link
+                               );
+                               # In the absence of both alt text and caption, fall back on providing screen readers with the filename as alt text
+                               if ( $alt == '' && $text == '' ) {
+                                       $imageParameters['alt'] = $nt->getText();
+                               }
+
+                               $this->adjustImageParameters( $thumb, $imageParameters );
+
+                               # Set both fixed width and min-height.
+                               $thumbhtml = "\n\t\t\t" .
+                                       '<div class="thumb" style="width: ' . $this->getThumbDivWidth( $thumb->getWidth() ) . 'px;">'
+                                       # Auto-margin centering for block-level elements. Needed now that we have video
+                                       # handlers since they may emit block-level elements as opposed to simple <img> tags.
+                                       # ref http://css-discuss.incutio.com/?page=CenteringBlockElement
+                                       . '<div style="margin:' . $vpad . 'px auto;">'
+                                       . $thumb->toHtml( $imageParameters ) . '</div></div>';
+
+                               // Call parser transform hook
+                               if ( $this->mParser && $img->getHandler() ) {
+                                       $img->getHandler()->parserTransformHook( $this->mParser, $img );
+                               }
+                       }
+
+                       //TODO
+                       // $linkTarget = Title::newFromText( $wgContLang->getNsText( MWNamespace::getUser() ) . ":{$ut}" );
+                       // $ul = Linker::link( $linkTarget, $ut );
+
+                       if ( $this->mShowBytes ) {
+                               if ( $img ) {
+                                       $fileSize = htmlspecialchars( $lang->formatSize( $img->getSize() ) );
+                               } else {
+                                       $fileSize = $this->msg( 'filemissing' )->escaped();
+                               }
+                               $fileSize = "$fileSize<br />\n";
+                       } else {
+                               $fileSize = '';
+                       }
+
+                       $textlink = $this->mShowFilename ?
+                               Linker::link(
+                                       $nt,
+                                       htmlspecialchars( $lang->truncate( $nt->getText(), $this->mCaptionLength ) ),
+                                       array(),
+                                       array(),
+                                       array( 'known', 'noclasses' )
+                               ) . "<br />\n" :
+                               '';
+
+
+                       $galleryText  = $textlink . $text . $fileSize;
+                       $galleryText = $this->wrapGalleryText( $galleryText, $thumb );
+
+                       # Weird double wrapping (the extra div inside the li) needed due to FF2 bug
+                       # Can be safely removed if FF2 falls completely out of existence
+                       $output .=
+                               "\n\t\t" . '<li class="gallerybox" style="width: ' . $this->getGBWidth( $thumb ) . 'px">'
+                                       . '<div style="width: ' . $this->getGBWidth( $thumb ) . 'px">'
+                                       . $thumbhtml
+                                       . $galleryText
+                                       . "\n\t\t</div></li>";
+               }
+               $output .= "\n</ul>";
+
+               return $output;
+       }
+
+
+       /**
+        * Add the wrapper html around the thumb's caption
+        *
+        * @param String $galleryText The caption
+        * @param MediaTransformOutput|boolean $thumb The thumb this caption is for or false for bad image.
+        */
+       protected function wrapGalleryText( $galleryText, $thumb ) {
+               # ATTENTION: The newline after <div class="gallerytext"> is needed to accommodate htmltidy which
+               # in version 4.8.6 generated crackpot html in its absence, see:
+               # http://bugzilla.wikimedia.org/show_bug.cgi?id=1765 -Ævar
+
+               return "\n\t\t\t" . '<div class="gallerytext">' . "\n"
+                                       . $galleryText
+                                       . "\n\t\t\t</div>";
+       }
+
+       /**
+        * How much padding such the thumb have between image and inner div that
+        * that contains the border. This is both for verical and horizontal
+        * padding. (However, it is cut in half in the vertical direction).
+        * @return int
+        */
+       protected function getThumbPadding() {
+               return 30;
+       }
+
+       /**
+        *
+        * @note GB stands for gallerybox (as in the <li class="gallerybox"> element)
+        *
+        * @return int
+        */
+       protected function getGBPadding() {
+               return 5;
+       }
+
+       /**
+        * Get how much extra space the borders around the image takes up.
+        *
+        * For this mode, it is 2px borders on each side + 2px implied padding on
+        * each side from the stylesheet, giving us 2*2+2*2 = 8.
+        * @return int
+        */
+       protected function getGBBorders() {
+               return 8;
+       }
+
+       /**
+        * Get total padding.
+        *
+        * @return int How many pixels of whitespace surround the thumbnail.
+        */
+       protected function getAllPadding() {
+               return $this->getThumbPadding() + $this->getGBPadding() + $this->getGBBorders();
+       }
+
+       /**
+        * Get vertical padding for a thumbnail
+        *
+        * Generally this is the total height minus how high the thumb is.
+        *
+        * @param int $boxHeight How high we want the box to be.
+        * @param int $thumbHeight How high the thumbnail is.
+        * @return int How many vertical padding to add on each side.
+        */
+       protected function getVPad( $boxHeight, $thumbHeight ) {
+               return ( $this->getThumbPadding() + $boxHeight - $thumbHeight ) / 2;
+       }
+
+       /**
+        * Get the transform parameters for a thumbnail.
+        *
+        * @param File $img The file in question. May be false for invalid image
+        */
+       protected function getThumbParams( $img ) {
+               return array(
+                       'width' => $this->mWidths,
+                       'height' => $this->mHeights
+               );
+       }
+
+       /**
+        * Get the width of the inner div that contains the thumbnail in
+        * question. This is the div with the class of "thumb".
+        *
+        * @param int $thumbWidth The width of the thumbnail.
+        * @return int Width of inner thumb div.
+        */
+       protected function getThumbDivWidth( $thumbWidth ) {
+               return $this->mWidths + $this->getThumbPadding();
+       }
+
+       /**
+        * Width of gallerybox <li>.
+        *
+        * Generally is the width of the image, plus padding on image
+        * plus padding on gallerybox.
+        *
+        * @note Important: parameter will be false if no thumb used.
+        * @param Mixed $thumb MediaTransformObject object or false.
+        * @return int width of gallerybox element
+        */
+       protected function getGBWidth( $thumb ) {
+               return $this->mWidths + $this->getThumbPadding() + $this->getGBPadding();
+       }
+
+       /**
+        * Get a list of modules to include in the page.
+        *
+        * Primarily intended for subclasses.
+        *
+        * @return Array modules to include
+        */
+       protected function getModules() {
+               return array();
+       }
+
+       /**
+        * Adjust the image parameters for a thumbnail.
+        *
+        * Used by a subclass to insert extra high resolution images.
+        * @param MediaTransformOutput $thumb The thumbnail
+        * @param Array $imageParameters Array of options
+        */
+       protected function adjustImageParameters( $thumb, &$imageParameters ) { }
+}
+
+/**
+ * Backwards compatibility. This always uses traditional mode
+ * if called the old way, for extensions that may expect traditional
+ * mode.
+ *
+ * @deprecated 1.22 Use ImageGalleryBase::factory instead.
+ */
+class ImageGallery extends TraditionalImageGallery {
+       function __construct( $mode = 'traditional' ) {
+               wfDeprecated( __METHOD__, '1.22' );
+               parent::__construct( $mode );
+       }
+}
index 8632399..fde38bb 100644 (file)
@@ -298,6 +298,8 @@ class ThumbnailImage extends MediaTransformOutput {
         *     valign       vertical-align property, if the output is an inline element
         *     img-class    Class applied to the \<img\> tag, if there is such a tag
         *     desc-query   String, description link query params
+        *     override-width     Override width attribute. Should generally not set
+        *     override-height    Override height attribute. Should generally not set
         *     custom-url-link    Custom URL to link to
         *     custom-title-link  Custom Title object to link to
         *     custom target-link Value of the target attribute, for custom-target-link
@@ -359,6 +361,12 @@ class ThumbnailImage extends MediaTransformOutput {
                if ( !empty( $options['img-class'] ) ) {
                        $attribs['class'] = $options['img-class'];
                }
+               if ( isset( $options['override-height'] ) ) {
+                       $attribs['height'] = $options['override-height'];
+               }
+               if ( isset( $options['override-width'] ) ) {
+                       $attribs['width'] = $options['override-width'];
+               }
 
                // Additional densities for responsive images, if specified.
                if ( !empty( $this->responsiveUrls ) ) {
index 8fdf407..2b6363e 100644 (file)
@@ -5047,7 +5047,19 @@ class Parser {
         */
        function renderImageGallery( $text, $params ) {
                wfProfileIn( __METHOD__ );
-               $ig = new ImageGallery();
+
+               $mode = false;
+               if ( isset( $params['mode'] ) ) {
+                       $mode = $params['mode'];
+               }
+
+               try {
+                       $ig = ImageGalleryBase::factory( $mode );
+               } catch ( MWException $e ) {
+                       // If invalid type set, fallback to default.
+                       $ig = ImageGalleryBase::factory( false );
+               }
+
                $ig->setContextTitle( $this->mTitle );
                $ig->setShowBytes( false );
                $ig->setShowFilename( false );
@@ -5075,6 +5087,7 @@ class Parser {
                if ( isset( $params['heights'] ) ) {
                        $ig->setHeights( $params['heights'] );
                }
+               $ig->setAdditionalOptions( $params );
 
                wfRunHooks( 'BeforeParserrenderImageGallery', array( &$this, &$ig ) );
 
index 8e92e4a..cf5a01e 100644 (file)
@@ -117,7 +117,8 @@ class NewFilesPager extends ReverseChronologicalPager {
 
        function getStartBody() {
                if ( !$this->gallery ) {
-                       $this->gallery = new ImageGallery();
+                       $this->gallery = ImageGalleryBase::factory();
+                       $this->gallery->setContext( $this->getContext );
                }
 
                return '';
index efde5cb..3a057ea 100644 (file)
@@ -682,7 +682,8 @@ class SpecialUpload extends SpecialPage {
                        return '';
                }
 
-               $gallery = new ImageGallery;
+               $gallery = ImageGalleryBase::factory();
+               $gallery->setContext( $this->getContext() );
                $gallery->setShowBytes( false );
                foreach ( $dupes as $file ) {
                        $gallery->add( $file->getTitle() );
index 6f0d3be..1b0e33c 100644 (file)
@@ -851,6 +851,9 @@ return array(
 
        /* MediaWiki Page */
 
+       'mediawiki.page.gallery' => array(
+               'scripts' => 'resources/mediawiki.page/mediawiki.page.gallery.js',
+       ),
        'mediawiki.page.ready' => array(
                'scripts' => 'resources/mediawiki.page/mediawiki.page.ready.js',
                'dependencies' => array(
diff --git a/resources/mediawiki.page/mediawiki.page.gallery.js b/resources/mediawiki.page/mediawiki.page.gallery.js
new file mode 100644 (file)
index 0000000..fd2af40
--- /dev/null
@@ -0,0 +1,216 @@
+/**
+ * Show gallery captions when focused. Copied directly from jquery.mw-jump.js.
+ * Also Dynamically resize images to justify them.
+ */
+( function ( $, mw ) {
+       $( function () {
+               var isTouchScreen,
+                       gettingFocus,
+                       galleries = 'ul.mw-gallery-packed-overlay, ul.mw-gallery-packed-hover, ul.mw-gallery-packed';
+
+               // Is there a better way to detect a touchscreen? Current check taken from stack overflow.
+               isTouchScreen = !!( window.ontouchstart !== undefined || window.DocumentTouch !== undefined && document instanceof window.DocumentTouch );
+
+               if ( isTouchScreen ) {
+                       // Always show the caption for a touch screen.
+                       $( 'ul.mw-gallery-packed-hover' )
+                               .addClass( 'mw-gallery-packed-overlay' )
+                               .removeClass( 'mw-gallery-packed-hover' );
+               } else {
+                       // Note use of just "a", not a.image, since we want this to trigger if a link in
+                       // the caption receives focus
+                       $( 'ul.mw-gallery-packed-hover li.gallerybox' ).on( 'focus blur', 'a', function ( e ) {
+                               // Confusingly jQuery leaves e.type as focusout for delegated blur events
+                               gettingFocus = e.type !== 'blur' && e.type !== 'focusout';
+                               $( this ).closest( 'li.gallerybox' ).toggleClass( 'mw-gallery-focused', gettingFocus );
+                       } );
+               }
+
+               // Now on to justification.
+               // We may still get ragged edges if someone resizes their window. Could bind to
+               // that event, otoh do we really want to constantly be resizing galleries?
+               $( galleries ).each( function() {
+                       var lastTop,
+                               $img,
+                               imgWidth,
+                               imgHeight,
+                               rows = [],
+                               $gallery = $( this );
+
+                       $gallery.children( 'li' ).each( function() {
+                               // Math.floor to be paranoid if things are off by 0.00000000001
+                               var top = Math.floor( $(this ).position().top ),
+                                       $this = $( this );
+
+                               if ( top !== lastTop ) {
+                                       rows[rows.length] = [];
+                                       lastTop = top;
+                               }
+
+                               $img = $this.find( 'div.thumb a.image img' );
+                               if ( $img.length && $img[0].height ) {
+                                       imgHeight = $img[0].height;
+                                       imgWidth = $img[0].width;
+                               } else {
+                                       // If we don't have a real image, get the containing divs width/height.
+                                       // Note that if we do have a real image, using this method will generally
+                                       // give the same answer, but can be different in the case of a very
+                                       // narrow image where extra padding is added.
+                                       imgHeight = $this.children().children( 'div:first' ).height();
+                                       imgWidth = $this.children().children( 'div:first' ).width();
+                               }
+
+                               // Hack to make an edge case work ok
+                               if ( imgHeight < 30 ) {
+                                       // Don't try and resize this item.
+                                       imgHeight = 0;
+                               }
+
+                               rows[rows.length-1][rows[rows.length-1].length] = {
+                                       $elm: $this,
+                                       width: $this.outerWidth(),
+                                       imgWidth: imgWidth,
+                                       aspect: imgWidth / imgHeight, // XXX: can divide by 0 ever happen?
+                                       captionWidth: $this.children().children( 'div.gallerytextwrapper' ).width(),
+                                       height: imgHeight
+                               };
+                       });
+
+                       (function () {
+                               var maxWidth,
+                                       combinedAspect,
+                                       combinedPadding,
+                                       curRow,
+                                       curRowHeight,
+                                       wantedWidth,
+                                       preferredHeight,
+                                       newWidth,
+                                       padding,
+                                       $outerDiv,
+                                       $innerDiv,
+                                       $imageDiv,
+                                       $imageElm,
+                                       imageElm,
+                                       $caption,
+                                       hookInfo,
+                                       i,
+                                       j;
+
+                               for ( i = 0; i < rows.length; i++ ) {
+                                       maxWidth = $gallery.width();
+                                       combinedAspect = 0;
+                                       combinedPadding = 0;
+                                       curRow = rows[i];
+                                       curRowHeight = 0;
+
+                                       for ( j = 0; j < curRow.length; j++ ) {
+                                               if ( curRowHeight === 0 ) {
+                                                       if ( isFinite( curRow[j].height ) ) {
+                                                               // Get the height of this row, by taking the first
+                                                               // non-out of bounds height
+                                                               curRowHeight = curRow[j].height;
+                                                       }
+                                               }
+
+                                               if ( curRow[j].aspect === 0 || !isFinite( curRow[j].aspect ) ) {
+                                                       mw.log( 'Skipping item ' + j + ' due to aspect: ' + curRow[j].aspect );
+                                                       // One of the dimensions are 0. Probably should
+                                                       // not try to resize.
+                                                       combinedPadding += curRow[j].width;
+                                               } else {
+                                                       combinedAspect += curRow[j].aspect;
+                                                       combinedPadding += curRow[j].width - curRow[j].imgWidth;
+                                               }
+                                       }
+
+                                       // Add some padding for inter-element spacing.
+                                       combinedPadding += 5 * curRow.length;
+                                       wantedWidth = maxWidth - combinedPadding;
+                                       preferredHeight = wantedWidth / combinedAspect;
+
+                                       if ( preferredHeight > curRowHeight * 1.5 ) {
+                                               // Only expand at most 1.5 times current size
+                                               // As that's as high a resolution as we have.
+                                               // Also on the off chance there is a bug in this
+                                               // code, would prevent accidentally expanding to
+                                               // be 10 billion pixels wide.
+                                               mw.log( 'mw.page.gallery: Cannot fit row, aspect is ' + preferredHeight/curRowHeight );
+                                               preferredHeight = 1.5 * curRowHeight;
+                                       }
+                                       if ( !isFinite( preferredHeight ) ) {
+                                               // This *definitely* should not happen.
+                                               mw.log( 'mw.page.gallery: Trying to resize row ' + i + ' to ' + preferredHeight + '?!' );
+                                               // Skip this row.
+                                               continue;
+                                       }
+                                       if ( preferredHeight < 5 ) {
+                                               // Well something clearly went wrong...
+                                               mw.log( {maxWidth: maxWidth, combinedPadding: combinedPadding, combinedAspect: combinedAspect, wantedWidth: wantedWidth } );
+                                               mw.log( 'mw.page.gallery: [BUG!] Fitting row ' + i + ' to too small a size: ' + preferredHeight );
+                                               // Skip this row.
+                                               continue;
+                                       }
+                                       for ( j = 0; j < curRow.length; j++ ) {
+                                               newWidth = preferredHeight * curRow[j].aspect;
+                                               padding = curRow[j].width - curRow[j].imgWidth;
+                                               $outerDiv = curRow[j].$elm;
+                                               $innerDiv = $outerDiv.children( 'div' ).first();
+                                               $imageDiv = $innerDiv.children( 'div.thumb' );
+                                               $imageElm = $imageDiv.find( 'img' ).first();
+                                               imageElm = $imageElm.length ? $imageElm[0] : null;
+                                               $caption = $outerDiv.find( 'div.gallerytextwrapper' );
+
+
+                                               // Since we are going to re-adjust the height, the vertical
+                                               // centering margins need to be reset.
+                                               $imageDiv.children( 'div' ).css( 'margin', '0px auto' );
+
+                                               if ( newWidth < 60 || !isFinite( newWidth ) ) {
+                                                       // Making something skinnier than this will mess up captions,
+                                                       mw.log( 'mw.page.gallery: Tried to make image ' + newWidth + 'px wide but too narrow.' );
+                                                       if ( newWidth < 1 || !isFinite( newWidth ) ) {
+                                                               $innerDiv.height( preferredHeight );
+                                                               // Don't even try and touch the image size if it could mean
+                                                               // making it disappear.
+                                                               continue;
+                                                       }
+                                               } else {
+                                                       $outerDiv.width( newWidth + padding );
+                                                       $innerDiv.width( newWidth + padding );
+                                                       $imageDiv.width( newWidth );
+                                                       $caption.width( curRow[j].captionWidth + (newWidth - curRow[j].imgWidth ) );
+                                               }
+
+                                               hookInfo = {
+                                                       fullWidth: newWidth + padding,
+                                                       imgWidth: newWidth,
+                                                       imgHeight: preferredHeight,
+                                                       $innerDiv: $innerDiv,
+                                                       $imageDiv: $imageDiv,
+                                                       $outerDiv: $outerDiv,
+                                                       resolved: false  /* Did the hook take action */
+                                               };
+                                               // Allow other media handlers to hook in.
+                                               // If your hook resizes an image, it is expected it will
+                                               // set resolved to true. Additionally you should load
+                                               // your module in position top to ensure it is registered
+                                               // before this runs (FIXME: there must be a better way?)
+                                               // See TimedMediaHandler for an example.
+                                               mw.hook( 'mediawiki.page.gallery.resize' ).fire( hookInfo );
+
+                                               if ( !hookInfo.resolved ) {
+                                                       if ( imageElm ) {
+                                                               // We don't always have an img, e.g. in the case of an invalid file.
+                                                               imageElm.width = newWidth;
+                                                               imageElm.height = preferredHeight;
+                                                       } else {
+                                                               // Not a file box.
+                                                               $imageDiv.height( preferredHeight );
+                                                       }
+                                               }
+                                       }
+                               }
+                       } )();
+               } );
+       } );
+} )( jQuery, mediaWiki );
index 4c68401..a9b9d2b 100644 (file)
@@ -832,6 +832,69 @@ div.gallerytext {
        word-wrap: break-word;
 }
 
+/* new gallery stuff */
+ul.mw-gallery-nolines li.gallerybox div.thumb {
+       background-color: transparent;
+       border: none;
+}
+ul.mw-gallery-nolines li.gallerybox div.thumb img {
+       margin: 0;
+}
+
+
+/* height constrained gallery */
+
+ul.mw-gallery-packed li.gallerybox div.thumb,
+ul.mw-gallery-packed-overlay li.gallerybox div.thumb,
+ul.mw-gallery-packed-hover li.gallerybox div.thumb {
+       background-color: transparent;
+       border: none;
+}
+ul.mw-gallery-packed li.gallerybox div.thumb img,
+ul.mw-gallery-packed-overlay li.gallerybox div.thumb img,
+ul.mw-gallery-packed-hover li.gallerybox div.thumb img {
+       margin: 0;
+}
+
+ul.mw-gallery-packed-hover li.gallerybox,
+ul.mw-gallery-packed-overlay li.gallerybox {
+       position:relative;
+}
+
+ul.mw-gallery-packed-hover div.gallerytextwrapper {
+       overflow: hidden;
+       height: 0;
+}
+
+ul.mw-gallery-packed-hover li.gallerybox:hover div.gallerytextwrapper,
+ul.mw-gallery-packed-overlay li.gallerybox div.gallerytextwrapper,
+ul.mw-gallery-packed-hover li.gallerybox.mw-gallery-focused div.gallerytextwrapper {
+       position:absolute;
+       opacity:.8;
+       filter:alpha(opacity=80);
+       zoom: 1;
+       background-color:white;
+       padding: 5px 10px;
+       bottom: 0;
+       left: 0; /* Needed for IE */
+       height: auto;
+       font-weight: bold;
+       margin: 2px; /* correspond to style on div.thumb */
+}
+
+ul.mw-gallery-packed-hover,
+ul.mw-gallery-packed-overlay,
+ul.mw-gallery-packed {
+       text-align: center;
+}
+
+ul.mw-gallery-packed-hover div.gallerytext,
+ul.mw-gallery-packed-overlay div.gallerytext {
+       opacity: 1;
+       position: relative; /* Resets opacity in old IE */
+}
+
+
 .mw-ajax-loader {
        /* @embed */
        background-image: url(images/ajax-loader.gif);
index 57c5e1b..91fabb7 100644 (file)
@@ -12766,40 +12766,40 @@ image4    |300px| centre
 * image6
 </gallery>
 !! result
-<ul class="gallery">
+<ul class="gallery mw-gallery-traditional">
                <li class="gallerybox" style="width: 155px"><div style="width: 155px">
-                       <div style="height: 150px;">Image1.png</div>
+                       <div class="thumb" style="height: 150px;">Image1.png</div>
                        <div class="gallerytext">
                        </div>
                </div></li>
                <li class="gallerybox" style="width: 155px"><div style="width: 155px">
-                       <div style="height: 150px;">Image2.gif</div>
+                       <div class="thumb" style="height: 150px;">Image2.gif</div>
                        <div class="gallerytext">
 <p>||||
 </p>
                        </div>
                </div></li>
                <li class="gallerybox" style="width: 155px"><div style="width: 155px">
-                       <div style="height: 150px;">Image3</div>
+                       <div class="thumb" style="height: 150px;">Image3</div>
                        <div class="gallerytext">
                        </div>
                </div></li>
                <li class="gallerybox" style="width: 155px"><div style="width: 155px">
-                       <div style="height: 150px;">Image4</div>
+                       <div class="thumb" style="height: 150px;">Image4</div>
                        <div class="gallerytext">
 <p>300px| centre
 </p>
                        </div>
                </div></li>
                <li class="gallerybox" style="width: 155px"><div style="width: 155px">
-                       <div style="height: 150px;">Image5.svg</div>
+                       <div class="thumb" style="height: 150px;">Image5.svg</div>
                        <div class="gallerytext">
 <p><a rel="nofollow" class="external free" href="http://///////">http://///////</a>
 </p>
                        </div>
                </div></li>
                <li class="gallerybox" style="width: 155px"><div style="width: 155px">
-                       <div style="height: 150px;">* image6</div>
+                       <div class="thumb" style="height: 150px;">* image6</div>
                        <div class="gallerytext">
                        </div>
                </div></li>
@@ -12818,17 +12818,17 @@ image:foobar.jpg
 image:foobar.jpg|Blabla|alt=This is a foo-bar.|blabla.
 </gallery>
 !! result
-<ul class="gallery" style="max-width: 226px;_width: 226px;">
+<ul class="gallery mw-gallery-traditional" style="max-width: 226px;_width: 226px;">
        <li class='gallerycaption'>Foo <a href="/wiki/Main_Page" title="Main Page">Main Page</a></li>
                <li class="gallerybox" style="width: 105px"><div style="width: 105px">
-                       <div style="height: 70px;">Nonexistant.jpg</div>
+                       <div class="thumb" style="height: 70px;">Nonexistant.jpg</div>
                        <div class="gallerytext">
 <p>caption
 </p>
                        </div>
                </div></li>
                <li class="gallerybox" style="width: 105px"><div style="width: 105px">
-                       <div style="height: 70px;">Nonexistant.jpg</div>
+                       <div class="thumb" style="height: 70px;">Nonexistant.jpg</div>
                        <div class="gallerytext">
                        </div>
                </div></li>
@@ -12863,7 +12863,7 @@ File:foobar.jpg|[[File:foobar.jpg|20px|desc|alt=inneralt]]|alt=galleryalt
 File:foobar.jpg|{{Test|unamedParam|alt=param}}|alt=galleryalt
 </gallery>
 !! result
-<ul class="gallery">
+<ul class="gallery mw-gallery-traditional">
                <li class="gallerybox" style="width: 155px"><div style="width: 155px">
                        <div class="thumb" style="width: 150px;"><div style="margin:68px auto;"><a href="/wiki/File:Foobar.jpg" class="image"><img alt="galleryalt" src="http://example.com/images/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg" width="120" height="14" /></a></div></div>
                        <div class="gallerytext">
@@ -12892,9 +12892,9 @@ image:foobar.jpg|some '''caption''' [[Main Page]]
 File:Foobar.jpg
 </gallery>
 !! result
-<ul class="gallery">
+<ul class="gallery mw-gallery-traditional">
                <li class="gallerybox" style="width: 155px"><div style="width: 155px">
-                       <div style="height: 150px;">Nonexistant.jpg</div>
+                       <div class="thumb" style="height: 150px;">Nonexistant.jpg</div>
                        <div class="gallerytext">
 <p><a href="/wiki/File:Nonexistant.jpg" title="File:Nonexistant.jpg">Nonexistant.jpg</a><br />
 caption
@@ -12902,7 +12902,7 @@ caption
                        </div>
                </div></li>
                <li class="gallerybox" style="width: 155px"><div style="width: 155px">
-                       <div style="height: 150px;">Nonexistant.jpg</div>
+                       <div class="thumb" style="height: 150px;">Nonexistant.jpg</div>
                        <div class="gallerytext">
 <p><a href="/wiki/File:Nonexistant.jpg" title="File:Nonexistant.jpg">Nonexistant.jpg</a><br />
 </p>
@@ -12937,14 +12937,14 @@ image:foobar.jpg
 foobar.jpg
 </gallery>
 !! result
-<ul class="gallery">
+<ul class="gallery mw-gallery-traditional">
                <li class="gallerybox" style="width: 155px"><div style="width: 155px">
-                       <div style="height: 150px;">Nonexistant.jpg</div>
+                       <div class="thumb" style="height: 150px;">Nonexistant.jpg</div>
                        <div class="gallerytext">
                        </div>
                </div></li>
                <li class="gallerybox" style="width: 155px"><div style="width: 155px">
-                       <div style="height: 150px;">Nonexistant.jpg</div>
+                       <div class="thumb" style="height: 150px;">Nonexistant.jpg</div>
                        <div class="gallerytext">
                        </div>
                </div></li>
@@ -15256,7 +15256,7 @@ Gallery override link with WikiLink (bug 34852)
 File:foobar.jpg|caption|alt=galleryalt|link=InterWikiLink
 </gallery>
 !! result
-<ul class="gallery">
+<ul class="gallery mw-gallery-traditional">
                <li class="gallerybox" style="width: 155px"><div style="width: 155px">
                        <div class="thumb" style="width: 150px;"><div style="margin:68px auto;"><a href="/wiki/InterWikiLink"><img alt="galleryalt" src="http://example.com/images/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg" width="120" height="14" /></a></div></div>
                        <div class="gallerytext">
@@ -15275,7 +15275,7 @@ Gallery override link with absolute external link (bug 34852)
 File:foobar.jpg|caption|alt=galleryalt|link=http://www.example.org
 </gallery>
 !! result
-<ul class="gallery">
+<ul class="gallery mw-gallery-traditional">
                <li class="gallerybox" style="width: 155px"><div style="width: 155px">
                        <div class="thumb" style="width: 150px;"><div style="margin:68px auto;"><a href="http://www.example.org"><img alt="galleryalt" src="http://example.com/images/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg" width="120" height="14" /></a></div></div>
                        <div class="gallerytext">
@@ -15294,7 +15294,7 @@ Gallery override link with malicious javascript (bug 34852)
 File:foobar.jpg|caption|alt=galleryalt|link=" onclick="alert('malicious javascript code!');
 </gallery>
 !! result
-<ul class="gallery">
+<ul class="gallery mw-gallery-traditional">
                <li class="gallerybox" style="width: 155px"><div style="width: 155px">
                        <div class="thumb" style="width: 150px;"><div style="margin:68px auto;"><a href="/wiki/%22_onclick%3D%22alert(%27malicious_javascript_code!%27);"><img alt="galleryalt" src="http://example.com/images/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg" width="120" height="14" /></a></div></div>
                        <div class="gallerytext">
@@ -15313,7 +15313,7 @@ Gallery with invalid title as link (bug 43964)
 File:foobar.jpg|link=<
 </gallery>
 !! result
-<ul class="gallery">
+<ul class="gallery mw-gallery-traditional">
                <li class="gallerybox" style="width: 155px"><div style="width: 155px">
                        <div class="thumb" style="width: 150px;"><div style="margin:68px auto;"><a href="/wiki/File:Foobar.jpg" class="image"><img alt="Foobar.jpg" src="http://example.com/images/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg" width="120" height="14" /></a></div></div>
                        <div class="gallerytext">