Add autocomplete for WhatLinksHere subpages
[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 ( $ ) {
6 $( function () {
7 var isTouchScreen,
8 gettingFocus,
9 galleries = 'ul.mw-gallery-packed-overlay, ul.mw-gallery-packed-hover, ul.mw-gallery-packed';
10
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 );
13
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 }
28
29 // Now on to justification.
30 function justify() {
31 var lastTop,
32 $img,
33 imgWidth,
34 imgHeight,
35 captionWidth,
36 rows = [],
37 $gallery = $( this );
38
39 $gallery.children( 'li' ).each( function () {
40 // Math.floor to be paranoid if things are off by 0.00000000001
41 var top = Math.floor( $( this ).position().top ),
42 $this = $( this );
43
44 if ( top !== lastTop ) {
45 rows[rows.length] = [];
46 lastTop = top;
47 }
48
49 $img = $this.find( 'div.thumb a.image img' );
50 if ( $img.length && $img[0].height ) {
51 imgHeight = $img[0].height;
52 imgWidth = $img[0].width;
53 } else {
54 // If we don't have a real image, get the containing divs width/height.
55 // Note that if we do have a real image, using this method will generally
56 // give the same answer, but can be different in the case of a very
57 // narrow image where extra padding is added.
58 imgHeight = $this.children().children( 'div:first' ).height();
59 imgWidth = $this.children().children( 'div:first' ).width();
60 }
61
62 // Hack to make an edge case work ok
63 if ( imgHeight < 30 ) {
64 // Don't try and resize this item.
65 imgHeight = 0;
66 }
67
68 captionWidth = $this.children().children( 'div.gallerytextwrapper' ).width();
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: captionWidth,
76 height: imgHeight
77 };
78
79 // Save all boundaries so we can restore them on window resize
80 $this.data( 'imgWidth', imgWidth );
81 $this.data( 'imgHeight', imgHeight );
82 $this.data( 'width', $this.outerWidth() );
83 $this.data( 'captionWidth', captionWidth );
84 } );
85
86 ( function () {
87 var maxWidth,
88 combinedAspect,
89 combinedPadding,
90 curRow,
91 curRowHeight,
92 wantedWidth,
93 preferredHeight,
94 newWidth,
95 padding,
96 $outerDiv,
97 $innerDiv,
98 $imageDiv,
99 $imageElm,
100 imageElm,
101 $caption,
102 i,
103 j,
104 avgZoom,
105 totalZoom = 0;
106
107 for ( i = 0; i < rows.length; i++ ) {
108 maxWidth = $gallery.width();
109 combinedAspect = 0;
110 combinedPadding = 0;
111 curRow = rows[i];
112 curRowHeight = 0;
113
114 for ( j = 0; j < curRow.length; j++ ) {
115 if ( curRowHeight === 0 ) {
116 if ( isFinite( curRow[j].height ) ) {
117 // Get the height of this row, by taking the first
118 // non-out of bounds height
119 curRowHeight = curRow[j].height;
120 }
121 }
122
123 if ( curRow[j].aspect === 0 || !isFinite( curRow[j].aspect ) ) {
124 // One of the dimensions are 0. Probably should
125 // not try to resize.
126 combinedPadding += curRow[j].width;
127 } else {
128 combinedAspect += curRow[j].aspect;
129 combinedPadding += curRow[j].width - curRow[j].imgWidth;
130 }
131 }
132
133 // Add some padding for inter-element spacing.
134 combinedPadding += 5 * curRow.length;
135 wantedWidth = maxWidth - combinedPadding;
136 preferredHeight = wantedWidth / combinedAspect;
137
138 if ( preferredHeight > curRowHeight * 1.5 ) {
139 // Only expand at most 1.5 times current size
140 // As that's as high a resolution as we have.
141 // Also on the off chance there is a bug in this
142 // code, would prevent accidentally expanding to
143 // be 10 billion pixels wide.
144 if ( i === rows.length - 1 ) {
145 // If its the last row, and we can't fit it,
146 // don't make the entire row huge.
147 avgZoom = ( totalZoom / ( rows.length - 1 ) ) * curRowHeight;
148 if ( isFinite( avgZoom ) && avgZoom >= 1 && avgZoom <= 1.5 ) {
149 preferredHeight = avgZoom;
150 } else {
151 // Probably a single row gallery
152 preferredHeight = curRowHeight;
153 }
154 } else {
155 preferredHeight = 1.5 * curRowHeight;
156 }
157 }
158 if ( !isFinite( preferredHeight ) ) {
159 // This *definitely* should not happen.
160 // Skip this row.
161 continue;
162 }
163 if ( preferredHeight < 5 ) {
164 // Well something clearly went wrong...
165 // Skip this row.
166 continue;
167 }
168
169 if ( preferredHeight / curRowHeight > 1 ) {
170 totalZoom += preferredHeight / curRowHeight;
171 } else {
172 // If we shrink, still consider that a zoom of 1
173 totalZoom += 1;
174 }
175
176 for ( j = 0; j < curRow.length; j++ ) {
177 newWidth = preferredHeight * curRow[j].aspect;
178 padding = curRow[j].width - curRow[j].imgWidth;
179 $outerDiv = curRow[j].$elm;
180 $innerDiv = $outerDiv.children( 'div' ).first();
181 $imageDiv = $innerDiv.children( 'div.thumb' );
182 $imageElm = $imageDiv.find( 'img' ).first();
183 imageElm = $imageElm.length ? $imageElm[0] : null;
184 $caption = $outerDiv.find( 'div.gallerytextwrapper' );
185
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' );
189
190 if ( newWidth < 60 || !isFinite( newWidth ) ) {
191 // Making something skinnier than this will mess up captions,
192 if ( newWidth < 1 || !isFinite( newWidth ) ) {
193 $innerDiv.height( preferredHeight );
194 // Don't even try and touch the image size if it could mean
195 // making it disappear.
196 continue;
197 }
198 } else {
199 $outerDiv.width( newWidth + padding );
200 $innerDiv.width( newWidth + padding );
201 $imageDiv.width( newWidth );
202 $caption.width( curRow[j].captionWidth + ( newWidth - curRow[j].imgWidth ) );
203 }
204
205 if ( imageElm ) {
206 // We don't always have an img, e.g. in the case of an invalid file.
207 imageElm.width = newWidth;
208 imageElm.height = preferredHeight;
209 } else {
210 // Not a file box.
211 $imageDiv.height( preferredHeight );
212 }
213 }
214 }
215 }() );
216 }
217
218 $( galleries ).each( justify );
219 $( window ).resize( $.debounce( 300, true, function () {
220 $( galleries ).children( 'li' ).each( function () {
221 var imgWidth = $( this ).data( 'imgWidth' ),
222 imgHeight = $( this ).data( 'imgHeight' ),
223 width = $( this ).data( 'width' ),
224 captionWidth = $( this ).data( 'captionWidth' ),
225 $imageElm, imageElm;
226
227 // Restore original sizes so we can arrange the elements as on freshly loaded page
228 $( this ).width( width );
229 $( this ).children( 'div' ).first().width( width );
230 $( this ).children( 'div' ).first().children( 'div.thumb' ).width( imgWidth );
231 $( this ).find( 'div.gallerytextwrapper' ).width( captionWidth );
232
233 $imageElm = $( this ).find( 'img' ).first();
234 imageElm = $imageElm.length ? $imageElm[0] : null;
235 if ( imageElm ) {
236 imageElm.width = imgWidth;
237 imageElm.height = imgHeight;
238 }
239 } );
240 } ) );
241 $( window ).resize( $.debounce( 300, function () {
242 $( galleries ).each( justify );
243 } ) );
244 } );
245 }( jQuery ) );