Commit | Line | Data |
---|---|---|
4c51a0e9 | 1 | /*! |
9e16745a AE |
2 | Slimbox v2.05 - The ultimate lightweight Lightbox clone for jQuery |
3 | (c) 2007-2013 Christophe Beyls <http://www.digitalia.be> | |
4c51a0e9 AE |
4 | MIT-style license. |
5 | ||
6 | Modified by WoltLab GmbH to support large images. | |
7 | */ | |
8 | ||
9 | (function($) { | |
10 | ||
11 | // Global variables, accessible to Slimbox only | |
12 | var win = $(window), options, images, activeImage = -1, activeURL, prevImage, nextImage, compatibleOverlay, middle, centerWidth, centerHeight, | |
13 | ie6 = !window.XMLHttpRequest, hiddenElements = [], documentElement = document.documentElement, | |
14 | ||
15 | // Preload images | |
16 | preload = {}, preloadPrev = new Image(), preloadNext = new Image(), | |
17 | ||
18 | // DOM elements | |
19 | overlay, center, image, sizer, prevLink, nextLink, bottomContainer, bottom, caption, number; | |
20 | ||
21 | /* | |
22 | Initialization | |
23 | */ | |
24 | ||
25 | $(function() { | |
26 | // Append the Slimbox HTML code at the bottom of the document | |
27 | $("body").append( | |
28 | $([ | |
9e16745a | 29 | overlay = $('<div id="lbOverlay" />').click(close)[0], |
4c51a0e9 AE |
30 | center = $('<div id="lbCenter" />')[0], |
31 | bottomContainer = $('<div id="lbBottomContainer" />')[0] | |
32 | ]).css("display", "none") | |
33 | ); | |
34 | ||
35 | image = $('<div id="lbImage" />').appendTo(center).append( | |
36 | sizer = $('<div style="position: relative;" />').append([ | |
37 | prevLink = $('<a id="lbPrevLink" href="#" />').click(previous)[0], | |
38 | nextLink = $('<a id="lbNextLink" href="#" />').click(next)[0] | |
39 | ])[0] | |
40 | )[0]; | |
41 | ||
42 | bottom = $('<div id="lbBottom" />').appendTo(bottomContainer).append([ | |
9e16745a | 43 | $('<a id="lbCloseLink" href="#" />').click(close)[0], |
4c51a0e9 AE |
44 | caption = $('<div id="lbCaption" />')[0], |
45 | number = $('<div id="lbNumber" />')[0], | |
46 | $('<div style="clear: both;" />')[0] | |
47 | ])[0]; | |
48 | }); | |
49 | ||
50 | ||
51 | /* | |
52 | API | |
53 | */ | |
54 | ||
55 | // Open Slimbox with the specified parameters | |
56 | $.slimbox = function(_images, startImage, _options) { | |
57 | options = $.extend({ | |
58 | loop: false, // Allows to navigate between first and last images | |
59 | overlayOpacity: 0.8, // 1 is opaque, 0 is completely transparent (change the color in the CSS file) | |
60 | overlayFadeDuration: 400, // Duration of the overlay fade-in and fade-out animations (in milliseconds) | |
61 | resizeDuration: 400, // Duration of each of the box resize animations (in milliseconds) | |
62 | resizeEasing: "swing", // "swing" is jQuery's default easing | |
63 | initialWidth: 250, // Initial width of the box (in pixels) | |
64 | initialHeight: 250, // Initial height of the box (in pixels) | |
65 | imageFadeDuration: 400, // Duration of the image fade-in animation (in milliseconds) | |
66 | captionAnimationDuration: 400, // Duration of the caption animation (in milliseconds) | |
67 | counterText: "Image {x} of {y}", // Translate or change as you wish, or set it to false to disable counter text for image groups | |
68 | closeKeys: [27, 88, 67], // Array of keycodes to close Slimbox, default: Esc (27), 'x' (88), 'c' (67) | |
69 | previousKeys: [37, 80], // Array of keycodes to navigate to the previous image, default: Left arrow (37), 'p' (80) | |
70 | nextKeys: [39, 78] // Array of keycodes to navigate to the next image, default: Right arrow (39), 'n' (78) | |
71 | }, _options); | |
72 | ||
73 | // The function is called for a single image, with URL and Title as first two arguments | |
74 | if (typeof _images == "string") { | |
75 | _images = [[_images, startImage]]; | |
76 | startImage = 0; | |
77 | } | |
4d772854 | 78 | |
c6771d73 | 79 | // WoltLab modifications BEGIN |
4d772854 TD |
80 | if (activeImage == -1) { |
81 | middle = win.scrollTop() + (win.height() / 2); | |
82 | centerWidth = options.initialWidth; | |
83 | centerHeight = options.initialHeight; | |
84 | $(center).css({top: Math.max(0, middle - (centerHeight / 2)), width: centerWidth, height: centerHeight, marginLeft: -centerWidth/2}).show(); | |
85 | compatibleOverlay = ie6 || (overlay.currentStyle && (overlay.currentStyle.position != "fixed")); | |
86 | if (compatibleOverlay) overlay.style.position = "absolute"; | |
87 | $(overlay).css("opacity", options.overlayOpacity).fadeIn(options.overlayFadeDuration); | |
c6771d73 | 88 | |
4d772854 | 89 | WCF.System.DisableScrolling.disable(); |
c6771d73 | 90 | |
4d772854 TD |
91 | position(); |
92 | setup(1); | |
93 | } | |
c6771d73 | 94 | // WoltLab modifications END |
4d772854 | 95 | |
4c51a0e9 AE |
96 | images = _images; |
97 | options.loop = options.loop && (images.length > 1); | |
98 | return changeImage(startImage); | |
99 | }; | |
100 | ||
101 | /* | |
102 | options: Optional options object, see jQuery.slimbox() | |
103 | linkMapper: Optional function taking a link DOM element and an index as arguments and returning an array containing 2 elements: | |
104 | the image URL and the image caption (may contain HTML) | |
105 | linksFilter: Optional function taking a link DOM element and an index as arguments and returning true if the element is part of | |
106 | the image collection that will be shown on click, false if not. "this" refers to the element that was clicked. | |
107 | This function must always return true when the DOM element argument is "this". | |
108 | */ | |
109 | $.fn.slimbox = function(_options, linkMapper, linksFilter) { | |
110 | linkMapper = linkMapper || function(el) { | |
111 | return [el.href, el.title]; | |
112 | }; | |
113 | ||
114 | linksFilter = linksFilter || function() { | |
115 | return true; | |
116 | }; | |
117 | ||
118 | var links = this; | |
119 | ||
120 | return links.unbind("click").click(function() { | |
121 | // Build the list of images that will be displayed | |
122 | var link = this, startIndex = 0, filteredLinks, i = 0, length; | |
123 | filteredLinks = $.grep(links, function(el, i) { | |
124 | return linksFilter.call(link, el, i); | |
125 | }); | |
126 | ||
127 | // We cannot use jQuery.map() because it flattens the returned array | |
128 | for (length = filteredLinks.length; i < length; ++i) { | |
129 | if (filteredLinks[i] == link) startIndex = i; | |
130 | filteredLinks[i] = linkMapper(filteredLinks[i], i); | |
131 | } | |
132 | ||
133 | return $.slimbox(filteredLinks, startIndex, _options); | |
134 | }); | |
135 | }; | |
136 | ||
137 | ||
138 | /* | |
139 | Internal functions | |
140 | */ | |
141 | ||
142 | function position() { | |
143 | var l = win.scrollLeft(), w = win.width(); | |
144 | $([center, bottomContainer]).css("left", l + (w / 2)); | |
145 | if (compatibleOverlay) $(overlay).css({left: l, top: win.scrollTop(), width: w, height: win.height()}); | |
146 | } | |
147 | ||
148 | function setup(open) { | |
149 | if (open) { | |
150 | $("object").add(ie6 ? "select" : "embed").each(function(index, el) { | |
151 | hiddenElements[index] = [el, el.style.visibility]; | |
152 | el.style.visibility = "hidden"; | |
153 | }); | |
154 | } else { | |
155 | $.each(hiddenElements, function(index, el) { | |
156 | el[0].style.visibility = el[1]; | |
157 | }); | |
158 | hiddenElements = []; | |
159 | } | |
160 | var fn = open ? "bind" : "unbind"; | |
161 | win[fn]("scroll resize", position); | |
162 | $(document)[fn]("keydown", keyDown); | |
163 | } | |
164 | ||
165 | function keyDown(event) { | |
9e16745a | 166 | var code = event.which, fn = $.inArray; |
4c51a0e9 AE |
167 | // Prevent default keyboard action (like navigating inside the page) |
168 | return (fn(code, options.closeKeys) >= 0) ? close() | |
169 | : (fn(code, options.nextKeys) >= 0) ? next() | |
170 | : (fn(code, options.previousKeys) >= 0) ? previous() | |
9e16745a | 171 | : null; |
4c51a0e9 AE |
172 | } |
173 | ||
174 | function previous() { | |
175 | return changeImage(prevImage); | |
176 | } | |
177 | ||
178 | function next() { | |
179 | return changeImage(nextImage); | |
180 | } | |
181 | ||
182 | function changeImage(imageIndex) { | |
183 | if (imageIndex >= 0) { | |
184 | activeImage = imageIndex; | |
185 | activeURL = images[activeImage][0]; | |
186 | prevImage = (activeImage || (options.loop ? images.length : 0)) - 1; | |
187 | nextImage = ((activeImage + 1) % images.length) || (options.loop ? 0 : -1); | |
188 | ||
189 | stop(); | |
341b056c AE |
190 | // WoltLab modifications BEGIN |
191 | ||
c6771d73 | 192 | // center.className = "lbLoading"; |
341b056c AE |
193 | $('<span class="icon icon48 icon-spinner" />').appendTo(center); |
194 | ||
195 | // WoltLab modifications END | |
4c51a0e9 AE |
196 | |
197 | preload = new Image(); | |
198 | preload.onload = animateBox; | |
199 | preload.src = activeURL; | |
200 | } | |
201 | ||
202 | return false; | |
203 | } | |
204 | ||
205 | function animateBox() { | |
c6771d73 TD |
206 | // WoltLab modifications BEGIN |
207 | ||
341b056c | 208 | //center.className = ""; |
c6771d73 TD |
209 | |
210 | // WoltLab modifications END | |
4c51a0e9 AE |
211 | $(image).css({backgroundImage: "url(" + activeURL + ")", visibility: "hidden", display: ""}); |
212 | $(sizer).width(preload.width); | |
213 | $([sizer, prevLink, nextLink]).height(preload.height); | |
214 | ||
215 | $(caption).html(images[activeImage][1] || ""); | |
216 | $(number).html((((images.length > 1) && options.counterText) || "").replace(/{x}/, activeImage + 1).replace(/{y}/, images.length)); | |
217 | ||
218 | if (prevImage >= 0) preloadPrev.src = images[prevImage][0]; | |
219 | if (nextImage >= 0) preloadNext.src = images[nextImage][0]; | |
220 | ||
221 | centerWidth = image.offsetWidth; | |
222 | centerHeight = image.offsetHeight; | |
223 | ||
224 | // WoltLab modifications BEGIN | |
225 | ||
226 | // restrict maximum dimensions to 80% of page size | |
227 | var $dimensions = $(window).getDimensions(); | |
228 | var $maxHeight = Math.round($dimensions.height * 0.8); | |
229 | var $maxWidth = Math.round($dimensions.width * 0.8); | |
230 | if (centerHeight > $maxHeight || centerWidth > $maxWidth) { | |
231 | var $ratioHeight = $maxHeight / centerHeight; | |
232 | var $ratioWidth = $maxWidth / centerWidth; | |
233 | if ($ratioHeight < $ratioWidth) { | |
234 | centerWidth = Math.round(centerWidth * ($maxHeight / centerHeight)); | |
235 | centerHeight = $maxHeight; | |
236 | } | |
237 | else { | |
238 | centerHeight = Math.round(centerHeight * ($maxWidth / centerWidth)); | |
239 | centerWidth = $maxWidth; | |
240 | } | |
241 | ||
242 | // update dimensions for sizer and navigation links | |
243 | $(sizer).width(centerWidth - 20); | |
244 | $([sizer, prevLink, nextLink]).height(centerHeight - 20); | |
245 | } | |
246 | ||
247 | // WoltLab modifications END | |
248 | ||
249 | var top = Math.max(0, middle - (centerHeight / 2)); | |
250 | if (center.offsetHeight != centerHeight) { | |
251 | $(center).animate({height: centerHeight, top: top}, options.resizeDuration, options.resizeEasing); | |
252 | } | |
253 | if (center.offsetWidth != centerWidth) { | |
254 | $(center).animate({width: centerWidth, marginLeft: -centerWidth/2}, options.resizeDuration, options.resizeEasing); | |
255 | } | |
256 | $(center).queue(function() { | |
257 | $(bottomContainer).css({width: centerWidth, top: top + centerHeight, marginLeft: -centerWidth/2, visibility: "hidden", display: ""}); | |
341b056c AE |
258 | |
259 | // WoltLab modifications BEGIN | |
260 | ||
261 | // remove spinner | |
262 | $(center).children('.icon-spinner').remove(); | |
263 | ||
264 | // WoltLab modifications END | |
265 | ||
4c51a0e9 AE |
266 | $(image).css({display: "none", visibility: "", opacity: ""}).fadeIn(options.imageFadeDuration, animateCaption); |
267 | }); | |
268 | } | |
269 | ||
270 | function animateCaption() { | |
271 | if (prevImage >= 0) $(prevLink).show(); | |
272 | if (nextImage >= 0) $(nextLink).show(); | |
273 | $(bottom).css("marginTop", -bottom.offsetHeight).animate({marginTop: 0}, options.captionAnimationDuration); | |
274 | bottomContainer.style.visibility = ""; | |
275 | } | |
276 | ||
277 | function stop() { | |
278 | preload.onload = null; | |
279 | preload.src = preloadPrev.src = preloadNext.src = activeURL; | |
280 | $([center, image, bottom]).stop(true); | |
281 | $([prevLink, nextLink, image, bottomContainer]).hide(); | |
282 | } | |
283 | ||
284 | function close() { | |
285 | if (activeImage >= 0) { | |
286 | stop(); | |
287 | activeImage = prevImage = nextImage = -1; | |
288 | $(center).hide(); | |
289 | $(overlay).stop().fadeOut(options.overlayFadeDuration, setup); | |
9e16745a | 290 | |
c6771d73 TD |
291 | // WoltLab modifications BEGIN |
292 | ||
b2918c9d | 293 | WCF.System.DisableScrolling.enable(); |
c6771d73 TD |
294 | |
295 | // WoltLab modifications END | |
4c51a0e9 AE |
296 | } |
297 | ||
298 | return false; | |
299 | } | |
300 | ||
301 | })(jQuery); |