@tomasvn/glightbox 0.0.0-stage → 3.3.4

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.
Files changed (46) hide show
  1. package/.editorconfig +16 -0
  2. package/.eslintignore +3 -0
  3. package/.eslintrc.json +94 -0
  4. package/.github/FUNDING.yml +3 -0
  5. package/.github/ISSUE_TEMPLATE/bug_report.md +41 -0
  6. package/.github/ISSUE_TEMPLATE/feature_request.md +23 -0
  7. package/.github/stale.yml +21 -0
  8. package/.github/workflows/greetings.yml +13 -0
  9. package/.husky/pre-commit +3 -0
  10. package/.lando.yml +10 -0
  11. package/.prettierignore +3 -0
  12. package/.prettierrc.json +30 -0
  13. package/CHANGELOG.md +259 -0
  14. package/CONTRIBUTING.md +29 -0
  15. package/LICENSE.md +21 -0
  16. package/README.md +714 -2
  17. package/bower.json +29 -0
  18. package/development/build.js +44 -0
  19. package/development/jscompiler.js +90 -0
  20. package/development/notifications.js +15 -0
  21. package/development/package.js +116 -0
  22. package/development/postcss.js +63 -0
  23. package/development/watcher.js +105 -0
  24. package/dist/css/glightbox.css +939 -0
  25. package/dist/css/glightbox.min.css +1 -0
  26. package/dist/js/glightbox.js +3109 -0
  27. package/dist/js/glightbox.min.js +1 -0
  28. package/index.d.ts +586 -0
  29. package/package.json +60 -4
  30. package/src/js/.jshintrc +6 -0
  31. package/src/js/core/drag.js +203 -0
  32. package/src/js/core/keyboard-navigation.js +101 -0
  33. package/src/js/core/slide-parser.js +236 -0
  34. package/src/js/core/slide.js +251 -0
  35. package/src/js/core/touch-events.js +378 -0
  36. package/src/js/core/touch-navigation.js +291 -0
  37. package/src/js/core/zoom.js +156 -0
  38. package/src/js/glightbox.js +1351 -0
  39. package/src/js/slides/iframe.js +24 -0
  40. package/src/js/slides/image.js +56 -0
  41. package/src/js/slides/inline.js +71 -0
  42. package/src/js/slides/video.js +108 -0
  43. package/src/js/utils/helpers.js +541 -0
  44. package/src/postcss/glightbox.css +725 -0
  45. package/tests/slide.test.mjs +130 -0
  46. package/tests/touch-navigation.test.mjs +13 -0
