Merge "Remove not used private member variable mParserWarnings from OutputPage"
[mediawiki.git] / resources / src / mediawiki / page / gallery.js
blob79937e57065894b68687daf68fe629b478f6a4ae
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         var $galleries,
7                 bound = false,
8                 // Is there a better way to detect a touchscreen? Current check taken from stack overflow.
9                 isTouchScreen = !!( window.ontouchstart !== undefined ||
10                         window.DocumentTouch !== undefined && document instanceof window.DocumentTouch
11                 );
13         /**
14          * Perform the layout justification.
15          *
16          * @ignore
17          * @context {HTMLElement} A `ul.mw-gallery-*` element
18          */
19         function justify() {
20                 var lastTop,
21                         $img,
22                         imgWidth,
23                         imgHeight,
24                         captionWidth,
25                         rows = [],
26                         $gallery = $( this );
28                 $gallery.children( 'li.gallerybox' ).each( function () {
29                         // Math.floor to be paranoid if things are off by 0.00000000001
30                         var top = Math.floor( $( this ).position().top ),
31                                 $this = $( this );
33                         if ( top !== lastTop ) {
34                                 rows[ rows.length ] = [];
35                                 lastTop = top;
36                         }
38                         $img = $this.find( 'div.thumb a.image img' );
39                         if ( $img.length && $img[ 0 ].height ) {
40                                 imgHeight = $img[ 0 ].height;
41                                 imgWidth = $img[ 0 ].width;
42                         } else {
43                                 // If we don't have a real image, get the containing divs width/height.
44                                 // Note that if we do have a real image, using this method will generally
45                                 // give the same answer, but can be different in the case of a very
46                                 // narrow image where extra padding is added.
47                                 imgHeight = $this.children().children( 'div:first' ).height();
48                                 imgWidth = $this.children().children( 'div:first' ).width();
49                         }
51                         // Hack to make an edge case work ok
52                         if ( imgHeight < 30 ) {
53                                 // Don't try and resize this item.
54                                 imgHeight = 0;
55                         }
57                         captionWidth = $this.children().children( 'div.gallerytextwrapper' ).width();
58                         rows[ rows.length - 1 ][ rows[ rows.length - 1 ].length ] = {
59                                 $elm: $this,
60                                 width: $this.outerWidth(),
61                                 imgWidth: imgWidth,
62                                 // XXX: can divide by 0 ever happen?
63                                 aspect: imgWidth / imgHeight,
64                                 captionWidth: captionWidth,
65                                 height: imgHeight
66                         };
68                         // Save all boundaries so we can restore them on window resize
69                         $this.data( 'imgWidth', imgWidth );
70                         $this.data( 'imgHeight', imgHeight );
71                         $this.data( 'width', $this.outerWidth() );
72                         $this.data( 'captionWidth', captionWidth );
73                 } );
75                 ( function () {
76                         var maxWidth,
77                                 combinedAspect,
78                                 combinedPadding,
79                                 curRow,
80                                 curRowHeight,
81                                 wantedWidth,
82                                 preferredHeight,
83                                 newWidth,
84                                 padding,
85                                 $outerDiv,
86                                 $innerDiv,
87                                 $imageDiv,
88                                 $imageElm,
89                                 imageElm,
90                                 $caption,
91                                 i,
92                                 j,
93                                 avgZoom,
94                                 totalZoom = 0;
96                         for ( i = 0; i < rows.length; i++ ) {
97                                 maxWidth = $gallery.width();
98                                 combinedAspect = 0;
99                                 combinedPadding = 0;
100                                 curRow = rows[ i ];
101                                 curRowHeight = 0;
103                                 for ( j = 0; j < curRow.length; j++ ) {
104                                         if ( curRowHeight === 0 ) {
105                                                 if ( isFinite( curRow[ j ].height ) ) {
106                                                         // Get the height of this row, by taking the first
107                                                         // non-out of bounds height
108                                                         curRowHeight = curRow[ j ].height;
109                                                 }
110                                         }
112                                         if ( curRow[ j ].aspect === 0 || !isFinite( curRow[ j ].aspect ) ) {
113                                                 // One of the dimensions are 0. Probably should
114                                                 // not try to resize.
115                                                 combinedPadding += curRow[ j ].width;
116                                         } else {
117                                                 combinedAspect += curRow[ j ].aspect;
118                                                 combinedPadding += curRow[ j ].width - curRow[ j ].imgWidth;
119                                         }
120                                 }
122                                 // Add some padding for inter-element spacing.
123                                 combinedPadding += 5 * curRow.length;
124                                 wantedWidth = maxWidth - combinedPadding;
125                                 preferredHeight = wantedWidth / combinedAspect;
127                                 if ( preferredHeight > curRowHeight * 1.5 ) {
128                                         // Only expand at most 1.5 times current size
129                                         // As that's as high a resolution as we have.
130                                         // Also on the off chance there is a bug in this
131                                         // code, would prevent accidentally expanding to
132                                         // be 10 billion pixels wide.
133                                         if ( i === rows.length - 1 ) {
134                                                 // If its the last row, and we can't fit it,
135                                                 // don't make the entire row huge.
136                                                 avgZoom = ( totalZoom / ( rows.length - 1 ) ) * curRowHeight;
137                                                 if ( isFinite( avgZoom ) && avgZoom >= 1 && avgZoom <= 1.5 ) {
138                                                         preferredHeight = avgZoom;
139                                                 } else {
140                                                         // Probably a single row gallery
141                                                         preferredHeight = curRowHeight;
142                                                 }
143                                         } else {
144                                                 preferredHeight = 1.5 * curRowHeight;
145                                         }
146                                 }
147                                 if ( !isFinite( preferredHeight ) ) {
148                                         // This *definitely* should not happen.
149                                         // Skip this row.
150                                         continue;
151                                 }
152                                 if ( preferredHeight < 5 ) {
153                                         // Well something clearly went wrong...
154                                         // Skip this row.
155                                         continue;
156                                 }
158                                 if ( preferredHeight / curRowHeight > 1 ) {
159                                         totalZoom += preferredHeight / curRowHeight;
160                                 } else {
161                                         // If we shrink, still consider that a zoom of 1
162                                         totalZoom += 1;
163                                 }
165                                 for ( j = 0; j < curRow.length; j++ ) {
166                                         newWidth = preferredHeight * curRow[ j ].aspect;
167                                         padding = curRow[ j ].width - curRow[ j ].imgWidth;
168                                         $outerDiv = curRow[ j ].$elm;
169                                         $innerDiv = $outerDiv.children( 'div' ).first();
170                                         $imageDiv = $innerDiv.children( 'div.thumb' );
171                                         $imageElm = $imageDiv.find( 'img' ).first();
172                                         imageElm = $imageElm.length ? $imageElm[ 0 ] : null;
173                                         $caption = $outerDiv.find( 'div.gallerytextwrapper' );
175                                         // Since we are going to re-adjust the height, the vertical
176                                         // centering margins need to be reset.
177                                         $imageDiv.children( 'div' ).css( 'margin', '0px auto' );
179                                         if ( newWidth < 60 || !isFinite( newWidth ) ) {
180                                                 // Making something skinnier than this will mess up captions,
181                                                 if ( newWidth < 1 || !isFinite( newWidth ) ) {
182                                                         $innerDiv.height( preferredHeight );
183                                                         // Don't even try and touch the image size if it could mean
184                                                         // making it disappear.
185                                                         continue;
186                                                 }
187                                         } else {
188                                                 $outerDiv.width( newWidth + padding );
189                                                 $innerDiv.width( newWidth + padding );
190                                                 $imageDiv.width( newWidth );
191                                                 $caption.width( curRow[ j ].captionWidth + ( newWidth - curRow[ j ].imgWidth ) );
192                                         }
194                                         if ( imageElm ) {
195                                                 // We don't always have an img, e.g. in the case of an invalid file.
196                                                 imageElm.width = newWidth;
197                                                 imageElm.height = preferredHeight;
198                                         } else {
199                                                 // Not a file box.
200                                                 $imageDiv.height( preferredHeight );
201                                         }
202                                 }
203                         }
204                 }() );
205         }
207         function handleResizeStart() {
208                 $galleries.children( 'li.gallerybox' ).each( function () {
209                         var imgWidth = $( this ).data( 'imgWidth' ),
210                                 imgHeight = $( this ).data( 'imgHeight' ),
211                                 width = $( this ).data( 'width' ),
212                                 captionWidth = $( this ).data( 'captionWidth' ),
213                                 $innerDiv = $( this ).children( 'div' ).first(),
214                                 $imageDiv = $innerDiv.children( 'div.thumb' ),
215                                 $imageElm, imageElm;
217                         // Restore original sizes so we can arrange the elements as on freshly loaded page
218                         $( this ).width( width );
219                         $innerDiv.width( width );
220                         $imageDiv.width( imgWidth );
221                         $( this ).find( 'div.gallerytextwrapper' ).width( captionWidth );
223                         $imageElm = $( this ).find( 'img' ).first();
224                         imageElm = $imageElm.length ? $imageElm[ 0 ] : null;
225                         if ( imageElm ) {
226                                 imageElm.width = imgWidth;
227                                 imageElm.height = imgHeight;
228                         } else {
229                                 $imageDiv.height( imgHeight );
230                         }
231                 } );
232         }
234         function handleResizeEnd() {
235                 $galleries.each( justify );
236         }
238         mw.hook( 'wikipage.content' ).add( function ( $content ) {
239                 if ( isTouchScreen ) {
240                         // Always show the caption for a touch screen.
241                         $content.find( 'ul.mw-gallery-packed-hover' )
242                                 .addClass( 'mw-gallery-packed-overlay' )
243                                 .removeClass( 'mw-gallery-packed-hover' );
244                 } else {
245                         // Note use of just "a", not a.image, since we want this to trigger if a link in
246                         // the caption receives focus
247                         $content.find( 'ul.mw-gallery-packed-hover li.gallerybox' ).on( 'focus blur', 'a', function ( e ) {
248                                 // Confusingly jQuery leaves e.type as focusout for delegated blur events
249                                 var gettingFocus = e.type !== 'blur' && e.type !== 'focusout';
250                                 $( this ).closest( 'li.gallerybox' ).toggleClass( 'mw-gallery-focused', gettingFocus );
251                         } );
252                 }
254                 $galleries = $content.find( 'ul.mw-gallery-packed-overlay, ul.mw-gallery-packed-hover, ul.mw-gallery-packed' );
255                 // Call the justification asynchronous because live preview fires the hook with detached $content.
256                 setTimeout( function () {
257                         $galleries.each( justify );
259                         // Bind here instead of in the top scope as the callbacks use $galleries.
260                         if ( !bound ) {
261                                 bound = true;
262                                 $( window )
263                                         .resize( $.debounce( 300, true, handleResizeStart ) )
264                                         .resize( $.debounce( 300, handleResizeEnd ) );
265                         }
266                 } );
267         } );
268 }( mediaWiki, jQuery ) );