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