@@ -0,0 +1,203 @@
1
+ /**
2
+ * DragSlides
3
+ * Allow imaes to be dragged for prev and next
4
+ * in desktops
5
+ *
6
+ * @param { object } config
7
+ */
8
+
9
+ import { closest } from '../utils/helpers.js';
10
+
11
+ export default class DragSlides {
12
+ constructor(config = {}) {
13
+ let { dragEl, toleranceX = 40, toleranceY = 65, slide = null, instance = null } = config;
14
+
15
+ this.el = dragEl;
16
+ this.active = false;
17
+ this.dragging = false;
18
+ this.currentX = null;
19
+ this.currentY = null;
20
+ this.initialX = null;
21
+ this.initialY = null;
22
+ this.xOffset = 0;
23
+ this.yOffset = 0;
24
+ this.direction = null;
25
+ this.lastDirection = null;
26
+ this.toleranceX = toleranceX;
27
+ this.toleranceY = toleranceY;
28
+ this.toleranceReached = false;
29
+ this.dragContainer = this.el;
30
+ this.slide = slide;
31
+ this.instance = instance;
32
+
33
+ this.el.addEventListener('mousedown', (e) => this.dragStart(e), false);
34
+ this.el.addEventListener('mouseup', (e) => this.dragEnd(e), false);
35
+ this.el.addEventListener('mousemove', (e) => this.drag(e), false);
36
+ }
37
+ dragStart(e) {
38
+ if (this.slide.classList.contains('zoomed')) {
39
+ this.active = false;
40
+ return;
41
+ }
42
+
43
+ if (e.type === 'touchstart') {
44
+ this.initialX = e.touches[0].clientX - this.xOffset;
45
+ this.initialY = e.touches[0].clientY - this.yOffset;
46
+ } else {
47
+ this.initialX = e.clientX - this.xOffset;
48
+ this.initialY = e.clientY - this.yOffset;
49
+ }
50
+
51
+ let clicked = e.target.nodeName.toLowerCase();
52
+ let exludeClicks = ['input', 'select', 'textarea', 'button', 'a'];
53
+ if (e.target.classList.contains('nodrag') || closest(e.target, '.nodrag') || exludeClicks.indexOf(clicked) !== -1) {
54
+ this.active = false;
55
+ return;
56
+ }
57
+
58
+ e.preventDefault();
59
+
60
+ if (e.target === this.el || (clicked !== 'img' && closest(e.target, '.gslide-inline'))) {
61
+ this.active = true;
62
+ this.el.classList.add('dragging');
63
+ this.dragContainer = closest(e.target, '.ginner-container');
64
+ }
65
+ }
66
+ dragEnd(e) {
67
+ e && e.preventDefault();
68
+ this.initialX = 0;
69
+ this.initialY = 0;
70
+ this.currentX = null;
71
+ this.currentY = null;
72
+ this.initialX = null;
73
+ this.initialY = null;
74
+ this.xOffset = 0;
75
+ this.yOffset = 0;
76
+ this.active = false;
77
+
78
+ if (this.doSlideChange) {
79
+ this.instance.preventOutsideClick = true;
80
+ this.doSlideChange == 'right' && this.instance.prevSlide();
81
+ this.doSlideChange == 'left' && this.instance.nextSlide();
82
+ }
83
+
84
+ if (this.doSlideClose) {
85
+ this.instance.close();
86
+ }
87
+
88
+ if (!this.toleranceReached) {
89
+ this.setTranslate(this.dragContainer, 0, 0, true);
90
+ }
91
+
92
+ setTimeout(() => {
93
+ this.instance.preventOutsideClick = false;
94
+ this.toleranceReached = false;
95
+ this.lastDirection = null;
96
+ this.dragging = false;
97
+ this.el.isDragging = false;
98
+ this.el.classList.remove('dragging');
99
+ this.slide.classList.remove('dragging-nav');
100
+ this.dragContainer.style.transform = '';
101
+ this.dragContainer.style.transition = '';
102
+ }, 100);
103
+ }
104
+ drag(e) {
105
+ if (this.active) {
106
+ e.preventDefault();
107
+
108
+ this.slide.classList.add('dragging-nav');
109
+
110
+ if (e.type === 'touchmove') {
111
+ this.currentX = e.touches[0].clientX - this.initialX;
112
+ this.currentY = e.touches[0].clientY - this.initialY;
113
+ } else {
114
+ this.currentX = e.clientX - this.initialX;
115
+ this.currentY = e.clientY - this.initialY;
116
+ }
117
+
118
+ this.xOffset = this.currentX;
119
+ this.yOffset = this.currentY;
120
+
121
+ this.el.isDragging = true;
122
+ this.dragging = true;
123
+ this.doSlideChange = false;
124
+ this.doSlideClose = false;
125
+
126
+ let currentXInt = Math.abs(this.currentX);
127
+ let currentYInt = Math.abs(this.currentY);
128
+
129
+ // Horizontal drag
130
+ if (currentXInt > 0 && currentXInt >= Math.abs(this.currentY) && (!this.lastDirection || this.lastDirection == 'x')) {
131
+ this.yOffset = 0;
132
+ this.lastDirection = 'x';
133
+ this.setTranslate(this.dragContainer, this.currentX, 0);
134
+
135
+ let doChange = this.shouldChange();
136
+ if (!this.instance.settings.dragAutoSnap && doChange) {
137
+ this.doSlideChange = doChange;
138
+ }
139
+
140
+ if (this.instance.settings.dragAutoSnap && doChange) {
141
+ this.instance.preventOutsideClick = true;
142
+ this.toleranceReached = true;
143
+ this.active = false;
144
+ this.instance.preventOutsideClick = true;
145
+ this.dragEnd(null);
146
+ doChange == 'right' && this.instance.prevSlide();
147
+ doChange == 'left' && this.instance.nextSlide();
148
+ return;
149
+ }
150
+ }
151
+
152
+ // Vertical drag
153
+ if (this.toleranceY > 0 && currentYInt > 0 && currentYInt >= currentXInt && (!this.lastDirection || this.lastDirection == 'y')) {
154
+ this.xOffset = 0;
155
+ this.lastDirection = 'y';
156
+ this.setTranslate(this.dragContainer, 0, this.currentY);
157
+
158
+ let doClose = this.shouldClose();
159
+
160
+ if (!this.instance.settings.dragAutoSnap && doClose) {
161
+ this.doSlideClose = true;
162
+ }
163
+ if (this.instance.settings.dragAutoSnap && doClose) {
164
+ this.instance.close();
165
+ }
166
+ return;
167
+ }
168
+ }
169
+ }
170
+
171
+ shouldChange() {
172
+ let doChange = false;
173
+ let currentXInt = Math.abs(this.currentX);
174
+
175
+ if (currentXInt >= this.toleranceX) {
176
+ let dragDir = this.currentX > 0 ? 'right' : 'left';
177
+
178
+ if ((dragDir == 'left' && this.slide !== this.slide.parentNode.lastChild) || (dragDir == 'right' && this.slide !== this.slide.parentNode.firstChild)) {
179
+ doChange = dragDir;
180
+ }
181
+ }
182
+ return doChange;
183
+ }
184
+
185
+ shouldClose() {
186
+ let doClose = false;
187
+ let currentYInt = Math.abs(this.currentY);
188
+
189
+ if (currentYInt >= this.toleranceY) {
190
+ doClose = true;
191
+ }
192
+ return doClose;
193
+ }
194
+
195
+ setTranslate(node, xPos, yPos, animated = false) {
196
+ if (animated) {
197
+ node.style.transition = 'all .2s ease';
198
+ } else {
199
+ node.style.transition = '';
200
+ }
201
+ node.style.transform = `translate3d(${xPos}px, ${yPos}px, 0)`;
202
+ }
203
+ }
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Keyboard Navigation
3
+ * Allow navigation using the keyboard
4
+ *
5
+ * @param {object} instance
6
+ */
7
+
8
+ import { addEvent, addClass, removeClass, each } from '../utils/helpers.js';
9
+
10
+ function getNextFocusElement(current = -1) {
11
+ const btns = document.querySelectorAll('.gbtn[data-taborder]:not(.disabled)');
12
+ if (!btns.length) {
13
+ return false;
14
+ }
15
+
16
+ if (btns.length == 1) {
17
+ return btns[0];
18
+ }
19
+
20
+ if (typeof current == 'string') {
21
+ current = parseInt(current);
22
+ }
23
+
24
+ const orders = [];
25
+ each(btns, (btn) => {
26
+ orders.push(btn.getAttribute('data-taborder'));
27
+ });
28
+ const highestOrder = Math.max.apply(
29
+ Math,
30
+ orders.map((order) => parseInt(order))
31
+ );
32
+
33
+ let newIndex = current < 0 ? 1 : current + 1;
34
+ if (newIndex > highestOrder) {
35
+ newIndex = '1';
36
+ }
37
+
38
+ const nextOrders = orders.filter((el) => el >= parseInt(newIndex));
39
+ const nextFocus = nextOrders.sort()[0];
40
+
41
+ return document.querySelector(`.gbtn[data-taborder="${nextFocus}"]`);
42
+ }
43
+
44
+ export default function keyboardNavigation(instance) {
45
+ if (instance.events.hasOwnProperty('keyboard')) {
46
+ return false;
47
+ }
48
+
49
+ instance.events['keyboard'] = addEvent('keydown', {
50
+ onElement: window,
51
+ withCallback: (event, target) => {
52
+ event = event || window.event;
53
+ const key = event.keyCode;
54
+ if (key == 9) {
55
+ //prettier-ignore
56
+ const focusedButton = document.querySelector('.gbtn.focused');
57
+
58
+ if (!focusedButton) {
59
+ const activeElement = document.activeElement && document.activeElement.nodeName ? document.activeElement.nodeName.toLocaleLowerCase() : false;
60
+ if (activeElement == 'input' || activeElement == 'textarea' || activeElement == 'button') {
61
+ return;
62
+ }
63
+ }
64
+
65
+ event.preventDefault();
66
+ const btns = document.querySelectorAll('.gbtn[data-taborder]');
67
+ if (!btns || btns.length <= 0) {
68
+ return;
69
+ }
70
+
71
+ if (!focusedButton) {
72
+ const first = getNextFocusElement();
73
+ if (first) {
74
+ first.focus();
75
+ addClass(first, 'focused');
76
+ }
77
+ return;
78
+ }
79
+
80
+ let currentFocusOrder = focusedButton.getAttribute('data-taborder');
81
+ let nextFocus = getNextFocusElement(currentFocusOrder);
82
+
83
+ removeClass(focusedButton, 'focused');
84
+
85
+ if (nextFocus) {
86
+ nextFocus.focus();
87
+ addClass(nextFocus, 'focused');
88
+ }
89
+ }
90
+ if (key == 39) {
91
+ instance.nextSlide();
92
+ }
93
+ if (key == 37) {
94
+ instance.prevSlide();
95
+ }
96
+ if (key == 27) {
97
+ instance.close();
98
+ }
99
+ }
100
+ });
101
+ }
@@ -0,0 +1,236 @@
1
+ import { extend, has, each, isNil, isNode, isObject, isNumber } from '../utils/helpers.js';
2
+
3
+ export default class SlideConfigParser {
4
+ constructor(slideParamas = {}) {
5
+ this.defaults = {
6
+ href: '',
7
+ sizes: '',
8
+ srcset: '',
9
+ title: '',
10
+ type: '',
11
+ videoProvider: '',
12
+ description: '',
13
+ alt: '',
14
+ descPosition: 'bottom',
15
+ effect: '',
16
+ width: '',
17
+ height: '',
18
+ content: false,
19
+ zoomable: true,
20
+ draggable: true
21
+ };
22
+
23
+ if (isObject(slideParamas)) {
24
+ this.defaults = extend(this.defaults, slideParamas);
25
+ }
26
+ }
27
+
28
+ /**
29
+ * Get source type
30
+ * gte the source type of a url
31
+ *
32
+ * @param {string} url
33
+ */
34
+ sourceType(url) {
35
+ let origin = url;
36
+ url = url.toLowerCase();
37
+
38
+ if (url.match(/\.(jpeg|jpg|jpe|gif|png|apn|webp|avif|svg)/) !== null) {
39
+ return 'image';
40
+ }
41
+ if (
42
+ url.match(/(youtube\.com|youtube-nocookie\.com)\/watch\?v=([a-zA-Z0-9\-_]+)/) ||
43
+ url.match(/youtu\.be\/([a-zA-Z0-9\-_]+)/) ||
44
+ url.match(/(youtube\.com|youtube-nocookie\.com)\/embed\/([a-zA-Z0-9\-_]+)/) ||
45
+ url.match(/(youtube\.com|youtube-nocookie\.com)\/shorts\/([a-zA-Z0-9\-_]+)/)
46
+ ) {
47
+ return 'video';
48
+ }
49
+ if (url.match(/vimeo\.com\/([0-9]*)/)) {
50
+ return 'video';
51
+ }
52
+ if (url.match(/\.(mp4|ogg|webm|mov)/) !== null) {
53
+ return 'video';
54
+ }
55
+ if (url.match(/\.(mp3|wav|wma|aac|ogg)/) !== null) {
56
+ return 'audio';
57
+ }
58
+
59
+ // Check if inline content
60
+ if (url.indexOf('#') > -1) {
61
+ let hash = origin.split('#').pop();
62
+ if (hash.trim() !== '') {
63
+ return 'inline';
64
+ }
65
+ }
66
+ // Ajax
67
+ if (url.indexOf('goajax=true') > -1) {
68
+ return 'ajax';
69
+ }
70
+
71
+ return 'external';
72
+ }
73
+
74
+ parseConfig(element, settings) {
75
+ let data = extend({ descPosition: settings.descPosition }, this.defaults);
76
+
77
+ if (isObject(element) && !isNode(element)) {
78
+ if (!has(element, 'type')) {
79
+ if (has(element, 'content') && element.content) {
80
+ element.type = 'inline';
81
+ } else if (has(element, 'href')) {
82
+ element.type = this.sourceType(element.href);
83
+ }
84
+ }
85
+ let objectData = extend(data, element);
86
+ this.setSize(objectData, settings);
87
+
88
+ return objectData;
89
+ }
90
+
91
+ let url = '';
92
+ let config = element.getAttribute('data-glightbox');
93
+ let nodeType = element.nodeName.toLowerCase();
94
+
95
+ if (nodeType === 'a') {
96
+ url = element.href;
97
+ }
98
+ if (nodeType === 'img') {
99
+ url = element.src;
100
+ //alt attribute propagation
101
+ data.alt = element.alt;
102
+ }
103
+
104
+ data.href = url;
105
+
106
+ each(data, (val, key) => {
107
+ if (has(settings, key) && key !== 'width') {
108
+ data[key] = settings[key];
109
+ }
110
+ const nodeData = element.dataset[key];
111
+ if (!isNil(nodeData)) {
112
+ data[key] = this.sanitizeValue(nodeData);
113
+ }
114
+ });
115
+
116
+ if (data.content) {
117
+ data.type = 'inline';
118
+ }
119
+
120
+ if (!data.type && url) {
121
+ data.type = this.sourceType(url);
122
+ }
123
+
124
+ if (!isNil(config)) {
125
+ let cleanKeys = [];
126
+ each(data, (v, k) => {
127
+ cleanKeys.push(';\\s?' + k);
128
+ });
129
+ cleanKeys = cleanKeys.join('\\s?:|');
130
+ if (config.trim() !== '') {
131
+ each(data, (val, key) => {
132
+ const str = config;
133
+ const match = 's?' + key + 's?:s?(.*?)(' + cleanKeys + 's?:|$)';
134
+ const regex = new RegExp(match);
135
+ const matches = str.match(regex);
136
+
137
+ if (matches && matches.length && matches[1]) {
138
+ const value = matches[1].trim().replace(/;\s*$/, '');
139
+ data[key] = this.sanitizeValue(value);
140
+ }
141
+ });
142
+ }
143
+ } else {
144
+ if (!data.title && nodeType == 'a') {
145
+ let title = element.title;
146
+ if (!isNil(title) && title !== '') {
147
+ data.title = title;
148
+ }
149
+ }
150
+ if (!data.title && nodeType == 'img') {
151
+ let alt = element.alt;
152
+ if (!isNil(alt) && alt !== '') {
153
+ data.title = alt;
154
+ }
155
+ }
156
+ }
157
+
158
+ // Try to get the description from a referenced element
159
+ if (data.description && data.description.substring(0, 1) === '.') {
160
+ let description;
161
+
162
+ try {
163
+ description = document.querySelector(data.description).innerHTML;
164
+ } catch (error) {
165
+ if (!(error instanceof DOMException)) {
166
+ throw error;
167
+ }
168
+ }
169
+
170
+ if (description) {
171
+ data.description = description;
172
+ }
173
+ }
174
+
175
+ // Try to get the description from a .glightbox-desc element
176
+ if (!data.description) {
177
+ let nodeDesc = element.querySelector('.glightbox-desc');
178
+ if (nodeDesc) {
179
+ data.description = nodeDesc.innerHTML;
180
+ }
181
+ }
182
+
183
+ this.setSize(data, settings, element);
184
+ this.slideConfig = data;
185
+
186
+ return data;
187
+ }
188
+
189
+ /**
190
+ * Set slide data size
191
+ * set the correct size dependin
192
+ * on the slide type
193
+ *
194
+ * @param { object } data
195
+ * @param { object } settings
196
+ * @return { object }
197
+ */
198
+ setSize(data, settings, element = null) {
199
+ const defaultWith = data.type == 'video' ? this.checkSize(settings.videosWidth) : this.checkSize(settings.width);
200
+ const defaultHeight = this.checkSize(settings.height);
201
+
202
+ data.width = has(data, 'width') && data.width !== '' ? this.checkSize(data.width) : defaultWith;
203
+ data.height = has(data, 'height') && data.height !== '' ? this.checkSize(data.height) : defaultHeight;
204
+
205
+ if (element && data.type == 'image') {
206
+ data._hasCustomWidth = element.dataset.width ? true : false;
207
+ data._hasCustomHeight = element.dataset.height ? true : false;
208
+ }
209
+
210
+ return data;
211
+ }
212
+
213
+ /**
214
+ * [checkSize size
215
+ * check if the passed size has a correct unit
216
+ *
217
+ * @param {string} size
218
+ * @return {string}
219
+ */
220
+ checkSize(size) {
221
+ return isNumber(size) ? `${size}px` : size;
222
+ }
223
+
224
+ /**
225
+ * Sanitize data attributes value
226
+ *
227
+ * @param string val
228
+ * @return mixed
229
+ */
230
+ sanitizeValue(val) {
231
+ if (val !== 'true' && val !== 'false') {
232
+ return val;
233
+ }
234
+ return val === 'true';
235
+ }
236
+ }