Merge "Satisfy code analyzer by ensuring $urls is defined"
[mediawiki.git] / resources / mediawiki.page / mediawiki.page.gallery.js
blob147a869d872b1fca494d786c1404e63a2f8d15a1
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                                         aspect: imgWidth / imgHeight, // XXX: can divide by 0 ever happen?
74                                         captionWidth: $this.children().children( 'div.gallerytextwrapper' ).width(),
75                                         height: imgHeight
76                                 };
77                         });
79                         (function () {
80                                 var maxWidth,
81                                         combinedAspect,
82                                         combinedPadding,
83                                         curRow,
84                                         curRowHeight,
85                                         wantedWidth,
86                                         preferredHeight,
87                                         newWidth,
88                                         padding,
89                                         $outerDiv,
90                                         $innerDiv,
91                                         $imageDiv,
92                                         $imageElm,
93                                         imageElm,
94                                         $caption,
95                                         hookInfo,
96                                         i,
97                                         j,
98                                         avgZoom,
99                                         totalZoom = 0;
101                                 for ( i = 0; i < rows.length; i++ ) {
102                                         maxWidth = $gallery.width();
103                                         combinedAspect = 0;
104                                         combinedPadding = 0;
105                                         curRow = rows[i];
106                                         curRowHeight = 0;
108                                         for ( j = 0; j < curRow.length; j++ ) {
109                                                 if ( curRowHeight === 0 ) {
110                                                         if ( isFinite( curRow[j].height ) ) {
111                                                                 // Get the height of this row, by taking the first
112                                                                 // non-out of bounds height
113                                                                 curRowHeight = curRow[j].height;
114                                                         }
115                                                 }
117                                                 if ( curRow[j].aspect === 0 || !isFinite( curRow[j].aspect ) ) {
118                                                         mw.log( 'Skipping item ' + j + ' due to aspect: ' + curRow[j].aspect );
119                                                         // One of the dimensions are 0. Probably should
120                                                         // not try to resize.
121                                                         combinedPadding += curRow[j].width;
122                                                 } else {
123                                                         combinedAspect += curRow[j].aspect;
124                                                         combinedPadding += curRow[j].width - curRow[j].imgWidth;
125                                                 }
126                                         }
128                                         // Add some padding for inter-element spacing.
129                                         combinedPadding += 5 * curRow.length;
130                                         wantedWidth = maxWidth - combinedPadding;
131                                         preferredHeight = wantedWidth / combinedAspect;
133                                         if ( preferredHeight > curRowHeight * 1.5 ) {
134                                                 // Only expand at most 1.5 times current size
135                                                 // As that's as high a resolution as we have.
136                                                 // Also on the off chance there is a bug in this
137                                                 // code, would prevent accidentally expanding to
138                                                 // be 10 billion pixels wide.
139                                                 mw.log( 'mw.page.gallery: Cannot fit row, aspect is ' + preferredHeight/curRowHeight );
140                                                 if ( i === rows.length - 1 ) {
141                                                         // If its the last row, and we can't fit it,
142                                                         // don't make the entire row huge.
143                                                         avgZoom = ( totalZoom / ( rows.length - 1 ) ) * curRowHeight;
144                                                         if ( isFinite( avgZoom ) && avgZoom >= 1 && avgZoom <= 1.5 ) {
145                                                                 preferredHeight = avgZoom;
146                                                         } else {
147                                                                 // Probably a single row gallery
148                                                                 preferredHeight = curRowHeight;
149                                                         }
150                                                 } else {
151                                                         preferredHeight = 1.5 * curRowHeight;
152                                                 }
153                                         }
154                                         if ( !isFinite( preferredHeight ) ) {
155                                                 // This *definitely* should not happen.
156                                                 mw.log( 'mw.page.gallery: Trying to resize row ' + i + ' to ' + preferredHeight + '?!' );
157                                                 // Skip this row.
158                                                 continue;
159                                         }
160                                         if ( preferredHeight < 5 ) {
161                                                 // Well something clearly went wrong...
162                                                 mw.log( {maxWidth: maxWidth, combinedPadding: combinedPadding, combinedAspect: combinedAspect, wantedWidth: wantedWidth } );
163                                                 mw.log( 'mw.page.gallery: [BUG!] Fitting row ' + i + ' to too small a size: ' + preferredHeight );
164                                                 // Skip this row.
165                                                 continue;
166                                         }
168                                         if ( preferredHeight / curRowHeight > 1 ) {
169                                                 totalZoom += preferredHeight / curRowHeight;
170                                         } else {
171                                                 // If we shrink, still consider that a zoom of 1
172                                                 totalZoom += 1;
173                                         }
175                                         for ( j = 0; j < curRow.length; j++ ) {
176                                                 newWidth = preferredHeight * curRow[j].aspect;
177                                                 padding = curRow[j].width - curRow[j].imgWidth;
178                                                 $outerDiv = curRow[j].$elm;
179                                                 $innerDiv = $outerDiv.children( 'div' ).first();
180                                                 $imageDiv = $innerDiv.children( 'div.thumb' );
181                                                 $imageElm = $imageDiv.find( 'img' ).first();
182                                                 imageElm = $imageElm.length ? $imageElm[0] : null;
183                                                 $caption = $outerDiv.find( 'div.gallerytextwrapper' );
186                                                 // Since we are going to re-adjust the height, the vertical
187                                                 // centering margins need to be reset.
188                                                 $imageDiv.children( 'div' ).css( 'margin', '0px auto' );
190                                                 if ( newWidth < 60 || !isFinite( newWidth ) ) {
191                                                         // Making something skinnier than this will mess up captions,
192                                                         mw.log( 'mw.page.gallery: Tried to make image ' + newWidth + 'px wide but too narrow.' );
193                                                         if ( newWidth < 1 || !isFinite( newWidth ) ) {
194                                                                 $innerDiv.height( preferredHeight );
195                                                                 // Don't even try and touch the image size if it could mean
196                                                                 // making it disappear.
197                                                                 continue;
198                                                         }
199                                                 } else {
200                                                         $outerDiv.width( newWidth + padding );
201                                                         $innerDiv.width( newWidth + padding );
202                                                         $imageDiv.width( newWidth );
203                                                         $caption.width( curRow[j].captionWidth + (newWidth - curRow[j].imgWidth ) );
204                                                 }
206                                                 hookInfo = {
207                                                         fullWidth: newWidth + padding,
208                                                         imgWidth: newWidth,
209                                                         imgHeight: preferredHeight,
210                                                         $innerDiv: $innerDiv,
211                                                         $imageDiv: $imageDiv,
212                                                         $outerDiv: $outerDiv,
213                                                         // Whether the hook took action
214                                                         resolved: false
215                                                 };
217                                                 /**
218                                                  * Gallery resize.
219                                                  *
220                                                  * If your handler resizes an image, it should also set the resolved
221                                                  * property to true. Additionally, because this module only exposes this
222                                                  * logic temporarily, you should load your module in position top to
223                                                  * ensure it is registered before this runs (FIXME: Don't use mw.hook)
224                                                  *
225                                                  * See TimedMediaHandler for an example.
226                                                  *
227                                                  * @event mediawiki_page_gallery_resize
228                                                  * @member mw.hook
229                                                  * @param {Object} hookInfo
230                                                  */
231                                                 mw.hook( 'mediawiki.page.gallery.resize' ).fire( hookInfo );
233                                                 if ( !hookInfo.resolved ) {
234                                                         if ( imageElm ) {
235                                                                 // We don't always have an img, e.g. in the case of an invalid file.
236                                                                 imageElm.width = newWidth;
237                                                                 imageElm.height = preferredHeight;
238                                                         } else {
239                                                                 // Not a file box.
240                                                                 $imageDiv.height( preferredHeight );
241                                                         }
242                                                 }
243                                         }
244                                 }
245                         } )();
246                 } );
247         } );
248 } )( jQuery, mediaWiki );