Merge "API: Add show=unread to ApiQueryWatchlist"
[mediawiki.git] / resources / src / mediawiki.page / mediawiki.page.gallery.js
blobe7c962fe94c6285eb93407626b4162e5ab4221f8
1 /*!
2  * Show gallery captions when focused. Copied directly from jquery.mw-jump.js.
3  * Also Dynamically resize images to justify them.
4  */
5 ( function ( $, mw ) {
6         $( function () {
7                 var isTouchScreen,
8                         gettingFocus,
9                         galleries = 'ul.mw-gallery-packed-overlay, ul.mw-gallery-packed-hover, ul.mw-gallery-packed';
11                 // Is there a better way to detect a touchscreen? Current check taken from stack overflow.
12                 isTouchScreen = !!( window.ontouchstart !== undefined || window.DocumentTouch !== undefined && document instanceof window.DocumentTouch );
14                 if ( isTouchScreen ) {
15                         // Always show the caption for a touch screen.
16                         $( 'ul.mw-gallery-packed-hover' )
17                                 .addClass( 'mw-gallery-packed-overlay' )
18                                 .removeClass( 'mw-gallery-packed-hover' );
19                 } else {
20                         // Note use of just "a", not a.image, since we want this to trigger if a link in
21                         // the caption receives focus
22                         $( 'ul.mw-gallery-packed-hover li.gallerybox' ).on( 'focus blur', 'a', function ( e ) {
23                                 // Confusingly jQuery leaves e.type as focusout for delegated blur events
24                                 gettingFocus = e.type !== 'blur' && e.type !== 'focusout';
25                                 $( this ).closest( 'li.gallerybox' ).toggleClass( 'mw-gallery-focused', gettingFocus );
26                         } );
27                 }
29                 // Now on to justification.
30                 // We may still get ragged edges if someone resizes their window. Could bind to
31                 // that event, otoh do we really want to constantly be resizing galleries?
32                 $( galleries ).each( function () {
33                         var lastTop,
34                                 $img,
35                                 imgWidth,
36                                 imgHeight,
37                                 rows = [],
38                                 $gallery = $( this );
40                         $gallery.children( 'li' ).each( function () {
41                                 // Math.floor to be paranoid if things are off by 0.00000000001
42                                 var top = Math.floor( $( this ).position().top ),
43                                         $this = $( this );
45                                 if ( top !== lastTop ) {
46                                         rows[rows.length] = [];
47                                         lastTop = top;
48                                 }
50                                 $img = $this.find( 'div.thumb a.image img' );
51                                 if ( $img.length && $img[0].height ) {
52                                         imgHeight = $img[0].height;
53                                         imgWidth = $img[0].width;
54                                 } else {
55                                         // If we don't have a real image, get the containing divs width/height.
56                                         // Note that if we do have a real image, using this method will generally
57                                         // give the same answer, but can be different in the case of a very
58                                         // narrow image where extra padding is added.
59                                         imgHeight = $this.children().children( 'div:first' ).height();
60                                         imgWidth = $this.children().children( 'div:first' ).width();
61                                 }
63                                 // Hack to make an edge case work ok
64                                 if ( imgHeight < 30 ) {
65                                         // Don't try and resize this item.
66                                         imgHeight = 0;
67                                 }
69                                 rows[rows.length - 1][rows[rows.length - 1].length] = {
70                                         $elm: $this,
71                                         width: $this.outerWidth(),
72                                         imgWidth: imgWidth,
73                                         // XXX: can divide by 0 ever happen?
74                                         aspect: imgWidth / imgHeight,
75                                         captionWidth: $this.children().children( 'div.gallerytextwrapper' ).width(),
76                                         height: imgHeight
77                                 };
78                         } );
80                         ( function () {
81                                 var maxWidth,
82                                         combinedAspect,
83                                         combinedPadding,
84                                         curRow,
85                                         curRowHeight,
86                                         wantedWidth,
87                                         preferredHeight,
88                                         newWidth,
89                                         padding,
90                                         $outerDiv,
91                                         $innerDiv,
92                                         $imageDiv,
93                                         $imageElm,
94                                         imageElm,
95                                         $caption,
96                                         hookInfo,
97                                         i,
98                                         j,
99                                         avgZoom,
100                                         totalZoom = 0;
102                                 for ( i = 0; i < rows.length; i++ ) {
103                                         maxWidth = $gallery.width();
104                                         combinedAspect = 0;
105                                         combinedPadding = 0;
106                                         curRow = rows[i];
107                                         curRowHeight = 0;
109                                         for ( j = 0; j < curRow.length; j++ ) {
110                                                 if ( curRowHeight === 0 ) {
111                                                         if ( isFinite( curRow[j].height ) ) {
112                                                                 // Get the height of this row, by taking the first
113                                                                 // non-out of bounds height
114                                                                 curRowHeight = curRow[j].height;
115                                                         }
116                                                 }
118                                                 if ( curRow[j].aspect === 0 || !isFinite( curRow[j].aspect ) ) {
119                                                         mw.log( 'Skipping item ' + j + ' due to aspect: ' + curRow[j].aspect );
120                                                         // One of the dimensions are 0. Probably should
121                                                         // not try to resize.
122                                                         combinedPadding += curRow[j].width;
123                                                 } else {
124                                                         combinedAspect += curRow[j].aspect;
125                                                         combinedPadding += curRow[j].width - curRow[j].imgWidth;
126                                                 }
127                                         }
129                                         // Add some padding for inter-element spacing.
130                                         combinedPadding += 5 * curRow.length;
131                                         wantedWidth = maxWidth - combinedPadding;
132                                         preferredHeight = wantedWidth / combinedAspect;
134                                         if ( preferredHeight > curRowHeight * 1.5 ) {
135                                                 // Only expand at most 1.5 times current size
136                                                 // As that's as high a resolution as we have.
137                                                 // Also on the off chance there is a bug in this
138                                                 // code, would prevent accidentally expanding to
139                                                 // be 10 billion pixels wide.
140                                                 mw.log( 'mw.page.gallery: Cannot fit row, aspect is ' + preferredHeight / curRowHeight );
141                                                 if ( i === rows.length - 1 ) {
142                                                         // If its the last row, and we can't fit it,
143                                                         // don't make the entire row huge.
144                                                         avgZoom = ( totalZoom / ( rows.length - 1 ) ) * curRowHeight;
145                                                         if ( isFinite( avgZoom ) && avgZoom >= 1 && avgZoom <= 1.5 ) {
146                                                                 preferredHeight = avgZoom;
147                                                         } else {
148                                                                 // Probably a single row gallery
149                                                                 preferredHeight = curRowHeight;
150                                                         }
151                                                 } else {
152                                                         preferredHeight = 1.5 * curRowHeight;
153                                                 }
154                                         }
155                                         if ( !isFinite( preferredHeight ) ) {
156                                                 // This *definitely* should not happen.
157                                                 mw.log( 'mw.page.gallery: Trying to resize row ' + i + ' to ' + preferredHeight + '?!' );
158                                                 // Skip this row.
159                                                 continue;
160                                         }
161                                         if ( preferredHeight < 5 ) {
162                                                 // Well something clearly went wrong...
163                                                 mw.log( {
164                                                         maxWidth: maxWidth,
165                                                         combinedPadding: combinedPadding,
166                                                         combinedAspect: combinedAspect,
167                                                         wantedWidth: wantedWidth
168                                                 } );
169                                                 mw.log( 'mw.page.gallery: [BUG!] Fitting row ' + i + ' to too small a size: ' + preferredHeight );
170                                                 // Skip this row.
171                                                 continue;
172                                         }
174                                         if ( preferredHeight / curRowHeight > 1 ) {
175                                                 totalZoom += preferredHeight / curRowHeight;
176                                         } else {
177                                                 // If we shrink, still consider that a zoom of 1
178                                                 totalZoom += 1;
179                                         }
181                                         for ( j = 0; j < curRow.length; j++ ) {
182                                                 newWidth = preferredHeight * curRow[j].aspect;
183                                                 padding = curRow[j].width - curRow[j].imgWidth;
184                                                 $outerDiv = curRow[j].$elm;
185                                                 $innerDiv = $outerDiv.children( 'div' ).first();
186                                                 $imageDiv = $innerDiv.children( 'div.thumb' );
187                                                 $imageElm = $imageDiv.find( 'img' ).first();
188                                                 imageElm = $imageElm.length ? $imageElm[0] : null;
189                                                 $caption = $outerDiv.find( 'div.gallerytextwrapper' );
191                                                 // Since we are going to re-adjust the height, the vertical
192                                                 // centering margins need to be reset.
193                                                 $imageDiv.children( 'div' ).css( 'margin', '0px auto' );
195                                                 if ( newWidth < 60 || !isFinite( newWidth ) ) {
196                                                         // Making something skinnier than this will mess up captions,
197                                                         mw.log( 'mw.page.gallery: Tried to make image ' + newWidth + 'px wide but too narrow.' );
198                                                         if ( newWidth < 1 || !isFinite( newWidth ) ) {
199                                                                 $innerDiv.height( preferredHeight );
200                                                                 // Don't even try and touch the image size if it could mean
201                                                                 // making it disappear.
202                                                                 continue;
203                                                         }
204                                                 } else {
205                                                         $outerDiv.width( newWidth + padding );
206                                                         $innerDiv.width( newWidth + padding );
207                                                         $imageDiv.width( newWidth );
208                                                         $caption.width( curRow[j].captionWidth + ( newWidth - curRow[j].imgWidth ) );
209                                                 }
211                                                 hookInfo = {
212                                                         fullWidth: newWidth + padding,
213                                                         imgWidth: newWidth,
214                                                         imgHeight: preferredHeight,
215                                                         $innerDiv: $innerDiv,
216                                                         $imageDiv: $imageDiv,
217                                                         $outerDiv: $outerDiv,
218                                                         // Whether the hook took action
219                                                         resolved: false
220                                                 };
222                                                 /**
223                                                  * Gallery resize.
224                                                  *
225                                                  * If your handler resizes an image, it should also set the resolved
226                                                  * property to true. Additionally, because this module only exposes this
227                                                  * logic temporarily, you should load your module in position top to
228                                                  * ensure it is registered before this runs (FIXME: Don't use mw.hook)
229                                                  *
230                                                  * See TimedMediaHandler for an example.
231                                                  *
232                                                  * @event mediawiki_page_gallery_resize
233                                                  * @member mw.hook
234                                                  * @param {Object} hookInfo
235                                                  */
236                                                 mw.hook( 'mediawiki.page.gallery.resize' ).fire( hookInfo );
238                                                 if ( !hookInfo.resolved ) {
239                                                         if ( imageElm ) {
240                                                                 // We don't always have an img, e.g. in the case of an invalid file.
241                                                                 imageElm.width = newWidth;
242                                                                 imageElm.height = preferredHeight;
243                                                         } else {
244                                                                 // Not a file box.
245                                                                 $imageDiv.height( preferredHeight );
246                                                         }
247                                                 }
248                                         }
249                                 }
250                         }() );
251                 } );
252         } );
253 }( jQuery, mediaWiki ) );