PageSourceSearch

https://bettinadiel.space/assets/vendors/BigPicure/BigPicture.js?v2.0.0

js bettinadiel.space collected 2026-10-07 20:44:52 UTC 24,055 bytes, 755 lines download raw bytes

vendor: 5,537 bytes, lines 1-218
1var BigPicture = (function () {
2	// BigPicture.js | license MIT | henrygd.me/bigpicture
3
4	// trigger element used to open popup
5	var el;
6
7	// set to true after first interaction
8	var initialized;
9
10	// container element holding html needed for script
11	var container;
12
13	// currently active display element (image, video, youtube / vimeo iframe container)
14	var displayElement;
15
16	// popup image element
17	var displayImage;
18
19	// popup video element
20	var displayVideo;
21
22	// popup audio element
23	var displayAudio;
24
25	// container element to hold youtube / vimeo iframe
26	var iframeContainer;
27
28	// iframe to hold youtube / vimeo player
29	var iframeSiteVid;
30
31	// store requested image source
32	var imgSrc;
33
34	// button that closes the container
35	var closeButton;
36
37	// youtube / vimeo video id
38	var siteVidID;
39
40	// keeps track of loading icon display state
41	var isLoading;
42
43	// timeout to check video status while loading
44	var checkMediaTimeout;
45
46	// loading icon element
47	var loadingIcon;
48
49	// caption element
50	var caption;
51
52	// caption content element
53	var captionText;
54
55	// store caption content
56	var captionContent;
57
58	// hide caption button element
59	var captionHideButton;
60
61	// open state for container element
62	var isOpen;
63
64	// gallery open state
65	var galleryOpen;
66
67	// used during close animation to avoid triggering timeout twice
68	var isClosing;
69
70	// array of prev viewed image urls to check if cached before showing loading icon
71	var imgCache = [];
72
73	// store whether image requested is remote or local
74	var remoteImage;
75
76	// store animation opening callbacks
77	var animationStart;
78	var animationEnd;
79
80	// store changeGalleryImage callback
81	var onChangeImage;
82
83	// gallery left / right icons
84	var rightArrowBtn;
85
86	var leftArrowBtn;
87
88	// position of gallery
89	var galleryPosition;
90
91	// hold active gallery els / image src
92	var galleryEls;
93
94	// counter element
95	var galleryCounter;
96
97	// store images in gallery that are being loaded
98	var preloadedImages = {};
99
100	// whether device supports touch events
101	var supportsTouch;
102
103	// options object
104	var opts;
105
106	// Save bytes in the minified version
107	var appendEl = 'appendChild';
108	var createEl = 'createElement';
109	var removeEl = 'removeChild';
110
111	function BigPicture (options) {
112		// initialize called on initial open to create elements / style / event handlers
113		initialized || initialize();
114
115		// clear currently loading stuff
116		if (isLoading) {
117			clearTimeout(checkMediaTimeout);
118			removeContainer();
119		}
120
121		opts = options;
122
123		// store video id if youtube / vimeo video is requested
124		siteVidID = options.ytSrc || options.vimeoSrc;
125
126		// store optional callbacks
127		animationStart = options.animationStart;
128		animationEnd = options.animationEnd;
129		onChangeImage = options.onChangeImage;
130
131		// set trigger element
132		el = options.el;
133
134		// wipe existing remoteImage state
135		remoteImage = false;
136
137		// set caption if provided
138		captionContent = el.getAttribute('data-caption');
139
140		if (options.gallery) {
141			makeGallery(options.gallery, options.position);
142		} else if (siteVidID || options.iframeSrc) {
143			// if vimeo, youtube, or iframe video
144			// toggleLoadingIcon(true)
145			displayElement = iframeContainer;
146			createIframe();
147		} else if (options.imgSrc) {
148			// if remote image
149			remoteImage = true;
150			imgSrc = options.imgSrc;
151			!~imgCache.indexOf(imgSrc) && toggleLoadingIcon(true);
152			displayElement = displayImage;
153			displayElement.src = imgSrc;
154		} else if (options.audio) {
155			// if direct video link
156			toggleLoadingIcon(true);
157			displayElement = displayAudio;
158			displayElement.src = options.audio;
159			checkMedia('audio file');
160		} else if (options.vidSrc) {
161			// if direct video link
162			toggleLoadingIcon(true);
163			if (options.dimensions) {
164				changeCSS(displayVideo, ("width:" + (options.dimensions[0]) + "px"));
165			}
166			makeVidSrc(options.vidSrc);
167			checkMedia('video');
168		} else {
169			// local image / background image already loaded on page
170			displayElement = displayImage;
171			// get img source or element background image
172			displayElement.src =
173				el.tagName === 'IMG'
174					? el.src
175					: window
176							.getComputedStyle(el)
177							.backgroundImage.replace(/^url|[(|)|'|"]/g, '');
178		}
179
180		// add container to page
181		container[appendEl](displayElement);
182		document.body[appendEl](container);
183		return {
184			close: close,
185			next: function () { return updateGallery(1); },
186			prev: function () { return updateGallery(-1); },
187		}
188	}
189
190	// create all needed methods / store dom elements on first use
191	function initialize() {
192		var startX, isPinch;
193		// return close button elements
194		function createCloseButton(className) {
195			var el = document[createEl]('button');
196			el.className = className;
197			el.innerHTML =
198				'<svg viewBox="0 0 48 48"><path d="M28 24L47 5a3 3 0 1 0-4-4L24 20 5 1a3 3 0 1 0-4 4l19 19L1 43a3 3 0 1 0 4 4l19-19 19 19a3 3 0 0 0 4 0v-4L28 24z"/></svg>';
199			return el
200		}
201
202		function createArrowSymbol(direction, style) {
203			var el = document[createEl]('button');
204			el.className = 'bp-lr';
205			el.innerHTML =
206				'<svg viewBox="0 0 129 129" height="70" fill="#fff"><path d="M88.6 121.3c.8.8 1.8 1.2 2.9 1.2s2.1-.4 2.9-1.2a4.1 4.1 0 0 0 0-5.8l-51-51 51-51a4.1 4.1 0 0 0-5.8-5.8l-54 53.9a4.1 4.1 0 0 0 0 5.8l54 53.9z"/></svg>';
207			changeCSS(el, style);
208			el.onclick = function (e) {
209				e.stopPropagation();
210				updateGallery(direction);
211			};
212			return el
213		}
214
215		// add style - if you want to tweak, run through beautifier
216		var style = document[createEl]('STYLE');
217		style.innerHTML =
218			'#bp_caption,#bp_container{bottom:0;left:0;right:0;position:fixed;opacity:0}
218#bp_container>*,#bp_loader{position:absolute;right:0;z-index:10}#bp_container,#bp_caption,#bp_container svg{pointer-events:none}#bp_container{top:0;z-index:9999;background:rgba(0,0,0,.7);opacity:0;transition:opacity .35s}#bp_loader{top:0;left:0;bottom:0;display:flex;align-items:center;cursor:wait;background:0;z-index:9}#bp_loader svg{width:50%;max-width:300px;max-height:50%;margin:auto;animation:bpturn 1s infinite linear}#bp_aud,#bp_container img,#bp_sv,#bp_vid{user-select:none;max-height:96%;max-width:96%;top:0;bottom:0;left:0;margin:auto;box-shadow:0 0 3em rgba(0,0,0,.4);z-index:-1}#bp_sv{background:#111}#bp_sv svg{width:66px}#bp_caption{font-size:.9em;padding:1.3em;background:rgba(15,15,15,.94);color:#fff;text-align:center;transition:opacity .3s}#bp_aud{width:650px;top:calc(50% - 20px);bottom:auto;box-shadow:none}#bp_count{left:0;right:auto;padding:14px;color:rgba(255,255,255,.7);font-size:22px;cursor:default}#bp_container button{position:absolute;border:0;outline:0;background:0;cursor:pointer;transition:all .1s}#bp_container>.bp-x{padding:0;height:41px;width:41px;border-radius:100%;top:8px;right:14px;opacity:.8;line-height:1}#bp_container>.bp-x:focus,#bp_container>.bp-x:hover{background:rgba(255,255,255,.2)}.bp-x svg,.bp-xc svg{height:21px;width:20px;fill:#fff;vertical-align:top;}.bp-xc svg{width:16px}#bp_container .bp-xc{left:2%;bottom:100%;padding:9px 20px 7px;background:#d04444;border-radius:2px 2px 0 0;opacity:.85}#bp_container .bp-xc:focus,#bp_container .bp-xc:hover{opacity:1}.bp-lr{top:50%;top:calc(50% - 130px);padding:99px 0;width:6%;background:0;border:0;opacity:.4;transition:opacity .1s}.bp-lr:focus,.bp-lr:hover{opacity:.8}@keyframes bpf{50%{transform:translatex(15px)}100%{transform:none}}@keyframes bpl{50%{transform:translatex(-15px)}100%{transform:none}}@keyframes bpfl{0%{opacity:0;transform:translatex(70px)}100%{opacity:1;transform:none}}@keyframes bpfr{0%{opacity:0;transform:translatex(-70px)}100%{opacity:1;transform:none}}@keyframes bpfol{0%{opacity:1;transform:none}100%{opacity:0;transform:translatex(-70px)}}@keyframes bpfor{0%{opacity:1;transform:none}100%{opacity:0;transform:translatex(70px)}}@keyframes bpturn{0%{transform:none}100%{transform:rotate(360deg)}}@media (max-width:600px){.bp-lr{font-size:15vw}}';
219		document.head[appendEl](style);
220
221		// create container element
222		container = document[createEl]('DIV');
223		container.id = 'bp_container';
224		container.onclick = close;
225		closeButton = createCloseButton('bp-x');
226		container[appendEl](closeButton);
227		// gallery touch listeners
228		if ('ontouchend' in window && window.visualViewport) {
229			supportsTouch = true;
230			container.ontouchstart = function (ref) {
231				var touches = ref.touches;
232				var changedTouches = ref.changedTouches;
233
234				isPinch = touches.length > 1;
235				startX = changedTouches[0].pageX;
236			};
237			container.ontouchend = function (ref) {
238				var changedTouches = ref.changedTouches;
239
240				if (galleryOpen && !isPinch && window.visualViewport.scale <= 1) {
241					var distX = changedTouches[0].pageX - startX;
242					// swipe right
243					distX < -30 && updateGallery(1);
244					// swipe left
245					distX > 30 && updateGallery(-1);
246				}
247			};
248		}
249
250		// create display image element
251		displayImage = document[createEl]('IMG');
252
253		// create display video element
254		displayVideo = document[createEl]('VIDEO');
255		displayVideo.id = 'bp_vid';
256		displayVideo.setAttribute('playsinline', true);
257		displayVideo.controls = true;
258		displayVideo.loop = true;
259
260		// create audio element
261		displayAudio = document[createEl]('audio');
262		displayAudio.id = 'bp_aud';
263		displayAudio.controls = true;
264		displayAudio.loop = true;
265
266		// create gallery counter
267		galleryCounter = document[createEl]('span');
268		galleryCounter.id = 'bp_count';
269
270		// create caption elements
271		caption = document[createEl]('DIV');
272		caption.id = 'bp_caption';
273		captionHideButton = createCloseButton('bp-xc');
274		captionHideButton.onclick = toggleCaption.bind(null, false);
275		caption[appendEl](captionHideButton);
276		captionText = document[createEl]('SPAN');
277		caption[appendEl](captionText);
278		container[appendEl](caption);
279
280		// left / right arrow icons
281		rightArrowBtn = createArrowSymbol(1, 'transform:scalex(-1)');
282		leftArrowBtn = createArrowSymbol(-1, 'left:0;right:auto');
283
284		// create loading icon element
285		loadingIcon = document[createEl]('DIV');
286		loadingIcon.id = 'bp_loader';
287		loadingIcon.innerHTML =
vendor: 1,162 bytes, lines 288-312
288			'<svg viewbox="0 0 32 32" fill="#fff" opacity=".8"><path d="M16 0a16 16 0 0 0 0 32 16 16 0 0 0 0-32m0 4a12 12 0 0 1 0 24 12 12 0 0 1 0-24" fill="#000" opacity=".5"/><path d="M16 0a16 16 0 0 1 16 16h-4A12 12 0 0 0 16 4z"/></svg>';
289		// create youtube / vimeo container
290		iframeContainer = document[createEl]('DIV');
291		iframeContainer.id = 'bp_sv';
292
293		// create iframe to hold youtube / vimeo player
294		iframeSiteVid = document[createEl]('IFRAME');
295		iframeSiteVid.setAttribute('allowfullscreen', true);
296		iframeSiteVid.allow = 'autoplay; fullscreen';
297		iframeSiteVid.onload = function () { return iframeContainer[removeEl](loadingIcon); };
298		changeCSS(
299			iframeSiteVid,
300			'border:0;position:absolute;height:100%;width:100%;left:0;top:0'
301		);
302		iframeContainer[appendEl](iframeSiteVid);
303
304		// display image bindings for image load and error
305		displayImage.onload = open;
306		displayImage.onerror = open.bind(null, 'image');
307
308		window.addEventListener('resize', function () {
309			// adjust loader position on window resize
310			galleryOpen || (isLoading && toggleLoadingIcon(true));
311			// adjust iframe dimensions
312			displayElement === iframeContainer && updateIfram
vendor: 7,431 bytes, lines 312-551
312eDimensions();
313		});
314
315		// close container on escape key press and arrow buttons for gallery
316		document.addEventListener('keyup', function (ref) {
317			var keyCode = ref.keyCode;
318
319			keyCode === 27 && isOpen && close();
320			if (galleryOpen) {
321				keyCode === 39 && updateGallery(1);
322				keyCode === 37 && updateGallery(-1);
323				keyCode === 38 && updateGallery(10);
324				keyCode === 40 && updateGallery(-10);
325			}
326		});
327		// prevent scrolling with arrow keys if gallery open
328		document.addEventListener('keydown', function (e) {
329			var usedKeys = [37, 38, 39, 40];
330			if (galleryOpen && ~usedKeys.indexOf(e.keyCode)) {
331				e.preventDefault();
332			}
333		});
334
335		// trap focus within conainer while open
336		document.addEventListener(
337			'focus',
338			function (e) {
339				if (isOpen && !container.contains(e.target)) {
340					e.stopPropagation();
341					closeButton.focus();
342				}
343			},
344			true
345		);
346
347		// all done
348		initialized = true;
349	}
350
351	// return transform style to make full size display el match trigger el size
352	function getRect() {
353		var ref = el.getBoundingClientRect();
354		var top = ref.top;
355		var left = ref.left;
356		var width = ref.width;
357		var height = ref.height;
358		var leftOffset = left - (container.clientWidth - width) / 2;
359		var centerTop = top - (container.clientHeight - height) / 2;
360		var scaleWidth = el.clientWidth / displayElement.clientWidth;
361		var scaleHeight = el.clientHeight / displayElement.clientHeight;
362		return ("transform:translate3D(" + leftOffset + "px, " + centerTop + "px, 0) scale3D(" + scaleWidth + ", " + scaleHeight + ", 0)")
363	}
364
365	function makeVidSrc(source) {
366		if (Array.isArray(source)) {
367			displayElement = displayVideo.cloneNode();
368			source.forEach(function (src) {
369				var source = document[createEl]('SOURCE');
370				source.src = src;
371				source.type = "video/" + (src.match(/.(\w+)$/)[1]);
372				displayElement[appendEl](source);
373			});
374		} else {
375			displayElement = displayVideo;
376			displayElement.src = source;
377		}
378	}
379
380	function makeGallery(gallery, position) {
381		var galleryAttribute = opts.galleryAttribute || 'data-bp';
382		if (Array.isArray(gallery)) {
383			// is array of images
384			galleryPosition = position || 0;
385			galleryEls = gallery;
386			captionContent = gallery[galleryPosition].caption;
387		} else {
388			// is element selector or nodelist
389			galleryEls = [].slice.call(
390				typeof gallery === 'string'
391					? document.querySelectorAll((gallery + " [" + galleryAttribute + "]"))
392					: gallery
393			);
394			// find initial gallery position
395			var elIndex = galleryEls.indexOf(el);
396			galleryPosition =
397				position === 0 || position ? position : elIndex !== -1 ? elIndex : 0;
398			// make gallery object w/ els / src / caption
399			galleryEls = galleryEls.map(function (el) { return ({
400				el: el,
401				src: el.getAttribute(galleryAttribute),
402				caption: el.getAttribute('data-caption'),
403			}); });
404		}
405		// show loading icon if needed
406		remoteImage = true;
407		// set initial src to imgSrc so it will be cached in open func
408		imgSrc = galleryEls[galleryPosition].src;
409		!~imgCache.indexOf(imgSrc) && toggleLoadingIcon(true);
410		if (galleryEls.length > 1) {
411			// if length is greater than one, add gallery stuff
412			container[appendEl](galleryCounter);
413			galleryCounter.innerHTML = (galleryPosition + 1) + "/" + (galleryEls.length);
414			if (!supportsTouch) {
415				// add arrows if device doesn't support touch
416				container[appendEl](rightArrowBtn);
417				container[appendEl](leftArrowBtn);
418			}
419		} else {
420			// gallery is one, just show without clutter
421			galleryEls = false;
422		}
423		displayElement = displayImage;
424		// set initial image src
425		displayElement.src = imgSrc;
426	}
427
428	function updateGallery(movement) {
429		var galleryLength = galleryEls.length - 1;
430
431		// only allow one change at a time
432		if (isLoading) {
433			return
434		}
435
436		// return if requesting out of range image
437		var isEnd =
438			(movement > 0 && galleryPosition === galleryLength) ||
439			(movement < 0 && !galleryPosition);
440		if (isEnd) {
441			// if beginning or end of gallery, run end animation
442			if (!opts.loop) {
443				changeCSS(displayImage, '');
444				setTimeout(
445					changeCSS,
446					9,
447					displayImage,
448					("animation:" + (movement > 0 ? 'bpl' : 'bpf') + " .3s;transition:transform .35s")
449				);
450				return
451			}
452			// if gallery is looped, adjust position to beginning / end
453			galleryPosition = movement > 0 ? -1 : galleryLength + 1;
454		}
455
456		// normalize position
457		galleryPosition = Math.max(
458			0,
459			Math.min(galleryPosition + movement, galleryLength)
460		)
461
462		// load images before and after for quicker scrolling through pictures
463		;[galleryPosition - 1, galleryPosition, galleryPosition + 1].forEach(
464			function (position) {
465				// normalize position
466				position = Math.max(0, Math.min(position, galleryLength));
467				// cancel if image has already been preloaded
468				if (preloadedImages[position]) { return }
469				var src = galleryEls[position].src;
470				// create image for preloadedImages
471				var img = document[createEl]('IMG');
472				img.addEventListener('load', addToImgCache.bind(null, src));
473				img.src = src;
474				preloadedImages[position] = img;
475			}
476		);
477		// if image is loaded, show it
478		if (preloadedImages[galleryPosition].complete) {
479			return changeGalleryImage(movement)
480		}
481		// if not, show loading icon and change when loaded
482		isLoading = true;
483		changeCSS(loadingIcon, 'opacity:.4;');
484		container[appendEl](loadingIcon);
485		preloadedImages[galleryPosition].onload = function () {
486			galleryOpen && changeGalleryImage(movement);
487		};
488		// if error, store error object in el array
489		preloadedImages[galleryPosition].onerror = function () {
490			galleryEls[galleryPosition] = {
491				error: 'Error loading image',
492			};
493			galleryOpen && changeGalleryImage(movement);
494		};
495	}
496
497	function changeGalleryImage(movement) {
498		if (isLoading) {
499			container[removeEl](loadingIcon);
500			isLoading = false;
501		}
502		var activeEl = galleryEls[galleryPosition];
503		if (activeEl.error) {
504			// show alert if error
505			alert(activeEl.error);
506		} else {
507			// add new image, animate images in and out w/ css animation
508			var oldimg = container.querySelector('img:last-of-type');
509			displayImage = displayElement = preloadedImages[galleryPosition];
510			changeCSS(
511				displayImage,
512				("animation:" + (movement > 0 ? 'bpfl' : 'bpfr') + " .35s;transition:transform .35s")
513			);
514			changeCSS(oldimg, ("animation:" + (movement > 0 ? 'bpfol' : 'bpfor') + " .35s both"));
515			container[appendEl](displayImage);
516			// update el for closing animation
517			if (activeEl.el) {
518				el = activeEl.el;
519			}
520		}
521		// update counter
522		galleryCounter.innerHTML = (galleryPosition + 1) + "/" + (galleryEls.length);
523		// show / hide caption
524		toggleCaption(galleryEls[galleryPosition].caption);
525		// execute onChangeImage callback
526		onChangeImage && onChangeImage([displayImage, galleryEls[galleryPosition]]);
527	}
528
529	// create video iframe
530	function createIframe() {
531		var url;
532		var prefix = 'https://';
533		var suffix = 'autoplay=1';
534
535		// create appropriate url
536		if (opts.ytSrc) {
537			url = prefix + "www.youtube" + (opts.ytNoCookie ? '-nocookie' : '') + ".com/embed/" + siteVidID + "?html5=1&rel=0&playsinline=1&" + suffix;
538		} else if (opts.vimeoSrc) {
539			url = prefix + "player.vimeo.com/video/" + siteVidID + "?" + suffix;
540		} else if (opts.iframeSrc) {
541			url = opts.iframeSrc;
542		}
543
544		// add loading spinner to iframe container
545		changeCSS(loadingIcon, '');
546		iframeContainer[appendEl](loadingIcon);
547
548		// set iframe src to url
549		iframeSiteVid.src = url;
550
551		update
551IframeDimensions();
552
553		setTimeout(open, 9);
554	}
555
556	function updateIfram
vendor: 5,462 bytes, lines 556-755
556eDimensions() {
557		var height;
558		var width;
559
560		// handle height / width / aspect / max width for iframe
561		var windowHeight = window.innerHeight * 0.95;
562		var windowWidth = window.innerWidth * 0.95;
563		var windowAspect = windowHeight / windowWidth;
564
565		var ref = opts.dimensions || [1920, 1080];
566		var dimensionWidth = ref[0];
567		var dimensionHeight = ref[1];
568
569		var iframeAspect = dimensionHeight / dimensionWidth;
570
571		if (iframeAspect > windowAspect) {
572			height = Math.min(dimensionHeight, windowHeight);
573			width = height / iframeAspect;
574		} else {
575			width = Math.min(dimensionWidth, windowWidth);
576			height = width * iframeAspect;
577		}
578
579		iframeContainer.style.cssText += "width:" + width + "px;height:" + height + "px;";
580	}
581
582	// timeout to check video status while loading
583	function checkMedia(errMsg) {
584		if (~[1, 4].indexOf(displayElement.readyState)) {
585			open();
586			// short timeout to to make sure controls show in safari 11
587			setTimeout(function () {
588				displayElement.play();
589			}, 99);
590		} else if (displayElement.error) {
591			open(errMsg);
592		} else {
593			checkMediaTimeout = setTimeout(checkMedia, 35, errMsg);
594		}
595	}
596
597	// hide / show loading icon
598	function toggleLoadingIcon(bool) {
599		// don't show loading icon if noLoader is specified
600		if (opts.noLoader) {
601			return
602		}
603		// bool is true if we want to show icon, false if we want to remove
604		// change style to match trigger element dimensions if we want to show
605		bool &&
606			changeCSS(
607				loadingIcon,
608				("top:" + (el.offsetTop) + "px;left:" + (el.offsetLeft) + "px;height:" + (el.clientHeight) + "px;width:" + (el.clientWidth) + "px")
609			);
610		// add or remove loader from DOM
611		el.parentElement[bool ? appendEl : removeEl](loadingIcon);
612		isLoading = bool;
613	}
614
615	// hide & show caption
616	function toggleCaption(captionContent) {
617		if (captionContent) {
618			captionText.innerHTML = captionContent;
619		}
620		changeCSS(
621			caption,
622			("opacity:" + (captionContent ? "1;pointer-events:auto" : '0'))
623		);
624	}
625
626	function addToImgCache(url) {
627		!~imgCache.indexOf(url) && imgCache.push(url);
628	}
629
630	// animate open of image / video; display caption if needed
631	function open(err) {
632		// hide loading spinner
633		isLoading && toggleLoadingIcon();
634
635		// execute animationStart callback
636		animationStart && animationStart();
637
638		// check if we have an error string instead of normal event
639		if (typeof err === 'string') {
640			removeContainer();
641			return opts.onError
642				? opts.onError()
643				: alert(("Error: The requested " + err + " could not be loaded."))
644		}
645
646		// if remote image is loaded, add url to imgCache array
647		remoteImage && addToImgCache(imgSrc);
648
649		// transform displayEl to match trigger el
650		displayElement.style.cssText += getRect();
651
652		// fade in container
653		changeCSS(container, "opacity:1;pointer-events:auto");
654
655		// set animationEnd callback to run after animation ends (cleared if container closed)
656		if (animationEnd) {
657			animationEnd = setTimeout(animationEnd, 410);
658		}
659
660		isOpen = true;
661
662		galleryOpen = !!galleryEls;
663
664		// enlarge displayEl, fade in caption if hasCaption
665		setTimeout(function () {
666			displayElement.style.cssText += 'transition:transform .35s;transform:none';
667			captionContent && setTimeout(toggleCaption, 250, captionContent);
668		}, 60);
669	}
670
671	// close active display element
672	function close(e) {
673		var target = e ? e.target : container;
674		var clickEls = [
675			caption,
676			captionHideButton,
677			displayVideo,
678			displayAudio,
679			captionText,
680			leftArrowBtn,
681			rightArrowBtn,
682			loadingIcon ];
683
684		// blur to hide close button focus style
685		target.blur();
686
687		// don't close if one of the clickEls was clicked or container is already closing
688		if (isClosing || ~clickEls.indexOf(target)) {
689			return
690		}
691
692		// animate closing
693		displayElement.style.cssText += getRect();
694		changeCSS(container, 'pointer-events:auto');
695
696		// timeout to remove els from dom; use variable to avoid calling more than once
697		setTimeout(removeContainer, 350);
698
699		// clear animationEnd timeout
700		clearTimeout(animationEnd);
701
702		isOpen = false;
703		isClosing = true;
704	}
705
706	// remove container / display element from the DOM
707	function removeContainer() {
708		// clear src of displayElement (or iframe if display el is iframe container)
709		// needs to be done before removing container in IE
710		var srcEl =
711			displayElement === iframeContainer ? iframeSiteVid : displayElement;
712		srcEl.removeAttribute('src');
713
714		// remove container from DOM & clear inline style
715		document.body[removeEl](container);
716		container[removeEl](displayElement);
717		changeCSS(container, '');
718		changeCSS(displayElement, '');
719
720		// remove caption
721		toggleCaption(false);
722
723		if (galleryOpen) {
724			// remove all gallery stuff
725			var images = container.querySelectorAll('img');
726			for (var i = 0; i < images.length; i++) {
727				container[removeEl](images[i]);
728			}
729			isLoading && container[removeEl](loadingIcon);
730			container[removeEl](galleryCounter);
731			galleryOpen = galleryEls = false;
732			preloadedImages = {};
733			supportsTouch || container[removeEl](rightArrowBtn);
734			supportsTouch || container[removeEl](leftArrowBtn);
735			// in case displayimage changed, we need to update event listeners
736			displayImage.onload = open;
737			displayImage.onerror = open.bind(null, 'image');
738		}
739
740		// run close callback
741		opts.onClose && opts.onClose();
742
743		isClosing = isLoading = false;
744	}
745
746	// style helper functions
747	function changeCSS(ref, newStyle) {
748		var style = ref.style;
749
750		style.cssText = newStyle;
751	}
752
753	return BigPicture;
754
755}());

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.