intranet-pictures 1.0.6 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/intranet/pictures/responder.rb +27 -18
- data/lib/intranet/pictures/version.rb +1 -1
- data/lib/intranet/resources/haml/pictures_browse.haml +0 -1
- data/lib/intranet/resources/haml/pictures_home.haml +0 -3
- data/lib/intranet/resources/locales/en.yml +0 -1
- data/lib/intranet/resources/locales/fr.yml +0 -1
- data/lib/intranet/resources/www/jpictures.js +32 -14
- data/lib/intranet/resources/www/photoswipe/photoswipe-dynamic-caption-plugin.css +47 -0
- data/lib/intranet/resources/www/photoswipe/photoswipe-dynamic-caption-plugin.esm.js +400 -0
- data/lib/intranet/resources/www/photoswipe/photoswipe-lightbox.esm.js +1382 -0
- data/lib/intranet/resources/www/photoswipe/photoswipe-lightbox.esm.js.map +1 -0
- data/lib/intranet/resources/www/photoswipe/photoswipe-lightbox.esm.min.js +5 -0
- data/lib/intranet/resources/www/photoswipe/photoswipe.css +383 -142
- data/lib/intranet/resources/www/photoswipe/photoswipe.esm.js +5279 -0
- data/lib/intranet/resources/www/photoswipe/photoswipe.esm.js.map +1 -0
- data/lib/intranet/resources/www/photoswipe/photoswipe.esm.min.js +5 -0
- data/lib/intranet/resources/www/style.css +13 -0
- data/spec/intranet/pictures/responder_spec.rb +78 -58
- metadata +26 -28
- data/lib/intranet/resources/haml/pictures_photoswipe.haml +0 -23
- data/lib/intranet/resources/www/photoswipe/LICENSE +0 -21
- data/lib/intranet/resources/www/photoswipe/default-skin/default-skin.css +0 -484
- data/lib/intranet/resources/www/photoswipe/default-skin/default-skin.png +0 -0
- data/lib/intranet/resources/www/photoswipe/default-skin/default-skin.svg +0 -1
- data/lib/intranet/resources/www/photoswipe/default-skin/preloader.gif +0 -0
- data/lib/intranet/resources/www/photoswipe/photoswipe-ui-default.js +0 -861
- data/lib/intranet/resources/www/photoswipe/photoswipe-ui-default.min.js +0 -4
- data/lib/intranet/resources/www/photoswipe/photoswipe.js +0 -3734
- data/lib/intranet/resources/www/photoswipe/photoswipe.min.js +0 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 375bdf2fb9b0a0354f3066e44c59a4426077916642a248a9180aabc3842628d5
|
4
|
+
data.tar.gz: 34b5b68ae804b7a441320eee02880726ce8ca85eb15a813f9e49ab99c64491a6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7ba048e77bb9b56447962e17c005c28e14f3ef30795cc218610e485be2ab7ef2da3df9faa0a189704391bae6d55bb75245ba925899093b4d56a58dfdf394cfad
|
7
|
+
data.tar.gz: 99eac1be0abd8986a67e3fe0ea09700a74304a053d3b33cff10c7fe7c30850942232655e48c42d2064de366829a01a835f8fc101e1052d1ec0cd961c82425464
|
@@ -87,6 +87,7 @@ module Intranet
|
|
87
87
|
when %r{^/browse_\w+\.html$}
|
88
88
|
serve_groups(path.gsub(%r{^/browse_(\w+)\.html$}, '\\1'), query)
|
89
89
|
when %r{^/api/} then serve_api(path.gsub(%r{^/api}, ''), query)
|
90
|
+
when %r{^/i18n\.js$} then serve_i18n_js
|
90
91
|
else super(path, query)
|
91
92
|
end
|
92
93
|
end
|
@@ -97,22 +98,6 @@ module Intranet
|
|
97
98
|
@provider.title
|
98
99
|
end
|
99
100
|
|
100
|
-
# Provides the list of Cascade Style Sheets (CSS) dependencies for this module.
|
101
|
-
# @return [Array<String>] The list of CSS dependencies.
|
102
|
-
def css_dependencies
|
103
|
-
super + ['design/style.css',
|
104
|
-
'design/photoswipe/photoswipe.css',
|
105
|
-
'design/photoswipe/default-skin/default-skin.css']
|
106
|
-
end
|
107
|
-
|
108
|
-
# Provides the list of Javascript files (JS) dependencies for this module.
|
109
|
-
# @return [Array<String>] The list of JS dependencies.
|
110
|
-
def js_dependencies
|
111
|
-
super + ['design/jpictures.js',
|
112
|
-
'design/photoswipe/photoswipe.min.js',
|
113
|
-
'design/photoswipe/photoswipe-ui-default.min.js']
|
114
|
-
end
|
115
|
-
|
116
101
|
private
|
117
102
|
|
118
103
|
# Extract a selector from the given +query+.
|
@@ -141,6 +126,19 @@ module Intranet
|
|
141
126
|
raise KeyError # incorrect value for 'sort_order'
|
142
127
|
end
|
143
128
|
|
129
|
+
# Provides the list of required stylesheets.
|
130
|
+
# @return [Array<String>] The list of required stylesheets.
|
131
|
+
def stylesheets
|
132
|
+
['design/style.css', 'design/photoswipe/photoswipe.css',
|
133
|
+
'design/photoswipe/photoswipe-dynamic-caption-plugin.css']
|
134
|
+
end
|
135
|
+
|
136
|
+
# Provides the list of required script files.
|
137
|
+
# @return [Array<Hash<Symbol,String>>] The list of required scripts.
|
138
|
+
def scripts
|
139
|
+
[{ src: 'design/jpictures.js', type: 'module' }]
|
140
|
+
end
|
141
|
+
|
144
142
|
##########################################################################
|
145
143
|
### Servicing of the HTML "display-able" content ###
|
146
144
|
##########################################################################
|
@@ -195,7 +193,8 @@ module Intranet
|
|
195
193
|
|
196
194
|
def serve_home
|
197
195
|
content = to_markup('pictures_home', nav: make_nav)
|
198
|
-
[206, 'text/html',
|
196
|
+
[206, 'text/html',
|
197
|
+
{ content: content, title: title, stylesheets: stylesheets, scripts: scripts }]
|
199
198
|
rescue KeyError
|
200
199
|
[404, '', '']
|
201
200
|
end
|
@@ -204,11 +203,21 @@ module Intranet
|
|
204
203
|
groups = @provider.list_groups(type, selector(query), sort_by(query), sort_order(query))
|
205
204
|
content = to_markup('pictures_browse', nav: make_nav(type, query), group_type: type,
|
206
205
|
filters: selector(query), groups: groups)
|
207
|
-
[206, 'text/html',
|
206
|
+
[206, 'text/html',
|
207
|
+
{ content: content, title: title, stylesheets: stylesheets, scripts: scripts }]
|
208
208
|
rescue KeyError
|
209
209
|
[404, '', '']
|
210
210
|
end
|
211
211
|
|
212
|
+
def serve_i18n_js
|
213
|
+
[200, 'text/javascript',
|
214
|
+
"export default {\n" \
|
215
|
+
" viewer_close: '#{I18n.t('pictures.viewer.close')}',\n" \
|
216
|
+
" viewer_zoom: '#{I18n.t('pictures.viewer.zoom')}',\n" \
|
217
|
+
" viewer_previous: '#{I18n.t('pictures.viewer.previous')}',\n" \
|
218
|
+
" viewer_next: '#{I18n.t('pictures.viewer.next')}' };"]
|
219
|
+
end
|
220
|
+
|
212
221
|
##########################################################################
|
213
222
|
### Servicing of the REST API (raw JSON data & pictures) ###
|
214
223
|
##########################################################################
|
@@ -4,6 +4,14 @@
|
|
4
4
|
*/
|
5
5
|
"use strict";
|
6
6
|
|
7
|
+
// Import PhotoSwipe Lightbox
|
8
|
+
import PhotoSwipe from './photoswipe/photoswipe.esm.min.js';
|
9
|
+
import PhotoSwipeLightbox from './photoswipe/photoswipe-lightbox.esm.min.js';
|
10
|
+
import PhotoSwipeDynamicCaption from './photoswipe/photoswipe-dynamic-caption-plugin.esm.js'
|
11
|
+
|
12
|
+
// Import internationalization support
|
13
|
+
import i18n from './../i18n.js';
|
14
|
+
|
7
15
|
function convertDate(iso8601_date) {
|
8
16
|
const d = new Date(iso8601_date);
|
9
17
|
const str = d.toLocaleDateString() + ' ' + d.toLocaleTimeString().replace(/(\d{2}):(\d{2}):(\d{2})/, '$1h$2');
|
@@ -14,29 +22,39 @@ function createImageGallery(json) {
|
|
14
22
|
var items = [];
|
15
23
|
for (let i = 0; i < json.length; i++) {
|
16
24
|
const imageUrl = 'api/picture?id=' + json[i].id;
|
17
|
-
const imageTitle = convertDate(json[i].datetime, true) + '
|
18
|
-
items.push({ src: imageUrl,
|
25
|
+
const imageTitle = '<strong>' + convertDate(json[i].datetime, true) + '</strong> — ' + json[i].title;
|
26
|
+
items.push({ src: imageUrl, width: json[i].width, height: json[i].height, title: imageTitle});
|
19
27
|
}
|
20
28
|
|
21
|
-
//
|
22
|
-
const
|
29
|
+
// Create PhotoSwipe Lightbox
|
30
|
+
const lightboxOptions = {
|
31
|
+
dataSource: items,
|
32
|
+
pswpModule: PhotoSwipe,
|
33
|
+
bgOpacity: 0.95,
|
34
|
+
closeOnVerticalDrag: false,
|
35
|
+
closeTitle: i18n.viewer_close + ' (Esc)',
|
36
|
+
zoomTitle: i18n.viewer_zoom + ' (z)',
|
37
|
+
arrowPrevTitle: i18n.viewer_previous,
|
38
|
+
arrowNextTitle: i18n.viewer_next,
|
39
|
+
};
|
40
|
+
const lightbox = new PhotoSwipeLightbox(lightboxOptions);
|
23
41
|
|
24
|
-
//
|
25
|
-
const
|
26
|
-
|
27
|
-
|
28
|
-
history: false,
|
29
|
-
closeOnScroll: false,
|
30
|
-
closeOnVerticalDrag: false
|
42
|
+
// Initialize caption plugin
|
43
|
+
const captionPluginOptions = {
|
44
|
+
type: 'below',
|
45
|
+
captionContent: (slide) => { return slide.data.title || ''; }
|
31
46
|
};
|
47
|
+
const captionPlugin = new PhotoSwipeDynamicCaption(lightbox, captionPluginOptions);
|
32
48
|
|
33
|
-
//
|
34
|
-
|
35
|
-
|
49
|
+
// Open gallery
|
50
|
+
lightbox.init();
|
51
|
+
lightbox.loadAndOpen(0); // start at first slide
|
36
52
|
}
|
37
53
|
|
54
|
+
// Export this function so that it may be called from HTML
|
38
55
|
function openImagesGallery(selectors) {
|
39
56
|
fetch('api/pictures?' + selectors) // selectors is not empty
|
40
57
|
.then(response => response.json())
|
41
58
|
.then(data => createImageGallery(data));
|
42
59
|
}
|
60
|
+
window.openImagesGallery = openImagesGallery;
|
@@ -0,0 +1,47 @@
|
|
1
|
+
.pswp__dynamic-caption {
|
2
|
+
color: #fff;
|
3
|
+
position: absolute;
|
4
|
+
width: 100%;
|
5
|
+
left: 0;
|
6
|
+
top: 0;
|
7
|
+
transition: opacity 120ms linear !important; /* override default */
|
8
|
+
}
|
9
|
+
|
10
|
+
.pswp-caption-content {
|
11
|
+
display: none;
|
12
|
+
}
|
13
|
+
|
14
|
+
.pswp__dynamic-caption a {
|
15
|
+
color: #fff;
|
16
|
+
}
|
17
|
+
|
18
|
+
.pswp__dynamic-caption--faded {
|
19
|
+
opacity: 0 !important;
|
20
|
+
}
|
21
|
+
|
22
|
+
.pswp__dynamic-caption--aside {
|
23
|
+
width: auto;
|
24
|
+
max-width: 300px;
|
25
|
+
padding: 20px 15px 20px 20px;
|
26
|
+
margin-top: 70px;
|
27
|
+
}
|
28
|
+
|
29
|
+
.pswp__dynamic-caption--below {
|
30
|
+
width: auto;
|
31
|
+
max-width: 700px;
|
32
|
+
padding: 15px 0 0;
|
33
|
+
}
|
34
|
+
|
35
|
+
.pswp__dynamic-caption--on-hor-edge {
|
36
|
+
padding-left: 15px;
|
37
|
+
padding-right: 15px;
|
38
|
+
}
|
39
|
+
|
40
|
+
.pswp__dynamic-caption--mobile {
|
41
|
+
width: 100%;
|
42
|
+
top: auto;
|
43
|
+
right: 0;
|
44
|
+
bottom: 0;
|
45
|
+
background: rgba(0,0,0,0.5);
|
46
|
+
padding: 10px 15px;
|
47
|
+
}
|
@@ -0,0 +1,400 @@
|
|
1
|
+
/**
|
2
|
+
* PhotoSwipe Dynamic Caption plugin v1.1.0
|
3
|
+
* https://github.com/dimsemenov/photoswipe-dynamic-caption-plugin
|
4
|
+
*
|
5
|
+
* By https://dimsemenov.com
|
6
|
+
*/
|
7
|
+
|
8
|
+
const defaultOptions = {
|
9
|
+
captionContent: '.pswp-caption-content',
|
10
|
+
type: 'auto',
|
11
|
+
horizontalEdgeThreshold: 20,
|
12
|
+
mobileCaptionOverlapRatio: 0.3,
|
13
|
+
mobileLayoutBreakpoint: 600,
|
14
|
+
};
|
15
|
+
|
16
|
+
class PhotoSwipeDynamicCaption {
|
17
|
+
constructor(lightbox, options) {
|
18
|
+
this.options = {
|
19
|
+
...defaultOptions,
|
20
|
+
...options
|
21
|
+
};
|
22
|
+
|
23
|
+
this.lightbox = lightbox;
|
24
|
+
|
25
|
+
this.lightbox.on('init', () => {
|
26
|
+
this.initPlugin();
|
27
|
+
});
|
28
|
+
}
|
29
|
+
|
30
|
+
initPlugin() {
|
31
|
+
this.pswp = this.lightbox.pswp;
|
32
|
+
this.isCaptionHidden = false;
|
33
|
+
this.tempCaption = false;
|
34
|
+
this.captionElement = false;
|
35
|
+
|
36
|
+
this.pswp.on('uiRegister', () => {
|
37
|
+
this.pswp.ui.registerElement({
|
38
|
+
name: 'dynamic-caption',
|
39
|
+
order: 9,
|
40
|
+
isButton: false,
|
41
|
+
appendTo: 'root',
|
42
|
+
html: '',
|
43
|
+
onInit: (el) => {
|
44
|
+
this.captionElement = el;
|
45
|
+
this.initCaption();
|
46
|
+
}
|
47
|
+
});
|
48
|
+
});
|
49
|
+
}
|
50
|
+
|
51
|
+
initCaption() {
|
52
|
+
const { pswp } = this;
|
53
|
+
|
54
|
+
pswp.on('change', () => {
|
55
|
+
this.updateCaptionHTML();
|
56
|
+
this.updateCurrentCaptionPosition();
|
57
|
+
|
58
|
+
// make sure caption is displayed after slides are switched
|
59
|
+
this.showCaption();
|
60
|
+
});
|
61
|
+
|
62
|
+
pswp.on('calcSlideSize', (e) => this.onCalcSlideSize(e));
|
63
|
+
|
64
|
+
// hide caption if mainscroll is shifted (dragging)
|
65
|
+
pswp.on('moveMainScroll', () => {
|
66
|
+
if (!this.useMobileLayout()) {
|
67
|
+
if (this.pswp.mainScroll.isShifted()) {
|
68
|
+
this.hideCaption();
|
69
|
+
} else {
|
70
|
+
this.showCaption();
|
71
|
+
}
|
72
|
+
}
|
73
|
+
});
|
74
|
+
|
75
|
+
// hide caption if zoomed
|
76
|
+
pswp.on('zoomPanUpdate', () => {
|
77
|
+
if (pswp.currSlide.currZoomLevel > pswp.currSlide.zoomLevels.initial) {
|
78
|
+
this.hideCaption();
|
79
|
+
} else {
|
80
|
+
this.showCaption();
|
81
|
+
}
|
82
|
+
});
|
83
|
+
|
84
|
+
pswp.on('beforeZoomTo', (e) => {
|
85
|
+
const { currSlide } = pswp;
|
86
|
+
|
87
|
+
if (currSlide.__dcAdjustedPanAreaSize) {
|
88
|
+
if (e.destZoomLevel > currSlide.zoomLevels.initial) {
|
89
|
+
currSlide.panAreaSize.x = currSlide.__dcOriginalPanAreaSize.x;
|
90
|
+
currSlide.panAreaSize.y = currSlide.__dcOriginalPanAreaSize.y;
|
91
|
+
} else {
|
92
|
+
// Restore panAreaSize after we zoom back to initial position
|
93
|
+
currSlide.panAreaSize.x = currSlide.__dcAdjustedPanAreaSize.x;
|
94
|
+
currSlide.panAreaSize.y = currSlide.__dcAdjustedPanAreaSize.y;
|
95
|
+
}
|
96
|
+
}
|
97
|
+
});
|
98
|
+
}
|
99
|
+
|
100
|
+
useMobileLayout() {
|
101
|
+
const { mobileLayoutBreakpoint } = this.options;
|
102
|
+
|
103
|
+
if (typeof mobileLayoutBreakpoint === 'function') {
|
104
|
+
return mobileLayoutBreakpoint.call(this);
|
105
|
+
} else if (typeof mobileLayoutBreakpoint === 'number') {
|
106
|
+
if (window.innerWidth < mobileLayoutBreakpoint) {
|
107
|
+
return true;
|
108
|
+
}
|
109
|
+
}
|
110
|
+
|
111
|
+
return false;
|
112
|
+
}
|
113
|
+
|
114
|
+
hideCaption() {
|
115
|
+
if (!this.isCaptionHidden) {
|
116
|
+
this.isCaptionHidden = true;
|
117
|
+
this.captionElement.classList.add('pswp__dynamic-caption--faded');
|
118
|
+
|
119
|
+
// Disable caption visibility with the delay, so it's not interactable
|
120
|
+
if (this.captionFadeTimeout) {
|
121
|
+
clearTimeout(this.captionFadeTimeout);
|
122
|
+
}
|
123
|
+
this.captionFadeTimeout = setTimeout(() => {
|
124
|
+
this.captionElement.style.visibility = 'hidden';
|
125
|
+
this.captionFadeTimeout = null;
|
126
|
+
}, 400);
|
127
|
+
}
|
128
|
+
}
|
129
|
+
|
130
|
+
showCaption() {
|
131
|
+
if (this.isCaptionHidden) {
|
132
|
+
this.isCaptionHidden = false;
|
133
|
+
this.captionElement.style.visibility = 'visible';
|
134
|
+
|
135
|
+
clearTimeout(this.captionFadeTimeout);
|
136
|
+
this.captionFadeTimeout = setTimeout(() => {
|
137
|
+
this.captionElement.classList.remove('pswp__dynamic-caption--faded');
|
138
|
+
this.captionFadeTimeout = null;
|
139
|
+
}, 50);
|
140
|
+
}
|
141
|
+
}
|
142
|
+
|
143
|
+
setCaptionPosition(x, y) {
|
144
|
+
const isOnHorizontalEdge = (x <= this.options.horizontalEdgeThreshold);
|
145
|
+
this.captionElement.classList[
|
146
|
+
isOnHorizontalEdge ? 'add' : 'remove'
|
147
|
+
]('pswp__dynamic-caption--on-hor-edge');
|
148
|
+
|
149
|
+
this.captionElement.style.left = x + 'px';
|
150
|
+
this.captionElement.style.top = y + 'px';
|
151
|
+
}
|
152
|
+
|
153
|
+
setCaptionWidth(captionEl, width) {
|
154
|
+
if (!width) {
|
155
|
+
captionEl.style.removeProperty('width');
|
156
|
+
} else {
|
157
|
+
captionEl.style.width = width + 'px';
|
158
|
+
}
|
159
|
+
}
|
160
|
+
|
161
|
+
setCaptionType(captionEl, type) {
|
162
|
+
const prevType = captionEl.dataset.pswpCaptionType;
|
163
|
+
if (type !== prevType) {
|
164
|
+
captionEl.classList.add('pswp__dynamic-caption--' + type);
|
165
|
+
captionEl.classList.remove('pswp__dynamic-caption--' + prevType);
|
166
|
+
captionEl.dataset.pswpCaptionType = type;
|
167
|
+
}
|
168
|
+
}
|
169
|
+
|
170
|
+
updateCurrentCaptionPosition() {
|
171
|
+
const slide = this.pswp.currSlide;
|
172
|
+
|
173
|
+
if (!slide.dynamicCaptionType) {
|
174
|
+
return;
|
175
|
+
}
|
176
|
+
|
177
|
+
if (slide.dynamicCaptionType === 'mobile') {
|
178
|
+
this.setCaptionType(this.captionElement, slide.dynamicCaptionType);
|
179
|
+
|
180
|
+
this.captionElement.style.removeProperty('left');
|
181
|
+
this.captionElement.style.removeProperty('top');
|
182
|
+
this.setCaptionWidth(this.captionElement, false);
|
183
|
+
return;
|
184
|
+
}
|
185
|
+
|
186
|
+
const zoomLevel = slide.zoomLevels.initial;
|
187
|
+
const imageWidth = Math.ceil(slide.width * zoomLevel);
|
188
|
+
const imageHeight = Math.ceil(slide.height * zoomLevel);
|
189
|
+
|
190
|
+
|
191
|
+
this.setCaptionType(this.captionElement, slide.dynamicCaptionType);
|
192
|
+
if (slide.dynamicCaptionType === 'aside') {
|
193
|
+
this.setCaptionPosition(
|
194
|
+
this.pswp.currSlide.bounds.center.x + imageWidth,
|
195
|
+
this.pswp.currSlide.bounds.center.y
|
196
|
+
);
|
197
|
+
this.setCaptionWidth(this.captionElement, false);
|
198
|
+
} else if (slide.dynamicCaptionType === 'below') {
|
199
|
+
this.setCaptionPosition(
|
200
|
+
this.pswp.currSlide.bounds.center.x,
|
201
|
+
this.pswp.currSlide.bounds.center.y + imageHeight
|
202
|
+
);
|
203
|
+
this.setCaptionWidth(this.captionElement, imageWidth);
|
204
|
+
}
|
205
|
+
}
|
206
|
+
|
207
|
+
/**
|
208
|
+
* Temporary caption is used to measure size for the current/next/previous captions,
|
209
|
+
* (it has visibility:hidden)
|
210
|
+
*/
|
211
|
+
createTemporaryCaption() {
|
212
|
+
this.tempCaption = document.createElement('div');
|
213
|
+
this.tempCaption.className = 'pswp__dynamic-caption pswp__dynamic-caption--temp';
|
214
|
+
this.tempCaption.style.visibility = 'hidden';
|
215
|
+
this.tempCaption.setAttribute('aria-hidden', 'true');
|
216
|
+
// move caption element, so it's after BG,
|
217
|
+
// so that other controls can freely overlap it
|
218
|
+
this.pswp.bg.after(this.captionElement);
|
219
|
+
this.captionElement.after(this.tempCaption);
|
220
|
+
}
|
221
|
+
|
222
|
+
onCalcSlideSize(e) {
|
223
|
+
const { slide } = e;
|
224
|
+
|
225
|
+
const captionHTML = this.getCaptionHTML(e.slide);
|
226
|
+
let useMobileVersion = false;
|
227
|
+
let captionSize;
|
228
|
+
|
229
|
+
if (!captionHTML) {
|
230
|
+
slide.dynamicCaptionType = false;
|
231
|
+
return;
|
232
|
+
}
|
233
|
+
|
234
|
+
this.storeOriginalPanAreaSize(slide);
|
235
|
+
|
236
|
+
slide.bounds.update(slide.zoomLevels.initial);
|
237
|
+
|
238
|
+
if (this.useMobileLayout()) {
|
239
|
+
slide.dynamicCaptionType = 'mobile';
|
240
|
+
useMobileVersion = true;
|
241
|
+
} else {
|
242
|
+
if (this.options.type === 'auto') {
|
243
|
+
if (slide.bounds.center.x > slide.bounds.center.y) {
|
244
|
+
slide.dynamicCaptionType = 'aside';
|
245
|
+
} else {
|
246
|
+
slide.dynamicCaptionType = 'below';
|
247
|
+
}
|
248
|
+
} else {
|
249
|
+
slide.dynamicCaptionType = this.options.type;
|
250
|
+
}
|
251
|
+
}
|
252
|
+
|
253
|
+
const imageWidth = Math.ceil(slide.width * slide.zoomLevels.initial);
|
254
|
+
const imageHeight = Math.ceil(slide.height * slide.zoomLevels.initial);
|
255
|
+
|
256
|
+
if (!this.tempCaption) {
|
257
|
+
this.createTemporaryCaption();
|
258
|
+
}
|
259
|
+
|
260
|
+
this.setCaptionType(this.tempCaption, slide.dynamicCaptionType);
|
261
|
+
|
262
|
+
if (slide.dynamicCaptionType === 'aside') {
|
263
|
+
this.tempCaption.innerHTML = this.getCaptionHTML(e.slide);
|
264
|
+
this.setCaptionWidth(this.tempCaption, false);
|
265
|
+
captionSize = this.measureCaptionSize(this.tempCaption, e.slide);
|
266
|
+
const captionWidth = captionSize.x;
|
267
|
+
|
268
|
+
const horizontalEnding = imageWidth + slide.bounds.center.x;
|
269
|
+
const horizontalLeftover = (slide.panAreaSize.x - horizontalEnding);
|
270
|
+
|
271
|
+
if (horizontalLeftover <= captionWidth) {
|
272
|
+
slide.panAreaSize.x -= captionWidth;
|
273
|
+
this.recalculateZoomLevelAndBounds(slide);
|
274
|
+
} else {
|
275
|
+
// do nothing, caption will fit aside without any adjustments
|
276
|
+
}
|
277
|
+
} else if (slide.dynamicCaptionType === 'below' || useMobileVersion) {
|
278
|
+
this.setCaptionWidth(
|
279
|
+
this.tempCaption,
|
280
|
+
useMobileVersion ? this.pswp.viewportSize.x : imageWidth
|
281
|
+
);
|
282
|
+
this.tempCaption.innerHTML = this.getCaptionHTML(e.slide);
|
283
|
+
captionSize = this.measureCaptionSize(this.tempCaption, e.slide);
|
284
|
+
const captionHeight = captionSize.y;
|
285
|
+
|
286
|
+
|
287
|
+
// vertical ending of the image
|
288
|
+
const verticalEnding = imageHeight + slide.bounds.center.y;
|
289
|
+
|
290
|
+
// height between bottom of the screen and ending of the image
|
291
|
+
// (before any adjustments applied)
|
292
|
+
const verticalLeftover = slide.panAreaSize.y - verticalEnding;
|
293
|
+
const initialPanAreaHeight = slide.panAreaSize.y;
|
294
|
+
|
295
|
+
if (verticalLeftover <= captionHeight) {
|
296
|
+
// lift up the image to give more space for caption
|
297
|
+
slide.panAreaSize.y -= Math.min((captionHeight - verticalLeftover) * 2, captionHeight);
|
298
|
+
|
299
|
+
// we reduce viewport size, thus we need to update zoom level and pan bounds
|
300
|
+
this.recalculateZoomLevelAndBounds(slide);
|
301
|
+
|
302
|
+
const maxPositionX = slide.panAreaSize.x * this.options.mobileCaptionOverlapRatio / 2;
|
303
|
+
|
304
|
+
// Do not reduce viewport height if too few space available
|
305
|
+
if (useMobileVersion
|
306
|
+
&& slide.bounds.center.x > maxPositionX) {
|
307
|
+
// Restore the default position
|
308
|
+
slide.panAreaSize.y = initialPanAreaHeight;
|
309
|
+
this.recalculateZoomLevelAndBounds(slide);
|
310
|
+
}
|
311
|
+
}
|
312
|
+
|
313
|
+
|
314
|
+
|
315
|
+
// if (this.useMobileLayout && slide.bounds.center.x > 100) {
|
316
|
+
// // do nothing, caption will overlap the bottom part of the image
|
317
|
+
// } else if (verticalLeftover <= captionHeight) {
|
318
|
+
|
319
|
+
// } else {
|
320
|
+
// // do nothing, caption will fit below the image without any adjustments
|
321
|
+
// }
|
322
|
+
} else {
|
323
|
+
// mobile
|
324
|
+
}
|
325
|
+
|
326
|
+
this.storeAdjustedPanAreaSize(slide);
|
327
|
+
|
328
|
+
if (slide === this.pswp.currSlide) {
|
329
|
+
this.updateCurrentCaptionPosition();
|
330
|
+
}
|
331
|
+
}
|
332
|
+
|
333
|
+
measureCaptionSize(captionEl, slide) {
|
334
|
+
const rect = captionEl.getBoundingClientRect();
|
335
|
+
const event = this.pswp.dispatch('dynamicCaptionMeasureSize', {
|
336
|
+
captionEl,
|
337
|
+
slide,
|
338
|
+
captionSize: {
|
339
|
+
x: rect.width,
|
340
|
+
y: rect.height
|
341
|
+
}
|
342
|
+
});
|
343
|
+
return event.captionSize;
|
344
|
+
}
|
345
|
+
|
346
|
+
recalculateZoomLevelAndBounds(slide) {
|
347
|
+
slide.zoomLevels.update(slide.width, slide.height, slide.panAreaSize);
|
348
|
+
slide.bounds.update(slide.zoomLevels.initial);
|
349
|
+
}
|
350
|
+
|
351
|
+
storeAdjustedPanAreaSize(slide) {
|
352
|
+
if (!slide.__dcAdjustedPanAreaSize) {
|
353
|
+
slide.__dcAdjustedPanAreaSize = {};
|
354
|
+
}
|
355
|
+
slide.__dcAdjustedPanAreaSize.x = slide.panAreaSize.x;
|
356
|
+
slide.__dcAdjustedPanAreaSize.y = slide.panAreaSize.y;
|
357
|
+
}
|
358
|
+
|
359
|
+
storeOriginalPanAreaSize(slide) {
|
360
|
+
if (!slide.__dcOriginalPanAreaSize) {
|
361
|
+
slide.__dcOriginalPanAreaSize = {};
|
362
|
+
}
|
363
|
+
slide.__dcOriginalPanAreaSize.x = slide.panAreaSize.x;
|
364
|
+
slide.__dcOriginalPanAreaSize.y = slide.panAreaSize.y;
|
365
|
+
}
|
366
|
+
|
367
|
+
getCaptionHTML(slide) {
|
368
|
+
if (typeof this.options.captionContent === 'function') {
|
369
|
+
return this.options.captionContent.call(this, slide);
|
370
|
+
}
|
371
|
+
|
372
|
+
const currSlideElement = slide.data.element;
|
373
|
+
let captionHTML = '';
|
374
|
+
if (currSlideElement) {
|
375
|
+
const hiddenCaption = currSlideElement.querySelector(this.options.captionContent);
|
376
|
+
if (hiddenCaption) {
|
377
|
+
// get caption from element with class pswp-caption-content
|
378
|
+
captionHTML = hiddenCaption.innerHTML;
|
379
|
+
} else {
|
380
|
+
const img = currSlideElement.querySelector('img');
|
381
|
+
if (img) {
|
382
|
+
// get caption from alt attribute
|
383
|
+
captionHTML = img.getAttribute('alt');
|
384
|
+
}
|
385
|
+
}
|
386
|
+
}
|
387
|
+
return captionHTML;
|
388
|
+
}
|
389
|
+
|
390
|
+
updateCaptionHTML() {
|
391
|
+
const captionHTML = this.getCaptionHTML(this.pswp.currSlide);
|
392
|
+
this.captionElement.style.visibility = captionHTML ? 'visible' : 'hidden';
|
393
|
+
this.captionElement.innerHTML = captionHTML || '';
|
394
|
+
this.pswp.dispatch('dynamicCaptionUpdateHTML', {
|
395
|
+
captionElement: this.captionElement
|
396
|
+
});
|
397
|
+
}
|
398
|
+
}
|
399
|
+
|
400
|
+
export default PhotoSwipeDynamicCaption;
|