diff --git a/index.html b/index.html
index f319207fa..95c68bfcf 100644
--- a/index.html
+++ b/index.html
@@ -78,6 +78,7 @@
@@ -100,7 +101,8 @@
{
src: 'https://farm7.staticflickr.com/6175/6176698785_7dee72237e_b.jpg',
w: 1024,
- h: 683
+ h: 683,
+ title: 'First caption. Image height and width defined. Images are displayed at their highest possible size and are not limited by the width of the column or wrapper. Each image is isolated from the other content and fits the viewport vertically, so that the user can focus on it. If an image is larger than the viewport, it can be zoomed (which most galleries are unable to do)'
},
// Auto sized images
{
@@ -111,7 +113,7 @@
{
src: 'https://farm6.staticflickr.com/5591/15008867125_b61960af01_h.jpg',
autoSize: true,
- title: 'Another example caption. Image height and width defined also via "autoSize: true"'
+ title: 'Another example caption. Image height and width defined also via "autoSize: true". Images are displayed at their highest possible size and are not limited by the width of the column or wrapper. Each image is isolated from the other content and fits the viewport vertically, so that the user can focus on it. If an image is larger than the viewport, it can be zoomed (which most galleries are unable to do). Images are displayed at their highest possible size and are not limited by the width of the column or wrapper. Each image is isolated from the other content and fits the viewport vertically, so that the user can focus on it. If an image is larger than the viewport, it can be zoomed (which most galleries are unable to do). Images are displayed at their highest possible size and are not limited by the width of the column or wrapper. Each image is isolated from the other content and fits the viewport vertically, so that the user can focus on it. If an image is larger than the viewport, it can be zoomed (which most galleries are unable to do). Images are displayed at their highest possible size and are not limited by the width of the column or wrapper. Each image is isolated from the other content and fits the viewport vertically, so that the user can focus on it. If an image is larger than the viewport, it can be zoomed (which most galleries are unable to do)'
}
];
@@ -132,6 +134,7 @@
closeEl: true,
captionEl: true,
+ allowLongCaptions: true,
fullscreenEl: true,
zoomEl: true,
shareEl: true,
diff --git a/src/css/default-skin/default-skin.scss b/src/css/default-skin/default-skin.scss
index a7bafd5ce..96f04587d 100644
--- a/src/css/default-skin/default-skin.scss
+++ b/src/css/default-skin/default-skin.scss
@@ -142,6 +142,15 @@
width: 70px;
height: 100px;
position: absolute;
+ z-index: 1;
+}
+/* Buttons should appear above expanded caption overlay if viewport wider than 540px as the
+the maximum width of the caption is 480px plus 30px each side. */
+@media only screen and (min-width: 540px) {
+ .pswp__button--arrow--left,
+ .pswp__button--arrow--right {
+ z-index: 3;
+ }
}
.pswp__button--arrow--left {
@@ -329,6 +338,7 @@ a.pswp__share--download {
.pswp__caption {
position: absolute;
+ z-index: 2;
left: 0;
bottom: 0;
width: 100%;
@@ -344,10 +354,71 @@ a.pswp__share--download {
text-align: center;
max-width: 420px;
margin: 0 auto;
- font-size: 13px;
- padding: 10px;
+ font-size: 15px;
+ padding: 18px 10px 10px 10px;
line-height: 20px;
- color: #ccc;
+ color: #fff;
+ background-color: #000;
+ transition: height 250ms ease-out;
+
+ a {
+ color: #ccf;
+ }
+ a:hover {
+ color: #fff;
+ }
+
+ p {
+ margin: 0 0 1em 10px;
+ }
+
+ p:last-child::after {
+ display: inline-block;
+ height: 10px;
+ width: 10px;
+ background-color: white;
+ margin-left: 6px;
+ margin-bottom: -1px;
+ content: ' ';
+ }
+}
+
+.pswp__button--caption--ctrl {
+ display: none;
+ width: 36px;
+ height: 36px;
+ background-image: none;
+ border-radius: 18px;
+ border: none;
+ overflow: hidden;
+ position: absolute;
+ top: -18px;
+ left: 50%;
+ transform: translateX(-50%);
+ text-decoration: none;
+ opacity: 1;
+}
+
+.pswp__button--caption--ctrl.pswp__button--caption--ctrl--expand {
+ background-image: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Ik0xMiAwYzYuNjIzIDAgMTIgNS4zNzcgMTIgMTJzLTUuMzc3IDEyLTEyIDEyLTEyLTUuMzc3LTEyLTEyIDUuMzc3LTEyIDEyLTEyem0wIDFjNi4wNzEgMCAxMSA0LjkyOSAxMSAxMXMtNC45MjkgMTEtMTEgMTEtMTEtNC45MjktMTEtMTEgNC45MjktMTEgMTEtMTF6bTUuMjQ3IDE1bC01LjI0Ny02LjQ0LTUuMjYzIDYuNDQtLjczNy0uNjc4IDYtNy4zMjIgNiA3LjMzNS0uNzUzLjY2NXoiLz48L3N2Zz4=');
+}
+
+.pswp__button--caption--ctrl.pswp__button--caption--ctrl--collapse {
+ background-image: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Ik0xMiAwYzYuNjIzIDAgMTIgNS4zNzcgMTIgMTJzLTUuMzc3IDEyLTEyIDEyLTEyLTUuMzc3LTEyLTEyIDUuMzc3LTEyIDEyLTEyem0wIDFjNi4wNzEgMCAxMSA0LjkyOSAxMSAxMXMtNC45MjkgMTEtMTEgMTEtMTEtNC45MjktMTEtMTEgNC45MjktMTEgMTEtMTF6bTUuMjQ3IDhsLTUuMjQ3IDYuNDQtNS4yNjMtNi40NC0uNzM3LjY3OCA2IDcuMzIyIDYtNy4zMzUtLjc1My0uNjY1eiIvPjwvc3ZnPg==');
+}
+
+/* This must come after setting the background above */
+.pswp__button--caption--ctrl.pswp__button--caption--ctrl--expand,
+.pswp__button--caption--ctrl.pswp__button--caption--ctrl--collapse {
+ display: block;
+ width: 36px;
+ height: 36px;
+ background-color: #ccc;
+ background-size: 36px 36px;
+}
+.pswp__button--caption--ctrl.pswp__button--caption--ctrl--expand:hover,
+.pswp__button--caption--ctrl.pswp__button--caption--ctrl--collapse:hover {
+ background-color: #fff;
}
.pswp__caption--empty {
@@ -494,6 +565,7 @@ a.pswp__share--download {
/* top black bar with buttons and "1 of X" indicator */
.pswp__top-bar {
position: absolute;
+ z-index: 1;
left: 0;
top: 0;
height: 44px;
diff --git a/src/js/core.js b/src/js/core.js
index 96057faf5..b8f6c597d 100644
--- a/src/js/core.js
+++ b/src/js/core.js
@@ -19,8 +19,8 @@ var _options = {
mouseUsed: false,
loop: true,
pinchToClose: true,
- closeOnScroll: true,
- closeOnVerticalDrag: true,
+ closeOnScroll: true, // Will be overridden if allowLongCaptions is true
+ closeOnVerticalDrag: true, // Will be overridden if allowLongCaptions is true
verticalDragRange: 0.75,
hideAnimationDuration: 333,
showAnimationDuration: 333,
@@ -319,7 +319,12 @@ var _isOpen,
s.height = h + 'px';
s.left = item.initialPosition.x + 'px';
s.top = item.initialPosition.y + 'px';
+
+ item.zoom = zoomRatio;
+ item.apparentImageHeight = h;
+ item.imageFromTop = item.initialPosition;
};
+
_applyCurrentZoomPan = function () {
if (_currZoomElementStyle) {
var s = _currZoomElementStyle,
@@ -345,6 +350,21 @@ var _isOpen,
keydownAction = 'prev';
} else if (e.keyCode === 39) {
keydownAction = 'next';
+ } else if (e.keyCode === 13 || e.keyCode === 32) {
+ /* Enter or spacebar */
+ var btnCaptionCtrl = document.getElementById('pswp__button--caption--ctrl');
+ if (btnCaptionCtrl) {
+ if (
+ btnCaptionCtrl.classList.contains('pswp__button--caption--ctrl--expand') ||
+ btnCaptionCtrl.classList.contains('pswp__button--caption--ctrl--collapse')
+ ) {
+ // Add tabindex to the caption div so that it can take focus and be controlled by up/down arrows
+ var innerCaptionElement = btnCaptionCtrl.parentNode.querySelector('.pswp__caption__center');
+ innerCaptionElement.setAttribute('tabindex', '-1');
+ innerCaptionElement.focus();
+ keydownAction = 'toggleCaption';
+ }
+ }
}
}
@@ -672,8 +692,8 @@ var publicMethods = {
framework.unbind(window, 'scroll', self);
_stopDragUpdateLoop();
-
_stopAllAnimations();
+ self.ui.resetCaption();
_listeners = {};
},
@@ -796,6 +816,10 @@ var publicMethods = {
self.updateSize(true);
},
+ toggleCaption: function (el) {
+ self.ui.toggleCaption(el);
+ },
+
// update current zoom/pan objects
updateCurrZoomItem: function (emulateSetContent) {
if (emulateSetContent) {
diff --git a/src/js/desktop-zoom.js b/src/js/desktop-zoom.js
index f68fb9b3c..2a5504ad7 100644
--- a/src/js/desktop-zoom.js
+++ b/src/js/desktop-zoom.js
@@ -83,6 +83,28 @@ _registerModule('DesktopZoom', {
},
handleMouseWheel: function (e) {
+ // If scrolling down on a collapsed long caption, expand the caption
+ var _target = e.target || e.srcElement;
+ var targetCaption = _target.closest('.pswp__caption');
+ if (targetCaption) {
+ var toggleCaptionBtn = targetCaption.querySelector('.pswp__button--caption--ctrl');
+ if (toggleCaptionBtn) {
+ if (toggleCaptionBtn.classList.contains('pswp__button--caption--ctrl--expand') && e.wheelDeltaY < -50) {
+ self.ui.toggleCaption(toggleCaptionBtn);
+ } else if (toggleCaptionBtn.classList.contains('pswp__button--caption--ctrl--collapse')) {
+ // Collapse the caption if scrolled to the top and user scrolls further
+ var innerCaptionElement = targetCaption.querySelector('.pswp__caption__center');
+ if (innerCaptionElement.scrollTop == 0 && e.wheelDeltaY > 50) {
+ self.ui.toggleCaption(toggleCaptionBtn);
+ }
+ } else {
+ e.preventDefault();
+ }
+
+ return;
+ }
+ }
+
if (_currZoomLevel <= self.currItem.fitRatio) {
if (_options.modal) {
if (!_options.closeOnScroll || _numAnimations || _isDragging) {
diff --git a/src/js/framework-bridge.js b/src/js/framework-bridge.js
index c0bdb12c2..da3aef173 100644
--- a/src/js/framework-bridge.js
+++ b/src/js/framework-bridge.js
@@ -19,7 +19,7 @@ var framework = {
isArray: function (obj) {
return obj instanceof Array;
},
- createEl: function (classes, tag) {
+ createElement: function (classes, tag) {
var el = document.createElement(tag || 'div');
if (classes) {
el.className = classes;
@@ -133,7 +133,7 @@ var framework = {
if (framework.features) {
return framework.features;
}
- var helperEl = framework.createEl(),
+ var helperEl = framework.createElement(),
helperStyle = helperEl.style,
vendor = '',
features = {};
@@ -277,3 +277,21 @@ if (framework.features.oldIE) {
}
};
}
+
+// Polyfill for closest() for IE11 from https://developer.mozilla.org/en-US/docs/Web/API/Element/closest.
+// Not bothering with versions older than 11 and it is debatable whether we should worry even about 11 now.
+if (!Element.prototype.matches) {
+ Element.prototype.matches = Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector;
+}
+
+if (!Element.prototype.closest) {
+ Element.prototype.closest = function (s) {
+ var el = this;
+
+ do {
+ if (Element.prototype.matches.call(el, s)) return el;
+ el = el.parentElement || el.parentNode;
+ } while (el !== null && el.nodeType === 1);
+ return null;
+ };
+}
diff --git a/src/js/gestures.js b/src/js/gestures.js
index 2bb73150c..af549bbdc 100644
--- a/src/js/gestures.js
+++ b/src/js/gestures.js
@@ -47,6 +47,7 @@ var _gestureStartTime,
_opacityChanged,
_bgOpacity,
_wasOverInitialZoom,
+ _target,
_isEqualPoints = function (p1, p2) {
return p1.x === p2.x && p1.y === p2.y;
},
@@ -288,6 +289,8 @@ var _gestureStartTime,
_shout('pointerDown');
+ _target = e.target || e.srcElement;
+
if (_pointerEventEnabled) {
var pointerIndex = framework.arraySearch(_currPointers, e.pointerId, 'id');
if (pointerIndex < 0) {
@@ -307,6 +310,7 @@ var _gestureStartTime,
if (!_isDragging || numPoints === 1) {
_isDragging = _isFirstMove = true;
framework.bind(window, _upMoveEvents, self);
+ // framework.bind(window, _downEvents, self); // I thought this might be needed to collapse caption but it made no difference.
_isZoomingIn = _wasOverInitialZoom = _opacityChanged = _verticalDragInitiated = _mainScrollShifted = _moved = _isMultitouch = _zoomStarted = false;
@@ -521,6 +525,29 @@ var _gestureStartTime,
return;
}
+ /* **************************************************************
+ Commenting this section out because it does not work reliably
+ especially when swiping down in an attempt to close the caption.
+ I'd be grateful if anyone can figure out why and fix it.
+ *****************************************************************
+ // If dragging up on a collapsed long caption, expand the caption;
+ // If dragging down on expanded long caption when at the top, collapse the caption.
+ if(_direction === 'v' && _options.allowLongCaptions) {
+ var targetCaption = _target.closest(".pswp__caption");
+ if(targetCaption) {
+ var toggleCaptionBtn = targetCaption.querySelector(".pswp__button--caption--ctrl");
+ var isExpanded = toggleCaptionBtn.classList.contains("pswp__button--caption--ctrl--collapse");
+ var isCollapsed = toggleCaptionBtn.classList.contains("pswp__button--caption--ctrl--expand");
+ var innerCaptionElement = targetCaption.querySelector(".pswp__caption__center");
+ if((delta.y < -DIRECTION_CHECK_OFFSET && isCollapsed) ||
+ (delta.y > DIRECTION_CHECK_OFFSET && isExpanded && innerCaptionElement.scrollTop === 0)) {
+ self.ui.toggleCaption(toggleCaptionBtn);
+ return;
+ }
+ }
+ }
+ */
+
if (_direction === 'v' && _options.closeOnVerticalDrag) {
if (!_canPan()) {
_currPanDist.y += delta.y;
@@ -640,6 +667,7 @@ var _gestureStartTime,
if (numPoints === 0) {
_isDragging = false;
framework.unbind(window, _upMoveEvents, self);
+ // framework.unbind(window, _downEvents, self); // I thought this might be needed to collapse caption but it made no difference.
_stopDragUpdateLoop();
diff --git a/src/js/items-controller.js b/src/js/items-controller.js
index 096b0f8dd..1afacb8f3 100644
--- a/src/js/items-controller.js
+++ b/src/js/items-controller.js
@@ -66,7 +66,6 @@ var _getItemAt,
var vRatio = _tempPanAreaSize.y / item.h;
item.fitRatio = hRatio < vRatio ? hRatio : vRatio;
- //item.fillRatio = hRatio > vRatio ? hRatio : vRatio;
var scaleMode = _options.scaleMode;
@@ -133,7 +132,7 @@ var _getItemAt,
_preloadImage = function (item) {
item.loading = true;
item.loaded = false;
- var img = (item.img = framework.createEl('pswp__img', 'img'));
+ var img = (item.img = framework.createElement('pswp__img', 'img'));
var onComplete = function () {
item.loading = false;
item.loaded = true;
@@ -316,7 +315,7 @@ _registerModule('Controller', {
allowProgressiveImg: function () {
// 1. Progressive image loading isn't working on webkit/blink
// when hw-acceleration (e.g. translateZ) is applied to IMG element.
- // That's why in PhotoSwipe parent element gets zoom transform, not image itself.
+ // That's why in PhotoSwipe parent element (.pwsp__zoom-wrap) gets zoom transform, not image itself.
//
// 2. Progressive image loading sometimes blinks in webkit/blink when applying animation to parent element.
// That's why it's disabled on touch devices (mainly because of swipe transition)
@@ -339,7 +338,8 @@ _registerModule('Controller', {
}
var item = self.getItemAt(index),
- img;
+ img,
+ imageSize;
if (!item) {
framework.resetEl(holder.el);
@@ -353,7 +353,7 @@ _registerModule('Controller', {
holder.item = item;
// base container DIV is created only once for each of 3 holders
- var baseDiv = (item.container = framework.createEl('pswp__zoom-wrap'));
+ var baseDiv = (item.container = framework.createElement('pswp__zoom-wrap'));
if (!item.src && item.html) {
if (item.html.tagName) {
@@ -380,7 +380,6 @@ _registerModule('Controller', {
item.loadComplete = item.img = null;
_calculateItemSize(item, _viewportSize);
_applyZoomPanToItem(item);
-
if (holder.index === _currentItemIndex) {
// recalculate dimensions
self.updateCurrZoomItem();
@@ -419,12 +418,12 @@ _registerModule('Controller', {
var placeholderClassName = 'pswp__img pswp__img--placeholder';
placeholderClassName += item.msrc ? '' : ' pswp__img--placeholder--blank';
- var placeholder = framework.createEl(placeholderClassName, item.msrc ? 'img' : '');
+ var placeholder = framework.createElement(placeholderClassName, item.msrc ? 'img' : '');
if (item.msrc) {
placeholder.src = item.msrc;
}
- _setImageSize(item, placeholder);
+ imageSize = _setImageSize(item, placeholder);
baseDiv.appendChild(placeholder);
item.placeholder = placeholder;
@@ -450,10 +449,10 @@ _registerModule('Controller', {
}
} else if (item.src && !item.loadError) {
// image object is created every time, due to bugs of image loading & delay when switching images
- img = framework.createEl('pswp__img', 'img');
+ img = framework.createElement('pswp__img', 'img');
img.style.opacity = 1;
img.src = item.src;
- _setImageSize(item, img);
+ imageSize = _setImageSize(item, img);
_appendImage(index, item, baseDiv, img, true);
}
diff --git a/src/js/ui/photoswipe-ui-default.js b/src/js/ui/photoswipe-ui-default.js
index fb6ea695f..9ac0d659b 100644
--- a/src/js/ui/photoswipe-ui-default.js
+++ b/src/js/ui/photoswipe-ui-default.js
@@ -38,6 +38,9 @@
_loadingIndicatorHidden,
_loadingIndicatorTimeout,
_galleryHasOneSlide,
+ _stylesheet,
+ _ruleExpanded,
+ _ruleCollapsed,
_options,
_defaultUIOptions = {
barsSize: {top: 44, bottom: 'auto'},
@@ -46,17 +49,52 @@
timeToIdleOutside: 1000,
loadingIndicatorDelay: 1000, // 2s
- addCaptionHTMLFn: function (item, captionEl /*, isFake */) {
+ addCaptionHTMLFn: function (item, captionElement /*, isFake */) {
+ var captionCtrl = pswp.scrollWrap.querySelector('.pswp__button--caption--ctrl');
+ var captionElement = captionCtrl.parentNode;
+ var innerCaptionElement = captionElement.querySelector('.pswp__caption__center');
+ var imagePositionTop;
+ if (item.initialPosition) {
+ imagePositionTop = item.initialPosition.y;
+ } else {
+ imagePositionTop = 0;
+ }
+ var apparentImageHeight = Math.round(item.h * item.initialZoomLevel);
+ var gapTop = item.vGap;
+ var naturalCaptionHeight = innerCaptionElement.clientHeight;
+ var layoutData = _getLayoutData(captionElement);
+
if (!item.title) {
- framework.resetEl(captionEl.firstChild);
+ innerCaptionElement.innerHTML = '';
return false;
}
- captionEl.children[0].innerHTML = item.title;
+ innerCaptionElement.innerHTML = item.title;
+
+ // If allowLongCaptions is true, position caption just under picture and show "Expand" button if necessary
+ if (_options.allowLongCaptions) {
+ ui.resetCaption();
+ _setLayoutData(captionElement, imagePositionTop, apparentImageHeight, gapTop, naturalCaptionHeight);
+
+ // Show the 'expand' control only if caption extends out of view. Reset height first.
+ if (naturalCaptionHeight - 10 > layoutData.maxCollapsedCaptionHeight) {
+ captionCtrl.classList.add('pswp__button--caption--ctrl--expand');
+ captionCtrl.setAttribute('aria-controls', 'pswp__caption__center');
+
+ innerCaptionElement.setAttribute('aria-expanded', 'false');
+ } else {
+ _resetCaption(captionCtrl);
+ }
+
+ _ruleCollapsed.style.height = layoutData.maxCollapsedCaptionHeight + 'px';
+ innerCaptionElement.classList.add('collapsed');
+ }
+
return true;
},
closeEl: true,
captionEl: true,
+ allowLongCaptions: false,
fullscreenEl: true,
zoomEl: true,
shareEl: true,
@@ -98,6 +136,90 @@
_blockControlsTap,
_blockControlsTapTimeout;
+ // Write key layout dimensions as data attributes on the caption element
+ var _setLayoutData = function (captionElement, imagePositionTop, apparentImageHeight, gapTop, naturalCaptionHeight) {
+ captionElement.dataset.imagePositionTop = imagePositionTop;
+ captionElement.dataset.apparentImageHeight = apparentImageHeight;
+ captionElement.dataset.gapTop = gapTop;
+ captionElement.dataset.naturalCaptionHeight = naturalCaptionHeight;
+ };
+
+ var _getLayoutData = function (captionElement) {
+ var layoutData = {};
+
+ // Read data attributes on the caption element
+ layoutData.gapTop = parseInt(captionElement.dataset.gapTop, 10);
+ layoutData.imagePositionTop = parseInt(captionElement.dataset.imagePositionTop, 10);
+ layoutData.apparentImageHeight = parseInt(captionElement.dataset.apparentImageHeight, 10);
+ layoutData.naturalCaptionHeight = parseInt(captionElement.dataset.naturalCaptionHeight, 10);
+
+ var imageBottomEdgeFromTop = layoutData.imagePositionTop + layoutData.apparentImageHeight;
+ layoutData.maxCollapsedCaptionHeight = window.innerHeight - imageBottomEdgeFromTop;
+ layoutData.maxExpandedCaptionHeight = window.innerHeight - layoutData.gapTop;
+
+ return layoutData;
+ };
+
+ var _resetCaption = function (captionCtrl) {
+ if (!captionCtrl) {
+ captionCtrl = pswp.scrollWrap.querySelector('.pswp__button--caption--ctrl');
+ }
+
+ captionCtrl.classList.remove('pswp__button--caption--ctrl--expand');
+ captionCtrl.classList.remove('pswp__button--caption--ctrl--collapse');
+ captionCtrl.removeAttribute('aria-controls');
+
+ var innerCaptionElement = captionCtrl.parentNode.querySelector('.pswp__caption__center');
+ innerCaptionElement.removeAttribute('aria-expanded');
+ innerCaptionElement.classList.remove('expanded');
+ innerCaptionElement.classList.remove('collapsed');
+
+ _ruleExpanded.style.height = 'auto';
+ _ruleCollapsed.style.height = 'auto';
+ };
+
+ var _toggleCaption = function (captionCtrl) {
+ if (!captionCtrl) {
+ captionCtrl = pswp.scrollWrap.querySelector('.pswp__button--caption--ctrl');
+ }
+
+ var captionElement = captionCtrl.parentNode;
+ var innerCaptionElement = captionElement.querySelector('.pswp__caption__center');
+ var layoutData = _getLayoutData(captionElement);
+
+ if (captionCtrl.classList.contains('pswp__button--caption--ctrl--expand')) {
+ // Expand caption
+ if (layoutData.naturalCaptionHeight < layoutData.maxExpandedCaptionHeight) {
+ // It fits in space below top bar
+ _ruleExpanded.style.height = layoutData.naturalCaptionHeight + 'px';
+ } else {
+ // Caption is taller than the space available
+ _ruleExpanded.style.height = layoutData.maxExpandedCaptionHeight + 'px';
+ innerCaptionElement.style.overflowY = 'auto';
+ }
+
+ captionCtrl.classList.remove('pswp__button--caption--ctrl--expand');
+ captionCtrl.classList.add('pswp__button--caption--ctrl--collapse');
+ captionCtrl.setAttribute('title', 'Collapse caption');
+
+ innerCaptionElement.classList.remove('collapsed');
+ innerCaptionElement.classList.add('expanded');
+ innerCaptionElement.setAttribute('aria-expanded', 'true');
+ } else {
+ // Collapse caption
+ _ruleCollapsed.style.height = layoutData.maxCollapsedCaptionHeight + 'px';
+
+ captionCtrl.classList.add('pswp__button--caption--ctrl--expand');
+ captionCtrl.classList.remove('pswp__button--caption--ctrl--collapse');
+ captionCtrl.setAttribute('title', 'Expand caption');
+
+ innerCaptionElement.style.overflowY = 'hidden';
+ innerCaptionElement.classList.remove('expanded');
+ innerCaptionElement.classList.add('collapsed');
+ innerCaptionElement.setAttribute('aria-expanded', 'false');
+ }
+ };
+
var _onControlsTap = function (e) {
if (_blockControlsTap) {
return true;
@@ -118,11 +240,16 @@
for (var i = 0; i < _uiElements.length; i++) {
uiElement = _uiElements[i];
if (uiElement.onTap && clickedClass.indexOf('pswp__' + uiElement.name) > -1) {
- uiElement.onTap();
+ uiElement.onTap(target);
found = true;
}
}
+ // Long captions will contain HTML so caption element will be an ancestor of target
+ if (target.closest('.pswp__caption__center')) {
+ found = true;
+ }
+
if (found) {
if (e.stopPropagation) {
e.stopPropagation();
@@ -142,7 +269,9 @@
}
},
_fitControlsInViewport = function () {
- return !pswp.likelyTouchDevice || _options.mouseUsed || screen.width > _options.fitControlsWidth;
+ return (
+ !pswp.likelyTouchDevice || _options.mouseUsed || screen.width > _options.fitControlsWidth || _options.allowLongCaptions
+ );
},
_togglePswpClass = function (el, cName, add) {
framework[(add ? 'add' : 'remove') + 'Class'](el, 'pswp__' + cName);
@@ -345,8 +474,8 @@
if (_fitControlsInViewport()) {
if (_options.captionEl && bars.bottom === 'auto') {
if (!_fakeCaptionContainer) {
- _fakeCaptionContainer = framework.createEl('pswp__caption pswp__caption--fake');
- _fakeCaptionContainer.appendChild(framework.createEl('pswp__caption__center'));
+ _fakeCaptionContainer = framework.createElement('pswp__caption pswp__caption--fake');
+ _fakeCaptionContainer.appendChild(framework.createElement('pswp__caption__center'));
_controls.insertBefore(_fakeCaptionContainer, _captionContainer);
framework.addClass(_controls, 'pswp__ui--fit');
}
@@ -382,9 +511,23 @@
});
}
},
+ _overrideOptionsIfAllowLongCaptionsTrue = function () {
+ if (_options.closeOnScroll) {
+ console.info('FYI: Resetting _options.closeOnScroll to false because _options.allowLongCaptions is true.');
+ _options.closeOnScroll = false;
+ }
+ if (_options.closeOnVerticalDrag) {
+ console.info('FYI: Resetting _options.closeOnVerticalDrag to false because _options.allowLongCaptions is true.');
+ _options.closeOnVerticalDrag = false;
+ }
+ },
_setupHidingControlsDuringGestures = function () {
// Hide controls on vertical drag
_listen('onVerticalDrag', function (now) {
+ if (_options.allowLongCaptions) {
+ return;
+ }
+
if (_controlsVisible && now < 0.95) {
ui.hideControls();
} else if (!_controlsVisible && now >= 0.95) {
@@ -411,6 +554,31 @@
});
};
+ // From https://davidwalsh.name/add-rules-stylesheets
+ var _createStylesheet = function () {
+ var style = document.createElement('style');
+ style.appendChild(document.createTextNode(''));
+ document.head.appendChild(style);
+ return style.sheet;
+ };
+
+ var _createStylesForLongCaptions = function () {
+ // Make a new stylesheet since there will be cross-site security issues if referencing a stylesheet on CDN
+ _stylesheet = _createStylesheet();
+
+ // From https://davidwalsh.name/add-rules-stylesheets
+ // We insert an empty rule just to create a new CSSStyleRule object. The second param is the index to
+ // insert at using the length property we effectively "append" the rule to the end of the sheet.
+ var ruleExpandedIndex = _stylesheet.insertRule('.pswp__caption__center.expanded {}', _stylesheet.cssRules.length);
+ var ruleCollapsedIndex = _stylesheet.insertRule('.pswp__caption__center.collapsed {}', _stylesheet.cssRules.length);
+ _ruleExpanded = _stylesheet.cssRules.item(ruleExpandedIndex);
+ _ruleCollapsed = _stylesheet.cssRules.item(ruleCollapsedIndex);
+
+ // While we are here, increase the width of the caption. It is very narrow which keeps it roughly centered
+ // if there are only a few words but it looks odd when the photo is wide and the caption is long.
+ _stylesheet.insertRule('.pswp__caption__center { width: 100%; max-width: 720px; }', _stylesheet.cssRules.length);
+ };
+
var _uiElements = [
{
name: 'caption',
@@ -493,6 +661,13 @@
}
}
},
+ {
+ name: 'button--caption--ctrl',
+ option: 'allowLongCaptions',
+ onTap: function (el) {
+ _toggleCaption(el);
+ }
+ },
{
name: 'preloader',
option: 'preloaderEl',
@@ -502,6 +677,19 @@
}
];
+ var _ensureCaptionCtrlExists = function () {
+ // Ensure that there is a button to toggle the caption
+ var captionElement = document.querySelector('.pswp__caption');
+ if (!captionElement.querySelector('.pswp__button--caption--ctrl')) {
+ var btn = document.createElement('button');
+ var innerCaptionElement = captionElement.querySelector('.pswp__caption__center');
+ btn.setAttribute('class', 'pswp__button pswp__button--caption--ctrl');
+ btn.setAttribute('id', 'pswp__button--caption--ctrl');
+ btn.setAttribute('title', 'Expand caption');
+ captionElement.insertBefore(btn, innerCaptionElement);
+ }
+ };
+
var _setupUIElements = function () {
var item, classAttr, uiElement;
@@ -542,6 +730,8 @@
if (topBar) {
loopThroughChildElements(topBar.children);
}
+
+ _ensureCaptionCtrlExists();
};
ui.init = function () {
@@ -557,6 +747,7 @@
// create local link
_listen = pswp.listen;
+ _overrideOptionsIfAllowLongCaptionsTrue();
_setupHidingControlsDuringGestures();
// update controls when slides change
@@ -664,6 +855,10 @@
_setupFullscreenAPI();
_setupLoadingIndicator();
+
+ if (_options.allowLongCaptions) {
+ _createStylesForLongCaptions();
+ }
};
ui.setIdle = function (isIdle) {
@@ -755,8 +950,10 @@
}
}
} else {
- // tap anywhere (except buttons) to toggle visibility of controls
- if (_options.tapToToggleControls) {
+ // Tap anywhere (except buttons and caption) to toggle visibility of controls
+ // Since the caption may now contain other elements, have to check if target is in caption
+ var targetCaption = target.closest('.pswp__caption');
+ if (_options.tapToToggleControls && !targetCaption) {
if (_controlsVisible) {
ui.hideControls();
} else {
@@ -859,6 +1056,14 @@
return api;
};
+
+ ui.toggleCaption = function (el) {
+ _toggleCaption(el);
+ };
+
+ ui.resetCaption = function (el) {
+ _resetCaption(el);
+ };
};
return PhotoSwipeUI_Default;
});