intranet-pictures 1.0.6 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/lib/intranet/pictures/responder.rb +27 -18
  3. data/lib/intranet/pictures/version.rb +1 -1
  4. data/lib/intranet/resources/haml/pictures_browse.haml +0 -1
  5. data/lib/intranet/resources/haml/pictures_home.haml +0 -3
  6. data/lib/intranet/resources/locales/en.yml +0 -1
  7. data/lib/intranet/resources/locales/fr.yml +0 -1
  8. data/lib/intranet/resources/www/jpictures.js +32 -14
  9. data/lib/intranet/resources/www/photoswipe/photoswipe-dynamic-caption-plugin.css +47 -0
  10. data/lib/intranet/resources/www/photoswipe/photoswipe-dynamic-caption-plugin.esm.js +400 -0
  11. data/lib/intranet/resources/www/photoswipe/photoswipe-lightbox.esm.js +1382 -0
  12. data/lib/intranet/resources/www/photoswipe/photoswipe-lightbox.esm.js.map +1 -0
  13. data/lib/intranet/resources/www/photoswipe/photoswipe-lightbox.esm.min.js +5 -0
  14. data/lib/intranet/resources/www/photoswipe/photoswipe.css +383 -142
  15. data/lib/intranet/resources/www/photoswipe/photoswipe.esm.js +5279 -0
  16. data/lib/intranet/resources/www/photoswipe/photoswipe.esm.js.map +1 -0
  17. data/lib/intranet/resources/www/photoswipe/photoswipe.esm.min.js +5 -0
  18. data/lib/intranet/resources/www/style.css +13 -0
  19. data/spec/intranet/pictures/responder_spec.rb +78 -58
  20. metadata +26 -28
  21. data/lib/intranet/resources/haml/pictures_photoswipe.haml +0 -23
  22. data/lib/intranet/resources/www/photoswipe/LICENSE +0 -21
  23. data/lib/intranet/resources/www/photoswipe/default-skin/default-skin.css +0 -484
  24. data/lib/intranet/resources/www/photoswipe/default-skin/default-skin.png +0 -0
  25. data/lib/intranet/resources/www/photoswipe/default-skin/default-skin.svg +0 -1
  26. data/lib/intranet/resources/www/photoswipe/default-skin/preloader.gif +0 -0
  27. data/lib/intranet/resources/www/photoswipe/photoswipe-ui-default.js +0 -861
  28. data/lib/intranet/resources/www/photoswipe/photoswipe-ui-default.min.js +0 -4
  29. data/lib/intranet/resources/www/photoswipe/photoswipe.js +0 -3734
  30. 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: 628a14d0155be72d18ea879e45225bfa736d3e8ff85f737245595c7faba201bb
4
- data.tar.gz: d9156887f2265f2dcfc622aab9ff63a87ff4b57eecd3b894b6bab8b21479250e
3
+ metadata.gz: 375bdf2fb9b0a0354f3066e44c59a4426077916642a248a9180aabc3842628d5
4
+ data.tar.gz: 34b5b68ae804b7a441320eee02880726ce8ca85eb15a813f9e49ab99c64491a6
5
5
  SHA512:
6
- metadata.gz: 98cba0984d30e6ad3c6086bee626376e8775624628ecc5ebac979b96e2ddb6026c6ef88a96e9f0471b16608d2b2eb6070fec3833e7f14646c2a1f78c086b69e2
7
- data.tar.gz: 8af2829b1308cbdbf28bacfbb81cd89c0ad8d5216c9084eccd8ca3b9b75b489d34925a58a07f36a4055c1f210b47a6c04b87732a57e8b7b7078956a527fa75ef
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', { content: content, title: title }]
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', { content: content, title: title }]
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
  ##########################################################################
@@ -8,7 +8,7 @@ module Intranet
8
8
  NAME = 'intranet-pictures'
9
9
 
10
10
  # The version of the gem, according to semantic versionning.
11
- VERSION = '1.0.6'
11
+ VERSION = '1.1.0'
12
12
 
13
13
  # The URL of the gem homepage.
14
14
  HOMEPAGE_URL = 'https://rubygems.org/gems/intranet-pictures'
@@ -11,4 +11,3 @@
11
11
  - if group['brief']
12
12
  %br
13
13
  %em= group['brief']
14
- = to_markup 'pictures_photoswipe'
@@ -31,6 +31,3 @@
31
31
  - if group['brief']
32
32
  %br
33
33
  %em= group['brief']
34
-
35
- - unless recent_groups.empty?
36
- = to_markup 'pictures_photoswipe'
@@ -7,7 +7,6 @@ en:
7
7
  see_more: 'See more results'
8
8
  viewer:
9
9
  close: 'Close'
10
- fullscreen: 'Fullscreen'
11
10
  zoom: 'Zoom'
12
11
  previous: 'Previous image'
13
12
  next: 'Next image'
@@ -7,7 +7,6 @@ fr:
7
7
  see_more: 'Afficher plus de résultats'
8
8
  viewer:
9
9
  close: 'Fermer'
10
- fullscreen: 'Plein écran'
11
10
  zoom: 'Agrandir'
12
11
  previous: 'Image précédente'
13
12
  next: 'Image suivante'
@@ -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) + '<br />' + json[i].title;
18
- items.push({ src: imageUrl, msrc: imageUrl, w: json[i].width, h: json[i].height, title: imageTitle});
25
+ const imageTitle = '<strong>' + convertDate(json[i].datetime, true) + '</strong> &mdash; ' + json[i].title;
26
+ items.push({ src: imageUrl, width: json[i].width, height: json[i].height, title: imageTitle});
19
27
  }
20
28
 
21
- // Get the PhotoSwipe container
22
- const pswpElement = document.querySelectorAll('.pswp')[0];
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
- // Define options
25
- const options = {
26
- // core options
27
- index: 0, // start at first slide
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
- // Initialize and open PhotoSwipe
34
- let gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, items, options);
35
- gallery.init();
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;