javascriptgantt 1.0.0 → 1.2.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.
@@ -1,1095 +1,1095 @@
1
- /* =========================================================
2
- * Created by Sunil Solanki
3
- *
4
- * Licensed under the Apache License, Version 2.0 (the "License");
5
- * you may not use this file except in compliance with the License.
6
- * You may obtain a copy of the License at
7
- *
8
- * http://www.apache.org/licenses/LICENSE-2.0
9
- *
10
- * Unless required by applicable law or agreed to in writing, software
11
- * distributed under the License is distributed on an "AS IS" BASIS,
12
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- * See the License for the specific language governing permissions and
14
- * limitations under the License.
15
- * ========================================================= */
16
-
17
- (function (global) {
18
- class ztTour {
19
- constructor(options, templates) {
20
- this.initializeOptions(options);
21
- this.initializeTemplate(templates);
22
-
23
- this.popup = null;
24
- this.overlaySvg = null;
25
- this.currentStep = 0;
26
- this.activeStagePosition = null;
27
-
28
- this.onKeyUp = this.onKeyUp.bind(this);
29
- this.refreshStep = this.refreshStep.bind(this);
30
-
31
- this.initEvents();
32
-
33
- this.onNextClick = options.onNextClick || null;
34
- this.onClose = options.onClose || null;
35
- this.onPreviousClick = options.onPreviousClick || null;
36
- }
37
-
38
- // initialize Options
39
- initializeOptions(opt) {
40
- if (opt == null) {
41
- opt = {};
42
- }
43
-
44
- this.options = {
45
- steps: opt.steps || [],
46
- overlayOpacity: opt.overlayOpacity || 0.7,
47
- stagePadding: opt.stagePadding || 10,
48
- popupOffset: opt.popupOffset || 10,
49
- stageRadius: opt.stageRadius || 5,
50
- overlayColor: opt.overlayColor || "#000",
51
- animate: opt.animate !== undefined ? opt.animate : true,
52
- smoothScroll: opt.smoothScroll || false,
53
- visibleButtons: opt.visibleButtons || ["next", "previous", "close"],
54
- disableButtons: opt.disableButtons || [],
55
- showProgress: opt.showProgress || false,
56
- nextBtnText: opt.nextBtnText || "Next →",
57
- prevBtnText: opt.prevBtnText || "← Previous",
58
- doneBtnText: opt.doneBtnText || "Done",
59
- allowBackdropClose: opt.allowBackdropClose !== undefined ? opt.allowBackdropClose : true,
60
- popupClass: opt.popupClass || false,
61
- keyboardControl: opt.keyboardControl !== undefined ? opt.keyboardControl : true,
62
- animationDuration: opt.animationDuration || 400,
63
- };
64
- }
65
-
66
- initializeTemplate(templ) {
67
- if (templ == null) {
68
- templ = {};
69
- }
70
- this.templates = {
71
- progressText:
72
- templ.progressText || ((current, total) => `${current} of ${total}`),
73
- };
74
- }
75
-
76
- initEvents() {
77
- window.addEventListener("keyup", this.onKeyUp, false);
78
- window.addEventListener("resize", this.refreshStep, false);
79
- window.addEventListener("scroll", this.refreshStep, false);
80
- }
81
-
82
- onKeyUp(e) {
83
- const keyboardControl = this.getOption("keyboardControl");
84
- if (keyboardControl) {
85
- if (e.key === "Escape") {
86
- this.destroyTour();
87
- } else if (e.key === "ArrowRight") {
88
- this.highlightStep(this.getOption("currentStep") + 1);
89
- } else if (e.key === "ArrowLeft") {
90
- this.highlightStep(this.getOption("currentStep") - 1);
91
- }
92
- }
93
- }
94
-
95
- destroyEvents() {
96
- window.removeEventListener("keyup", this.onKeyUp);
97
- window.removeEventListener("resize", this.refreshStep);
98
- window.removeEventListener("scroll", this.refreshStep);
99
- }
100
-
101
- getOption(key) {
102
- if (!key) {
103
- return this.options;
104
- }
105
- return this.options[key];
106
- }
107
-
108
- setOption(key, val) {
109
- this.options[key] = val;
110
- }
111
-
112
- throwError(error) {
113
- throw new Error(error);
114
- }
115
-
116
- start() {
117
- this.highlightStep(0);
118
- }
119
-
120
- showHint(hint, isAnnouncement = false){
121
- this.removeHint(isAnnouncement);
122
-
123
- let popupType = isAnnouncement ? "announcement" : "hint";
124
-
125
- let hintEle = document.createElement("div");
126
- let hintBackdrop = document.createElement("div");
127
- hintBackdrop.classList.add(`zt-tour-${popupType}-backdrop`);
128
- hintBackdrop.addEventListener('click',()=>{
129
- hintEle.remove();
130
- hintBackdrop.remove();
131
- })
132
- hintEle.innerHTML = hint.innerHTML;
133
- hintEle.classList.add(`zt-tour-${popupType}`);
134
-
135
- let dimentions;
136
- if(!isAnnouncement){
137
- dimentions = document.querySelector(hint.element).getBoundingClientRect();
138
- }
139
-
140
- document.body.appendChild(hintBackdrop);
141
- document.body.appendChild(hintEle);
142
-
143
-
144
- hintEle.style.top = isAnnouncement ? "50%" : dimentions.top + dimentions.height + 10 + "px";
145
- hintEle.style.left = isAnnouncement ? "50%" : dimentions.left + 10 + "px";
146
- hintEle.style.transform = isAnnouncement ? "translate(-50%, -50%)" : "";
147
- }
148
-
149
- showAnnouncement(announcement){
150
- announcement = {innerHTML: announcement};
151
- this.showHint(announcement, true);
152
- }
153
-
154
- removeHint(isAnnouncement = false){
155
- let popupType = isAnnouncement ? "announcement" : "hint";
156
- let oldHint = document.querySelectorAll(`.zt-tour-${popupType}`);
157
- let oldHintBackdrop = document.querySelectorAll(`.zt-tour-${popupType}-backdrop`);
158
-
159
- Array.from(oldHint).forEach((hint)=>hint.remove())
160
- Array.from(oldHintBackdrop).forEach((hintBackdrop)=>hintBackdrop.remove())
161
- }
162
-
163
- removeAnnouncement(){
164
- this.removeHint(true);
165
- }
166
-
167
- highlightStep(currentStep) {
168
- if (0 > currentStep || currentStep > this.getOption("steps").length - 1) {
169
- this.destroyTour();
170
- return;
171
- }
172
-
173
- let step = this.options.steps[currentStep];
174
- let element = document.querySelector(step?.element);
175
-
176
- if (!element) {
177
- element = this.addDummyElement();
178
- }
179
-
180
- this.changeHighlight(element, step, currentStep);
181
- }
182
-
183
- createPopup() {
184
- const wrapper = document.createElement("div");
185
- wrapper.classList.add("zt-tour-popup");
186
- if (this.getOption("popupClass")) {
187
- wrapper.classList.add(this.getOption("popupClass").trim());
188
- }
189
-
190
- const arrow = document.createElement("div");
191
- arrow.classList.add("zt-tour-popup-arrow");
192
-
193
- const title = document.createElement("header");
194
- title.id = "zt-tour-popup-title";
195
- title.classList.add("zt-tour-popup-title");
196
- title.style.display = "none";
197
- title.innerText = "Popup Title";
198
-
199
- const description = document.createElement("div");
200
- description.id = "zt-tour-popup-description";
201
- description.classList.add("zt-tour-popup-description");
202
- description.style.display = "none";
203
- description.innerText = "Popup description is here";
204
-
205
- const closeButton = document.createElement("button");
206
- closeButton.type = "button";
207
- closeButton.classList.add("zt-tour-popup-close-btn");
208
- closeButton.setAttribute("aria-label", "Close");
209
- closeButton.innerHTML = "×";
210
-
211
- const footer = document.createElement("footer");
212
- footer.classList.add("zt-tour-popup-footer");
213
-
214
- const progress = document.createElement("span");
215
- progress.classList.add("zt-tour-popup-progress-text");
216
- progress.innerText = "";
217
-
218
- const footerButtons = document.createElement("span");
219
- footerButtons.classList.add("zt-tour-popup-navigation-btns");
220
-
221
- const previousButton = document.createElement("button");
222
- previousButton.type = "button";
223
- previousButton.classList.add("zt-tour-popup-prev-btn");
224
- previousButton.innerHTML = "← Previous";
225
-
226
- const nextButton = document.createElement("button");
227
- nextButton.type = "button";
228
- nextButton.classList.add("zt-tour-popup-next-btn");
229
- nextButton.innerHTML = "Next →";
230
-
231
- footerButtons.appendChild(previousButton);
232
- footerButtons.appendChild(nextButton);
233
- footer.appendChild(progress);
234
- footer.appendChild(footerButtons);
235
-
236
- wrapper.appendChild(closeButton);
237
- wrapper.appendChild(arrow);
238
- wrapper.appendChild(title);
239
- wrapper.appendChild(description);
240
- wrapper.appendChild(footer);
241
-
242
- return {
243
- wrapper,
244
- arrow,
245
- title,
246
- description,
247
- footer,
248
- previousButton,
249
- nextButton,
250
- closeButton,
251
- footerButtons,
252
- progress,
253
- };
254
- }
255
-
256
- renderPopup(element, step) {
257
- let popup = this.createPopup();
258
- this.options.popup = popup;
259
-
260
- let oldPopups = document.querySelectorAll(".zt-tour-popup");
261
-
262
- // remove all old popups
263
- Array.from(oldPopups).forEach((ele) => {
264
- ele.remove();
265
- });
266
-
267
- document.body.appendChild(popup.wrapper);
268
-
269
- let {
270
- title,
271
- description,
272
- visibleButtons,
273
- disableButtons,
274
- showProgress,
275
- nextBtnText = this.options.nextBtnText,
276
- prevBtnText = this.options.prevBtnText,
277
- progressText = this.templates.progressText(
278
- this.options.currentStep + 1,
279
- this.options.steps.length
280
- ),
281
- } = step.popup || {};
282
-
283
- if (this.options.currentStep + 1 === this.options.steps.length) {
284
- nextBtnText = this.options.doneBtnText;
285
- }
286
-
287
- if (this.options.currentStep === 0) {
288
- popup.previousButton.disabled = true;
289
- }
290
-
291
- popup.nextButton.innerHTML = nextBtnText;
292
- popup.previousButton.innerHTML = prevBtnText;
293
- popup.progress.innerHTML = progressText;
294
-
295
- if (title) {
296
- popup.title.innerHTML = title;
297
- popup.title.style.display = "block";
298
- } else {
299
- popup.title.style.display = "none";
300
- }
301
-
302
- if (description) {
303
- popup.description.innerHTML = description;
304
- popup.description.style.display = "block";
305
- } else {
306
- popup.description.style.display = "none";
307
- }
308
-
309
- const showButtonsOption =
310
- visibleButtons || this.getOption("visibleButtons");
311
- const isShowProgress = showProgress || this.getOption("showProgress");
312
-
313
- const isShowFooter =
314
- showButtonsOption.includes("next") ||
315
- showButtonsOption?.includes("previous") ||
316
- isShowProgress;
317
-
318
- popup.closeButton.style.display = showButtonsOption.includes("close")
319
- ? "block"
320
- : "none";
321
-
322
- if (isShowFooter) {
323
- popup.footer.style.display = "flex";
324
-
325
- popup.progress.style.display = isShowProgress ? "block" : "none";
326
- popup.nextButton.style.display = showButtonsOption.includes("next")
327
- ? "block"
328
- : "none";
329
- popup.previousButton.style.display = showButtonsOption.includes(
330
- "previous"
331
- )
332
- ? "block"
333
- : "none";
334
- } else {
335
- popup.footer.style.display = "none";
336
- }
337
-
338
- const disabledButtonsOption =
339
- disableButtons || this.getOption("disableButtons");
340
- if (disabledButtonsOption?.includes("next")) {
341
- popup.nextButton.disabled = true;
342
- }
343
-
344
- if (disabledButtonsOption?.includes("previous")) {
345
- popup.previousButton.disabled = true;
346
- }
347
-
348
- if (disabledButtonsOption?.includes("close")) {
349
- popup.closeButton.disabled = true;
350
- }
351
-
352
- popup.nextButton.addEventListener("click", () => {
353
- let isOutOfIndex =
354
- this.options.currentStep + 1 < 0 ||
355
- this.options.currentStep + 1 >= this.getOption("steps").length;
356
- this.highlightStep(this.options.currentStep + 1);
357
- if (typeof this.onNextClick === "function" && !isOutOfIndex) {
358
- this.onNextClick(this.options.currentStep);
359
- }
360
- });
361
-
362
- popup.previousButton.addEventListener("click", () => {
363
- this.highlightStep(this.options.currentStep - 1);
364
- if (typeof this.onPreviousClick === "function") {
365
- this.onPreviousClick(this.options.currentStep);
366
- }
367
- });
368
-
369
- popup.closeButton.addEventListener("click", () => {
370
- this.destroyTour();
371
- if (typeof this.onClose === "function") {
372
- this.onClose();
373
- }
374
- });
375
-
376
- this.repositionPopup(element, step);
377
- }
378
-
379
- hidePopup() {
380
- const popup = this.getOption("popup");
381
- if (!popup) {
382
- return;
383
- }
384
-
385
- popup.wrapper.style.display = "none";
386
- }
387
-
388
- renderPopupArrow(alignment, side, element) {
389
- const elementDimensions = element.getBoundingClientRect();
390
- const popupDimensions = this.getPopupDimensions();
391
- const popupArrow = this.options.popup.arrow;
392
-
393
- const popupWidth = popupDimensions.width;
394
- const windowWidth = window.innerWidth;
395
- const elementWidth = elementDimensions.width;
396
- const elementLeft = elementDimensions.left;
397
-
398
- const popupHeight = popupDimensions.height;
399
- const windowHeight = window.innerHeight;
400
- const elementTop = elementDimensions.top;
401
- const elementHeight = elementDimensions.height;
402
-
403
- // Remove all arrow classes
404
- popupArrow.className = "zt-tour-popup-arrow";
405
-
406
- let arrowSide = side;
407
- let arrowAlignment = alignment;
408
-
409
- if (side === "top") {
410
- if (elementLeft + elementWidth <= 0) {
411
- arrowSide = "right";
412
- arrowAlignment = "end";
413
- } else if (elementLeft + elementWidth - popupWidth <= 0) {
414
- arrowSide = "top";
415
- arrowAlignment = "start";
416
- }
417
- if (elementLeft >= windowWidth) {
418
- arrowSide = "left";
419
- arrowAlignment = "end";
420
- } else if (elementLeft + popupWidth >= windowWidth) {
421
- arrowSide = "top";
422
- arrowAlignment = "end";
423
- }
424
- } else if (side === "bottom") {
425
- if (elementLeft + elementWidth <= 0) {
426
- arrowSide = "right";
427
- arrowAlignment = "start";
428
- } else if (elementLeft + elementWidth - popupWidth <= 0) {
429
- arrowSide = "bottom";
430
- arrowAlignment = "start";
431
- }
432
- if (elementLeft >= windowWidth) {
433
- arrowSide = "left";
434
- arrowAlignment = "start";
435
- } else if (elementLeft + popupWidth >= windowWidth) {
436
- arrowSide = "bottom";
437
- arrowAlignment = "end";
438
- }
439
- } else if (side === "left") {
440
- if (elementTop + elementHeight <= 0) {
441
- arrowSide = "bottom";
442
- arrowAlignment = "end";
443
- } else if (elementTop + elementHeight - popupHeight <= 0) {
444
- arrowSide = "left";
445
- arrowAlignment = "start";
446
- }
447
-
448
- if (elementTop >= windowHeight) {
449
- arrowSide = "top";
450
- arrowAlignment = "end";
451
- } else if (elementTop + popupHeight >= windowHeight) {
452
- arrowSide = "left";
453
- arrowAlignment = "end";
454
- }
455
- } else if (side === "right") {
456
- if (elementTop + elementHeight <= 0) {
457
- arrowSide = "bottom";
458
- arrowAlignment = "start";
459
- } else if (elementTop + elementHeight - popupHeight <= 0) {
460
- arrowSide = "right";
461
- arrowAlignment = "start";
462
- }
463
-
464
- if (elementTop >= windowHeight) {
465
- arrowSide = "top";
466
- arrowAlignment = "start";
467
- } else if (elementTop + popupHeight >= windowHeight) {
468
- arrowSide = "right";
469
- arrowAlignment = "end";
470
- }
471
- }
472
-
473
- if (!arrowSide) {
474
- popupArrow.classList.add("zt-tour-d-none");
475
- } else {
476
- popupArrow.classList.add(`zt-tour-popup-arrow-side-${arrowSide}`);
477
- popupArrow.classList.add(`zt-tour-popup-arrow-align-${arrowAlignment}`);
478
- }
479
- }
480
-
481
- getPopupDimensions() {
482
- const boundingClientRect =
483
- this.options.popup.wrapper.getBoundingClientRect();
484
-
485
- const stagePadding = this.options.stagePadding || 0;
486
- const popupOffset = this.options.popupOffset || 0;
487
-
488
- return {
489
- width: boundingClientRect.width + stagePadding + popupOffset,
490
- height: boundingClientRect.height + stagePadding + popupOffset,
491
-
492
- realWidth: boundingClientRect.width,
493
- realHeight: boundingClientRect.height,
494
- };
495
- }
496
-
497
- // create svg
498
- createOverlaySvg(stage) {
499
- const windowX = window.innerWidth;
500
- const windowY = window.innerHeight;
501
-
502
- const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
503
- svg.classList.add("zt-tour-overlay", "zt-tour-overlay-animated");
504
-
505
- svg.setAttribute("viewBox", `0 0 ${windowX} ${windowY}`);
506
- svg.setAttribute("xmlSpace", "preserve");
507
- svg.setAttribute("xmlnsXlink", "http://www.w3.org/1999/xlink");
508
- svg.setAttribute("version", "1.1");
509
- svg.setAttribute("preserveAspectRatio", "xMinYMin slice");
510
-
511
- svg.style.fillRule = "evenodd";
512
- svg.style.clipRule = "evenodd";
513
- svg.style.strokeLinejoin = "round";
514
- svg.style.strokeMiterlimit = "2";
515
- svg.style.zIndex = "10000";
516
- svg.style.position = "fixed";
517
- svg.style.top = "0";
518
- svg.style.left = "0";
519
- svg.style.width = "100%";
520
- svg.style.height = "100%";
521
-
522
- const stagePath = document.createElementNS(
523
- "http://www.w3.org/2000/svg",
524
- "path"
525
- );
526
-
527
- stagePath.setAttribute("d", this.generateStageSvgPathString(stage));
528
-
529
- stagePath.style.fill = this.options.overlayColor || "rgb(0,0,0)";
530
- stagePath.style.opacity = `${this.options.overlayOpacity}`;
531
- stagePath.style.pointerEvents = "auto";
532
- stagePath.style.cursor = "auto";
533
-
534
- svg.addEventListener("click", () => {
535
- if (this.getOption("allowBackdropClose")) {
536
- this.destroyTour();
537
- if (typeof this.onClose === "function") {
538
- this.onClose();
539
- }
540
- }
541
- });
542
-
543
- svg.appendChild(stagePath);
544
-
545
- return svg;
546
- }
547
-
548
- // generate svg path
549
- generateStageSvgPathString(stage) {
550
- const windowX = window.innerWidth;
551
- const windowY = window.innerHeight;
552
-
553
- const stagePadding = this.options.stagePadding || 0;
554
- const stageRadius = this.options.stageRadius || 0;
555
-
556
- const stageWidth = stage.width + stagePadding * 2;
557
- const stageHeight = stage.height + stagePadding * 2;
558
-
559
- // prevent glitches when stage is too small for radius
560
- const limitedRadius = Math.min(
561
- stageRadius,
562
- stageWidth / 2,
563
- stageHeight / 2
564
- );
565
-
566
- // no value below 0 allowed + round down
567
- const normalizedRadius = Math.floor(Math.max(limitedRadius, 0));
568
-
569
- const highlightBoxX = stage.x - stagePadding + normalizedRadius;
570
- const highlightBoxY = stage.y - stagePadding;
571
- const highlightBoxWidth = stageWidth - normalizedRadius * 2;
572
- const highlightBoxHeight = stageHeight - normalizedRadius * 2;
573
-
574
- return `M${windowX},0L0,0L0,${windowY}L${windowX},${windowY}L${windowX},0Z
575
- M${highlightBoxX},${highlightBoxY} h${highlightBoxWidth} a${normalizedRadius},${normalizedRadius} 0 0 1 ${normalizedRadius},${normalizedRadius} v${highlightBoxHeight} a${normalizedRadius},${normalizedRadius} 0 0 1 -${normalizedRadius},${normalizedRadius} h-${highlightBoxWidth} a${normalizedRadius},${normalizedRadius} 0 0 1 -${normalizedRadius},-${normalizedRadius} v-${highlightBoxHeight} a${normalizedRadius},${normalizedRadius} 0 0 1 ${normalizedRadius},-${normalizedRadius} z`;
576
- }
577
-
578
- repositionPopup(element, step) {
579
- const popup = this.options.popup;
580
- let { align = "start", side = "left" } = step?.popup || {};
581
-
582
- align = step.element ? align : "over";
583
- side = step.element ? side : "over";
584
-
585
- // Configure the popup positioning
586
- const requiredAlignment = align;
587
- const requiredSide = side;
588
- const popupPadding = this.options.stagePadding;
589
-
590
- const popupDimensions = this.getPopupDimensions();
591
- const popupArrowDimensions = popup.arrow.getBoundingClientRect();
592
- const elementDimensions = element.getBoundingClientRect();
593
-
594
- const topValue = elementDimensions.top - popupDimensions.height;
595
- let isTopOptimal = topValue >= 0;
596
-
597
- const bottomValue =
598
- window.innerHeight -
599
- (elementDimensions.bottom + popupDimensions.height);
600
- let isBottomOptimal = bottomValue >= 0;
601
-
602
- const leftValue = elementDimensions.left - popupDimensions.width;
603
- let isLeftOptimal = leftValue >= 0;
604
-
605
- const rightValue =
606
- window.innerWidth - (elementDimensions.right + popupDimensions.width);
607
- let isRightOptimal = rightValue >= 0;
608
-
609
- const noneOptimal =
610
- !isTopOptimal && !isBottomOptimal && !isLeftOptimal && !isRightOptimal;
611
- let popupRenderedSide = requiredSide;
612
-
613
- if (requiredSide === "top" && isTopOptimal) {
614
- isRightOptimal = isLeftOptimal = isBottomOptimal = false;
615
- } else if (requiredSide === "bottom" && isBottomOptimal) {
616
- isRightOptimal = isLeftOptimal = isTopOptimal = false;
617
- } else if (requiredSide === "left" && isLeftOptimal) {
618
- isRightOptimal = isTopOptimal = isBottomOptimal = false;
619
- } else if (requiredSide === "right" && isRightOptimal) {
620
- isLeftOptimal = isTopOptimal = isBottomOptimal = false;
621
- }
622
-
623
- if (requiredSide === "over") {
624
- const leftToSet = window.innerWidth / 2 - popupDimensions.realWidth / 2;
625
- const topToSet =
626
- window.innerHeight / 2 - popupDimensions.realHeight / 2;
627
-
628
- popup.wrapper.style.left = `${leftToSet}px`;
629
- popup.wrapper.style.right = `auto`;
630
- popup.wrapper.style.top = `${topToSet}px`;
631
- popup.wrapper.style.bottom = `auto`;
632
- } else if (noneOptimal) {
633
- const leftValue =
634
- window.innerWidth / 2 - popupDimensions?.realWidth / 2;
635
- const bottomValue = 10;
636
-
637
- popup.wrapper.style.left = `${leftValue}px`;
638
- popup.wrapper.style.right = `auto`;
639
- popup.wrapper.style.bottom = `${bottomValue}px`;
640
- popup.wrapper.style.top = `auto`;
641
- } else if (isLeftOptimal) {
642
- const leftToSet = Math.min(
643
- leftValue,
644
- window.innerWidth -
645
- popupDimensions?.realWidth -
646
- popupArrowDimensions.width
647
- );
648
-
649
- const topToSet = this.calculateTopForLeftRight(requiredAlignment, {
650
- elementDimensions,
651
- popupDimensions,
652
- popupPadding,
653
- popupArrowDimensions,
654
- });
655
-
656
- popup.wrapper.style.left = `${leftToSet}px`;
657
- popup.wrapper.style.top = `${topToSet}px`;
658
- popup.wrapper.style.bottom = `auto`;
659
- popup.wrapper.style.right = "auto";
660
-
661
- popupRenderedSide = "left";
662
- } else if (isRightOptimal) {
663
- const rightToSet = Math.min(
664
- rightValue,
665
- window.innerWidth -
666
- popupDimensions?.realWidth -
667
- popupArrowDimensions.width
668
- );
669
- const topToSet = this.calculateTopForLeftRight(requiredAlignment, {
670
- elementDimensions,
671
- popupDimensions,
672
- popupPadding,
673
- popupArrowDimensions,
674
- });
675
-
676
- popup.wrapper.style.right = `${rightToSet}px`;
677
- popup.wrapper.style.top = `${topToSet}px`;
678
- popup.wrapper.style.bottom = `auto`;
679
- popup.wrapper.style.left = "auto";
680
-
681
- popupRenderedSide = "right";
682
- } else if (isTopOptimal) {
683
- const topToSet = Math.min(
684
- topValue,
685
- window.innerHeight -
686
- popupDimensions.realHeight -
687
- popupArrowDimensions.width
688
- );
689
- let leftToSet = this.calculateLeftForTopBottom(requiredAlignment, {
690
- elementDimensions,
691
- popupDimensions,
692
- popupPadding,
693
- popupArrowDimensions,
694
- });
695
-
696
- popup.wrapper.style.top = `${topToSet}px`;
697
- popup.wrapper.style.left = `${leftToSet}px`;
698
- popup.wrapper.style.bottom = `auto`;
699
- popup.wrapper.style.right = "auto";
700
-
701
- popupRenderedSide = "top";
702
- } else if (isBottomOptimal) {
703
- const bottomToSet = Math.min(
704
- bottomValue,
705
- window.innerHeight -
706
- popupDimensions?.realHeight -
707
- popupArrowDimensions.width
708
- );
709
-
710
- let leftToSet = this.calculateLeftForTopBottom(requiredAlignment, {
711
- elementDimensions,
712
- popupDimensions,
713
- popupPadding,
714
- popupArrowDimensions,
715
- });
716
-
717
- popup.wrapper.style.left = `${leftToSet}px`;
718
- popup.wrapper.style.bottom = `${bottomToSet}px`;
719
- popup.wrapper.style.top = `auto`;
720
- popup.wrapper.style.right = "auto";
721
-
722
- popupRenderedSide = "bottom";
723
- }
724
-
725
- if (!noneOptimal) {
726
- this.renderPopupArrow(requiredAlignment, popupRenderedSide, element);
727
- } else {
728
- popup.arrow.classList.add("zt-tour-d-none");
729
- }
730
- }
731
-
732
- calculateLeftForTopBottom(alignment, config) {
733
- const {
734
- elementDimensions,
735
- popupDimensions,
736
- popupPadding,
737
- popupArrowDimensions,
738
- } = config;
739
-
740
- if (alignment === "start") {
741
- return Math.max(
742
- Math.min(
743
- elementDimensions.left - popupPadding,
744
- window.innerWidth -
745
- popupDimensions.realWidth -
746
- popupArrowDimensions.width
747
- ),
748
- popupArrowDimensions.width
749
- );
750
- }
751
-
752
- if (alignment === "end") {
753
- return Math.max(
754
- Math.min(
755
- elementDimensions.left -
756
- popupDimensions?.realWidth +
757
- elementDimensions.width +
758
- popupPadding,
759
- window.innerWidth -
760
- popupDimensions?.realWidth -
761
- popupArrowDimensions.width
762
- ),
763
- popupArrowDimensions.width
764
- );
765
- }
766
-
767
- if (alignment === "center") {
768
- return Math.max(
769
- Math.min(
770
- elementDimensions.left +
771
- elementDimensions.width / 2 -
772
- popupDimensions?.realWidth / 2,
773
- window.innerWidth -
774
- popupDimensions?.realWidth -
775
- popupArrowDimensions.width
776
- ),
777
- popupArrowDimensions.width
778
- );
779
- }
780
-
781
- return 0;
782
- }
783
-
784
- calculateTopForLeftRight(alignment, config) {
785
- const {
786
- elementDimensions,
787
- popupDimensions,
788
- popupPadding,
789
- popupArrowDimensions,
790
- } = config;
791
-
792
- if (alignment === "start") {
793
- return Math.max(
794
- Math.min(
795
- elementDimensions.top - popupPadding,
796
- window.innerHeight -
797
- popupDimensions.realHeight -
798
- popupArrowDimensions.width
799
- ),
800
- popupArrowDimensions.width
801
- );
802
- }
803
-
804
- if (alignment === "end") {
805
- return Math.max(
806
- Math.min(
807
- elementDimensions.top -
808
- popupDimensions?.realHeight +
809
- elementDimensions.height +
810
- popupPadding,
811
- window.innerHeight -
812
- popupDimensions?.realHeight -
813
- popupArrowDimensions.width
814
- ),
815
- popupArrowDimensions.width
816
- );
817
- }
818
-
819
- if (alignment === "center") {
820
- return Math.max(
821
- Math.min(
822
- elementDimensions.top +
823
- elementDimensions.height / 2 -
824
- popupDimensions?.realHeight / 2,
825
- window.innerHeight -
826
- popupDimensions?.realHeight -
827
- popupArrowDimensions.width
828
- ),
829
- popupArrowDimensions.width
830
- );
831
- }
832
-
833
- return 0;
834
- }
835
-
836
- destroyTour() {
837
- let popup = this.options.popup;
838
- const overlaySvg = document.querySelector(".zt-tour-overlay");
839
-
840
- if (popup.wrapper) {
841
- popup.wrapper.remove();
842
- }
843
- if (overlaySvg) {
844
- overlaySvg.remove();
845
- }
846
-
847
- this.options.activeStagePosition = null;
848
- this.options.overlaySvg = null;
849
- this.destroyEvents();
850
- }
851
-
852
- transitionStage(timeDiff, duration, from, to) {
853
- let activeStagePosition = this.getOption("activeStagePosition");
854
-
855
- const fromDefinition = activeStagePosition
856
- ? activeStagePosition
857
- : from.getBoundingClientRect();
858
- const toDefinition = to.getBoundingClientRect();
859
-
860
- const x = this.easeInOutQuad(
861
- timeDiff,
862
- fromDefinition.x,
863
- toDefinition.x - fromDefinition.x,
864
- duration
865
- );
866
- const y = this.easeInOutQuad(
867
- timeDiff,
868
- fromDefinition.y,
869
- toDefinition.y - fromDefinition.y,
870
- duration
871
- );
872
- const width = this.easeInOutQuad(
873
- timeDiff,
874
- fromDefinition.width,
875
- toDefinition.width - fromDefinition.width,
876
- duration
877
- );
878
- const height = this.easeInOutQuad(
879
- timeDiff,
880
- fromDefinition.height,
881
- toDefinition.height - fromDefinition.height,
882
- duration
883
- );
884
-
885
- activeStagePosition = {
886
- x,
887
- y,
888
- width,
889
- height,
890
- };
891
-
892
- this.renderOverlay(activeStagePosition);
893
- this.setOption("activeStagePosition", activeStagePosition);
894
- }
895
-
896
- renderOverlay(stagePosition) {
897
- const overlaySvg = this.getOption("overlaySvg");
898
- if (!overlaySvg) {
899
- this.addOverlay(stagePosition);
900
- return;
901
- }
902
-
903
- const pathElement = overlaySvg.firstElementChild;
904
- if (pathElement?.tagName !== "path") {
905
- throw new Error("no path element found in stage svg");
906
- }
907
-
908
- pathElement.setAttribute(
909
- "d",
910
- this.generateStageSvgPathString(stagePosition)
911
- );
912
- }
913
-
914
- changeHighlight(toElement, toStep, toStepIndex) {
915
- const duration = this.getOption("animationDuration");
916
- let currentStepEle =
917
- this.options.steps[this.options.currentStep]?.element;
918
-
919
- const start = Date.now();
920
-
921
- const fromElement = currentStepEle
922
- ? document.querySelector(currentStepEle)
923
- : toElement;
924
-
925
- const isFirstHighlight = !fromElement || fromElement === toElement;
926
-
927
- const isAnimatedTour = this.getOption("animate");
928
-
929
- const hasDelayedPopup = !isFirstHighlight && isAnimatedTour;
930
- let isPopupRendered = false;
931
-
932
- this.hidePopup();
933
-
934
- this.setOption("currentStep", toStepIndex);
935
-
936
- const animate = () => {
937
- const timeDiff = Date.now() - start;
938
- const timeRemaining = duration - timeDiff;
939
- const isHalfwayThrough = timeRemaining <= duration / 2;
940
- if (
941
- toStep.popup &&
942
- isHalfwayThrough &&
943
- !isPopupRendered &&
944
- hasDelayedPopup
945
- ) {
946
- this.renderPopup(toElement, toStep);
947
- isPopupRendered = true;
948
- }
949
-
950
- if (this.getOption("animate") && timeDiff < duration) {
951
- this.transitionStage(timeDiff, duration, fromElement, toElement);
952
- } else {
953
- this.trackActiveElement(toElement);
954
- }
955
- if (timeRemaining >= 0 && this.getOption("animate")) {
956
- window.requestAnimationFrame(animate);
957
- }
958
- };
959
-
960
- window.requestAnimationFrame(animate);
961
-
962
- this.bringInView(toElement);
963
- if (!hasDelayedPopup && toStep.popup) {
964
- this.renderPopup(toElement, toStep);
965
- }
966
- }
967
-
968
- bringInView(element) {
969
- if (!element || this.isElementInView(element)) {
970
- return;
971
- }
972
-
973
- const shouldSmoothScroll = this.getOption("smoothScroll");
974
-
975
- element.scrollIntoView({
976
- behavior:
977
- !shouldSmoothScroll || this.isScrollableParent(element)
978
- ? "auto"
979
- : "smooth",
980
- inline: "center",
981
- block: "center",
982
- });
983
- }
984
-
985
- isElementInView(element) {
986
- const rect = element.getBoundingClientRect();
987
-
988
- return (
989
- rect.top >= 0 &&
990
- rect.left >= 0 &&
991
- rect.bottom <=
992
- (window.innerHeight || document.documentElement.clientHeight) &&
993
- rect.right <=
994
- (window.innerWidth || document.documentElement.clientWidth)
995
- );
996
- }
997
-
998
- isScrollableParent(e) {
999
- if (!e || !e.parentElement) {
1000
- return;
1001
- }
1002
- const parent = e.parentElement;
1003
- return parent.scrollHeight > parent.clientHeight;
1004
- }
1005
-
1006
- trackActiveElement(element) {
1007
- if (!element) {
1008
- return;
1009
- }
1010
-
1011
- const definition = element.getBoundingClientRect();
1012
-
1013
- const activeStagePosition = {
1014
- x: definition.x,
1015
- y: definition.y,
1016
- width: definition.width,
1017
- height: definition.height,
1018
- };
1019
-
1020
- this.setOption("activeStagePosition", activeStagePosition);
1021
-
1022
- this.renderOverlay(activeStagePosition);
1023
- }
1024
-
1025
- addOverlay(stagePosition) {
1026
- const overlaySvg = this.createOverlaySvg(stagePosition);
1027
- document.body.appendChild(overlaySvg);
1028
-
1029
- this.setOption("overlaySvg", overlaySvg);
1030
- }
1031
-
1032
- easeInOutQuad(timeDiff, initialValue, amountOfChange, duration) {
1033
- if ((timeDiff /= duration / 2) < 1) {
1034
- return (amountOfChange / 2) * timeDiff * timeDiff + initialValue;
1035
- }
1036
- return (
1037
- (-amountOfChange / 2) * (--timeDiff * (timeDiff - 2) - 1) + initialValue
1038
- );
1039
- }
1040
-
1041
- refreshOverlay() {
1042
- const activeStagePosition = this.getOption("activeStagePosition");
1043
- const overlaySvg = this.getOption("overlaySvg");
1044
-
1045
- if (!activeStagePosition) {
1046
- return;
1047
- }
1048
-
1049
- if (!overlaySvg) {
1050
- console.warn("No svg found.");
1051
- return;
1052
- }
1053
-
1054
- const windowX = window.innerWidth;
1055
- const windowY = window.innerHeight;
1056
-
1057
- overlaySvg.setAttribute("viewBox", `0 0 ${windowX} ${windowY}`);
1058
- }
1059
-
1060
- refreshStep() {
1061
- const currentStep = this.getOption("currentStep");
1062
- if(!currentStep) return;
1063
- const step = this.getOption("steps")[currentStep];
1064
- const element = document.querySelector(step.element);
1065
- this.trackActiveElement(element);
1066
- this.refreshOverlay();
1067
- this.repositionPopup(element, step);
1068
- }
1069
-
1070
- addDummyElement() {
1071
- const isDummyElement = document.getElementById("zt-popup-dummy-element");
1072
- if (isDummyElement) {
1073
- return isDummyElement;
1074
- }
1075
-
1076
- let element = document.createElement("div");
1077
-
1078
- element.id = "zt-popup-dummy-element";
1079
- element.style.width = "0";
1080
- element.style.height = "0";
1081
- element.style.pointerEvents = "none";
1082
- element.style.opacity = "0";
1083
- element.style.position = "fixed";
1084
- element.style.top = "50%";
1085
- element.style.left = "50%";
1086
-
1087
- document.body.appendChild(element);
1088
-
1089
- return element;
1090
- }
1091
- //
1092
- }
1093
-
1094
- global.ztTour = ztTour;
1095
- })(this);
1
+ /* =========================================================
2
+ * Created by Sunil Solanki
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ * ========================================================= */
16
+
17
+ (function (global) {
18
+ class ztTour {
19
+ constructor(options, templates) {
20
+ this.initializeOptions(options);
21
+ this.initializeTemplate(templates);
22
+
23
+ this.popup = null;
24
+ this.overlaySvg = null;
25
+ this.currentStep = 0;
26
+ this.activeStagePosition = null;
27
+
28
+ this.onKeyUp = this.onKeyUp.bind(this);
29
+ this.refreshStep = this.refreshStep.bind(this);
30
+
31
+ this.initEvents();
32
+
33
+ this.onNextClick = options.onNextClick || null;
34
+ this.onClose = options.onClose || null;
35
+ this.onPreviousClick = options.onPreviousClick || null;
36
+ }
37
+
38
+ // initialize Options
39
+ initializeOptions(opt) {
40
+ if (opt == null) {
41
+ opt = {};
42
+ }
43
+
44
+ this.options = {
45
+ steps: opt.steps || [],
46
+ overlayOpacity: opt.overlayOpacity || 0.7,
47
+ stagePadding: opt.stagePadding || 10,
48
+ popupOffset: opt.popupOffset || 10,
49
+ stageRadius: opt.stageRadius || 5,
50
+ overlayColor: opt.overlayColor || "#000",
51
+ animate: opt.animate !== undefined ? opt.animate : true,
52
+ smoothScroll: opt.smoothScroll || false,
53
+ visibleButtons: opt.visibleButtons || ["next", "previous", "close"],
54
+ disableButtons: opt.disableButtons || [],
55
+ showProgress: opt.showProgress || false,
56
+ nextBtnText: opt.nextBtnText || "Next &rarr;",
57
+ prevBtnText: opt.prevBtnText || "&larr; Previous",
58
+ doneBtnText: opt.doneBtnText || "Done",
59
+ allowBackdropClose: opt.allowBackdropClose !== undefined ? opt.allowBackdropClose : true,
60
+ popupClass: opt.popupClass || false,
61
+ keyboardControl: opt.keyboardControl !== undefined ? opt.keyboardControl : true,
62
+ animationDuration: opt.animationDuration || 400,
63
+ };
64
+ }
65
+
66
+ initializeTemplate(templ) {
67
+ if (templ == null) {
68
+ templ = {};
69
+ }
70
+ this.templates = {
71
+ progressText:
72
+ templ.progressText || ((current, total) => `${current} of ${total}`),
73
+ };
74
+ }
75
+
76
+ initEvents() {
77
+ window.addEventListener("keyup", this.onKeyUp, false);
78
+ window.addEventListener("resize", this.refreshStep, false);
79
+ window.addEventListener("scroll", this.refreshStep, false);
80
+ }
81
+
82
+ onKeyUp(e) {
83
+ const keyboardControl = this.getOption("keyboardControl");
84
+ if (keyboardControl) {
85
+ if (e.key === "Escape") {
86
+ this.destroyTour();
87
+ } else if (e.key === "ArrowRight") {
88
+ this.highlightStep(this.getOption("currentStep") + 1);
89
+ } else if (e.key === "ArrowLeft") {
90
+ this.highlightStep(this.getOption("currentStep") - 1);
91
+ }
92
+ }
93
+ }
94
+
95
+ destroyEvents() {
96
+ window.removeEventListener("keyup", this.onKeyUp);
97
+ window.removeEventListener("resize", this.refreshStep);
98
+ window.removeEventListener("scroll", this.refreshStep);
99
+ }
100
+
101
+ getOption(key) {
102
+ if (!key) {
103
+ return this.options;
104
+ }
105
+ return this.options[key];
106
+ }
107
+
108
+ setOption(key, val) {
109
+ this.options[key] = val;
110
+ }
111
+
112
+ throwError(error) {
113
+ throw new Error(error);
114
+ }
115
+
116
+ start() {
117
+ this.highlightStep(0);
118
+ }
119
+
120
+ showHint(hint, isAnnouncement = false){
121
+ this.removeHint(isAnnouncement);
122
+
123
+ let popupType = isAnnouncement ? "announcement" : "hint";
124
+
125
+ let hintEle = document.createElement("div");
126
+ let hintBackdrop = document.createElement("div");
127
+ hintBackdrop.classList.add(`zt-tour-${popupType}-backdrop`);
128
+ hintBackdrop.addEventListener('click',()=>{
129
+ hintEle.remove();
130
+ hintBackdrop.remove();
131
+ })
132
+ hintEle.innerHTML = hint.innerHTML;
133
+ hintEle.classList.add(`zt-tour-${popupType}`);
134
+
135
+ let dimentions;
136
+ if(!isAnnouncement){
137
+ dimentions = document.querySelector(hint.element).getBoundingClientRect();
138
+ }
139
+
140
+ document.body.appendChild(hintBackdrop);
141
+ document.body.appendChild(hintEle);
142
+
143
+
144
+ hintEle.style.top = isAnnouncement ? "50%" : dimentions.top + dimentions.height + 10 + "px";
145
+ hintEle.style.left = isAnnouncement ? "50%" : dimentions.left + 10 + "px";
146
+ hintEle.style.transform = isAnnouncement ? "translate(-50%, -50%)" : "";
147
+ }
148
+
149
+ showAnnouncement(announcement){
150
+ announcement = {innerHTML: announcement};
151
+ this.showHint(announcement, true);
152
+ }
153
+
154
+ removeHint(isAnnouncement = false){
155
+ let popupType = isAnnouncement ? "announcement" : "hint";
156
+ let oldHint = document.querySelectorAll(`.zt-tour-${popupType}`);
157
+ let oldHintBackdrop = document.querySelectorAll(`.zt-tour-${popupType}-backdrop`);
158
+
159
+ Array.from(oldHint).forEach((hint)=>hint.remove())
160
+ Array.from(oldHintBackdrop).forEach((hintBackdrop)=>hintBackdrop.remove())
161
+ }
162
+
163
+ removeAnnouncement(){
164
+ this.removeHint(true);
165
+ }
166
+
167
+ highlightStep(currentStep) {
168
+ if (0 > currentStep || currentStep > this.getOption("steps").length - 1) {
169
+ this.destroyTour();
170
+ return;
171
+ }
172
+
173
+ let step = this.options.steps[currentStep];
174
+ let element = document.querySelector(step?.element);
175
+
176
+ if (!element) {
177
+ element = this.addDummyElement();
178
+ }
179
+
180
+ this.changeHighlight(element, step, currentStep);
181
+ }
182
+
183
+ createPopup() {
184
+ const wrapper = document.createElement("div");
185
+ wrapper.classList.add("zt-tour-popup");
186
+ if (this.getOption("popupClass")) {
187
+ wrapper.classList.add(this.getOption("popupClass").trim());
188
+ }
189
+
190
+ const arrow = document.createElement("div");
191
+ arrow.classList.add("zt-tour-popup-arrow");
192
+
193
+ const title = document.createElement("header");
194
+ title.id = "zt-tour-popup-title";
195
+ title.classList.add("zt-tour-popup-title");
196
+ title.style.display = "none";
197
+ title.innerText = "Popup Title";
198
+
199
+ const description = document.createElement("div");
200
+ description.id = "zt-tour-popup-description";
201
+ description.classList.add("zt-tour-popup-description");
202
+ description.style.display = "none";
203
+ description.innerText = "Popup description is here";
204
+
205
+ const closeButton = document.createElement("button");
206
+ closeButton.type = "button";
207
+ closeButton.classList.add("zt-tour-popup-close-btn");
208
+ closeButton.setAttribute("aria-label", "Close");
209
+ closeButton.innerHTML = "&times;";
210
+
211
+ const footer = document.createElement("footer");
212
+ footer.classList.add("zt-tour-popup-footer");
213
+
214
+ const progress = document.createElement("span");
215
+ progress.classList.add("zt-tour-popup-progress-text");
216
+ progress.innerText = "";
217
+
218
+ const footerButtons = document.createElement("span");
219
+ footerButtons.classList.add("zt-tour-popup-navigation-btns");
220
+
221
+ const previousButton = document.createElement("button");
222
+ previousButton.type = "button";
223
+ previousButton.classList.add("zt-tour-popup-prev-btn");
224
+ previousButton.innerHTML = "&larr; Previous";
225
+
226
+ const nextButton = document.createElement("button");
227
+ nextButton.type = "button";
228
+ nextButton.classList.add("zt-tour-popup-next-btn");
229
+ nextButton.innerHTML = "Next &rarr;";
230
+
231
+ footerButtons.appendChild(previousButton);
232
+ footerButtons.appendChild(nextButton);
233
+ footer.appendChild(progress);
234
+ footer.appendChild(footerButtons);
235
+
236
+ wrapper.appendChild(closeButton);
237
+ wrapper.appendChild(arrow);
238
+ wrapper.appendChild(title);
239
+ wrapper.appendChild(description);
240
+ wrapper.appendChild(footer);
241
+
242
+ return {
243
+ wrapper,
244
+ arrow,
245
+ title,
246
+ description,
247
+ footer,
248
+ previousButton,
249
+ nextButton,
250
+ closeButton,
251
+ footerButtons,
252
+ progress,
253
+ };
254
+ }
255
+
256
+ renderPopup(element, step) {
257
+ let popup = this.createPopup();
258
+ this.options.popup = popup;
259
+
260
+ let oldPopups = document.querySelectorAll(".zt-tour-popup");
261
+
262
+ // remove all old popups
263
+ Array.from(oldPopups).forEach((ele) => {
264
+ ele.remove();
265
+ });
266
+
267
+ document.body.appendChild(popup.wrapper);
268
+
269
+ let {
270
+ title,
271
+ description,
272
+ visibleButtons,
273
+ disableButtons,
274
+ showProgress,
275
+ nextBtnText = this.options.nextBtnText,
276
+ prevBtnText = this.options.prevBtnText,
277
+ progressText = this.templates.progressText(
278
+ this.options.currentStep + 1,
279
+ this.options.steps.length
280
+ ),
281
+ } = step.popup || {};
282
+
283
+ if (this.options.currentStep + 1 === this.options.steps.length) {
284
+ nextBtnText = this.options.doneBtnText;
285
+ }
286
+
287
+ if (this.options.currentStep === 0) {
288
+ popup.previousButton.disabled = true;
289
+ }
290
+
291
+ popup.nextButton.innerHTML = nextBtnText;
292
+ popup.previousButton.innerHTML = prevBtnText;
293
+ popup.progress.innerHTML = progressText;
294
+
295
+ if (title) {
296
+ popup.title.innerHTML = title;
297
+ popup.title.style.display = "block";
298
+ } else {
299
+ popup.title.style.display = "none";
300
+ }
301
+
302
+ if (description) {
303
+ popup.description.innerHTML = description;
304
+ popup.description.style.display = "block";
305
+ } else {
306
+ popup.description.style.display = "none";
307
+ }
308
+
309
+ const showButtonsOption =
310
+ visibleButtons || this.getOption("visibleButtons");
311
+ const isShowProgress = showProgress || this.getOption("showProgress");
312
+
313
+ const isShowFooter =
314
+ showButtonsOption.includes("next") ||
315
+ showButtonsOption?.includes("previous") ||
316
+ isShowProgress;
317
+
318
+ popup.closeButton.style.display = showButtonsOption.includes("close")
319
+ ? "block"
320
+ : "none";
321
+
322
+ if (isShowFooter) {
323
+ popup.footer.style.display = "flex";
324
+
325
+ popup.progress.style.display = isShowProgress ? "block" : "none";
326
+ popup.nextButton.style.display = showButtonsOption.includes("next")
327
+ ? "block"
328
+ : "none";
329
+ popup.previousButton.style.display = showButtonsOption.includes(
330
+ "previous"
331
+ )
332
+ ? "block"
333
+ : "none";
334
+ } else {
335
+ popup.footer.style.display = "none";
336
+ }
337
+
338
+ const disabledButtonsOption =
339
+ disableButtons || this.getOption("disableButtons");
340
+ if (disabledButtonsOption?.includes("next")) {
341
+ popup.nextButton.disabled = true;
342
+ }
343
+
344
+ if (disabledButtonsOption?.includes("previous")) {
345
+ popup.previousButton.disabled = true;
346
+ }
347
+
348
+ if (disabledButtonsOption?.includes("close")) {
349
+ popup.closeButton.disabled = true;
350
+ }
351
+
352
+ popup.nextButton.addEventListener("click", () => {
353
+ let isOutOfIndex =
354
+ this.options.currentStep + 1 < 0 ||
355
+ this.options.currentStep + 1 >= this.getOption("steps").length;
356
+ this.highlightStep(this.options.currentStep + 1);
357
+ if (typeof this.onNextClick === "function" && !isOutOfIndex) {
358
+ this.onNextClick(this.options.currentStep);
359
+ }
360
+ });
361
+
362
+ popup.previousButton.addEventListener("click", () => {
363
+ this.highlightStep(this.options.currentStep - 1);
364
+ if (typeof this.onPreviousClick === "function") {
365
+ this.onPreviousClick(this.options.currentStep);
366
+ }
367
+ });
368
+
369
+ popup.closeButton.addEventListener("click", () => {
370
+ this.destroyTour();
371
+ if (typeof this.onClose === "function") {
372
+ this.onClose();
373
+ }
374
+ });
375
+
376
+ this.repositionPopup(element, step);
377
+ }
378
+
379
+ hidePopup() {
380
+ const popup = this.getOption("popup");
381
+ if (!popup) {
382
+ return;
383
+ }
384
+
385
+ popup.wrapper.style.display = "none";
386
+ }
387
+
388
+ renderPopupArrow(alignment, side, element) {
389
+ const elementDimensions = element.getBoundingClientRect();
390
+ const popupDimensions = this.getPopupDimensions();
391
+ const popupArrow = this.options.popup.arrow;
392
+
393
+ const popupWidth = popupDimensions.width;
394
+ const windowWidth = window.innerWidth;
395
+ const elementWidth = elementDimensions.width;
396
+ const elementLeft = elementDimensions.left;
397
+
398
+ const popupHeight = popupDimensions.height;
399
+ const windowHeight = window.innerHeight;
400
+ const elementTop = elementDimensions.top;
401
+ const elementHeight = elementDimensions.height;
402
+
403
+ // Remove all arrow classes
404
+ popupArrow.className = "zt-tour-popup-arrow";
405
+
406
+ let arrowSide = side;
407
+ let arrowAlignment = alignment;
408
+
409
+ if (side === "top") {
410
+ if (elementLeft + elementWidth <= 0) {
411
+ arrowSide = "right";
412
+ arrowAlignment = "end";
413
+ } else if (elementLeft + elementWidth - popupWidth <= 0) {
414
+ arrowSide = "top";
415
+ arrowAlignment = "start";
416
+ }
417
+ if (elementLeft >= windowWidth) {
418
+ arrowSide = "left";
419
+ arrowAlignment = "end";
420
+ } else if (elementLeft + popupWidth >= windowWidth) {
421
+ arrowSide = "top";
422
+ arrowAlignment = "end";
423
+ }
424
+ } else if (side === "bottom") {
425
+ if (elementLeft + elementWidth <= 0) {
426
+ arrowSide = "right";
427
+ arrowAlignment = "start";
428
+ } else if (elementLeft + elementWidth - popupWidth <= 0) {
429
+ arrowSide = "bottom";
430
+ arrowAlignment = "start";
431
+ }
432
+ if (elementLeft >= windowWidth) {
433
+ arrowSide = "left";
434
+ arrowAlignment = "start";
435
+ } else if (elementLeft + popupWidth >= windowWidth) {
436
+ arrowSide = "bottom";
437
+ arrowAlignment = "end";
438
+ }
439
+ } else if (side === "left") {
440
+ if (elementTop + elementHeight <= 0) {
441
+ arrowSide = "bottom";
442
+ arrowAlignment = "end";
443
+ } else if (elementTop + elementHeight - popupHeight <= 0) {
444
+ arrowSide = "left";
445
+ arrowAlignment = "start";
446
+ }
447
+
448
+ if (elementTop >= windowHeight) {
449
+ arrowSide = "top";
450
+ arrowAlignment = "end";
451
+ } else if (elementTop + popupHeight >= windowHeight) {
452
+ arrowSide = "left";
453
+ arrowAlignment = "end";
454
+ }
455
+ } else if (side === "right") {
456
+ if (elementTop + elementHeight <= 0) {
457
+ arrowSide = "bottom";
458
+ arrowAlignment = "start";
459
+ } else if (elementTop + elementHeight - popupHeight <= 0) {
460
+ arrowSide = "right";
461
+ arrowAlignment = "start";
462
+ }
463
+
464
+ if (elementTop >= windowHeight) {
465
+ arrowSide = "top";
466
+ arrowAlignment = "start";
467
+ } else if (elementTop + popupHeight >= windowHeight) {
468
+ arrowSide = "right";
469
+ arrowAlignment = "end";
470
+ }
471
+ }
472
+
473
+ if (!arrowSide) {
474
+ popupArrow.classList.add("zt-tour-d-none");
475
+ } else {
476
+ popupArrow.classList.add(`zt-tour-popup-arrow-side-${arrowSide}`);
477
+ popupArrow.classList.add(`zt-tour-popup-arrow-align-${arrowAlignment}`);
478
+ }
479
+ }
480
+
481
+ getPopupDimensions() {
482
+ const boundingClientRect =
483
+ this.options.popup.wrapper.getBoundingClientRect();
484
+
485
+ const stagePadding = this.options.stagePadding || 0;
486
+ const popupOffset = this.options.popupOffset || 0;
487
+
488
+ return {
489
+ width: boundingClientRect.width + stagePadding + popupOffset,
490
+ height: boundingClientRect.height + stagePadding + popupOffset,
491
+
492
+ realWidth: boundingClientRect.width,
493
+ realHeight: boundingClientRect.height,
494
+ };
495
+ }
496
+
497
+ // create svg
498
+ createOverlaySvg(stage) {
499
+ const windowX = window.innerWidth;
500
+ const windowY = window.innerHeight;
501
+
502
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
503
+ svg.classList.add("zt-tour-overlay", "zt-tour-overlay-animated");
504
+
505
+ svg.setAttribute("viewBox", `0 0 ${windowX} ${windowY}`);
506
+ svg.setAttribute("xmlSpace", "preserve");
507
+ svg.setAttribute("xmlnsXlink", "http://www.w3.org/1999/xlink");
508
+ svg.setAttribute("version", "1.1");
509
+ svg.setAttribute("preserveAspectRatio", "xMinYMin slice");
510
+
511
+ svg.style.fillRule = "evenodd";
512
+ svg.style.clipRule = "evenodd";
513
+ svg.style.strokeLinejoin = "round";
514
+ svg.style.strokeMiterlimit = "2";
515
+ svg.style.zIndex = "10000";
516
+ svg.style.position = "fixed";
517
+ svg.style.top = "0";
518
+ svg.style.left = "0";
519
+ svg.style.width = "100%";
520
+ svg.style.height = "100%";
521
+
522
+ const stagePath = document.createElementNS(
523
+ "http://www.w3.org/2000/svg",
524
+ "path"
525
+ );
526
+
527
+ stagePath.setAttribute("d", this.generateStageSvgPathString(stage));
528
+
529
+ stagePath.style.fill = this.options.overlayColor || "rgb(0,0,0)";
530
+ stagePath.style.opacity = `${this.options.overlayOpacity}`;
531
+ stagePath.style.pointerEvents = "auto";
532
+ stagePath.style.cursor = "auto";
533
+
534
+ svg.addEventListener("click", () => {
535
+ if (this.getOption("allowBackdropClose")) {
536
+ this.destroyTour();
537
+ if (typeof this.onClose === "function") {
538
+ this.onClose();
539
+ }
540
+ }
541
+ });
542
+
543
+ svg.appendChild(stagePath);
544
+
545
+ return svg;
546
+ }
547
+
548
+ // generate svg path
549
+ generateStageSvgPathString(stage) {
550
+ const windowX = window.innerWidth;
551
+ const windowY = window.innerHeight;
552
+
553
+ const stagePadding = this.options.stagePadding || 0;
554
+ const stageRadius = this.options.stageRadius || 0;
555
+
556
+ const stageWidth = stage.width + stagePadding * 2;
557
+ const stageHeight = stage.height + stagePadding * 2;
558
+
559
+ // prevent glitches when stage is too small for radius
560
+ const limitedRadius = Math.min(
561
+ stageRadius,
562
+ stageWidth / 2,
563
+ stageHeight / 2
564
+ );
565
+
566
+ // no value below 0 allowed + round down
567
+ const normalizedRadius = Math.floor(Math.max(limitedRadius, 0));
568
+
569
+ const highlightBoxX = stage.x - stagePadding + normalizedRadius;
570
+ const highlightBoxY = stage.y - stagePadding;
571
+ const highlightBoxWidth = stageWidth - normalizedRadius * 2;
572
+ const highlightBoxHeight = stageHeight - normalizedRadius * 2;
573
+
574
+ return `M${windowX},0L0,0L0,${windowY}L${windowX},${windowY}L${windowX},0Z
575
+ M${highlightBoxX},${highlightBoxY} h${highlightBoxWidth} a${normalizedRadius},${normalizedRadius} 0 0 1 ${normalizedRadius},${normalizedRadius} v${highlightBoxHeight} a${normalizedRadius},${normalizedRadius} 0 0 1 -${normalizedRadius},${normalizedRadius} h-${highlightBoxWidth} a${normalizedRadius},${normalizedRadius} 0 0 1 -${normalizedRadius},-${normalizedRadius} v-${highlightBoxHeight} a${normalizedRadius},${normalizedRadius} 0 0 1 ${normalizedRadius},-${normalizedRadius} z`;
576
+ }
577
+
578
+ repositionPopup(element, step) {
579
+ const popup = this.options.popup;
580
+ let { align = "start", side = "left" } = step?.popup || {};
581
+
582
+ align = step.element ? align : "over";
583
+ side = step.element ? side : "over";
584
+
585
+ // Configure the popup positioning
586
+ const requiredAlignment = align;
587
+ const requiredSide = side;
588
+ const popupPadding = this.options.stagePadding;
589
+
590
+ const popupDimensions = this.getPopupDimensions();
591
+ const popupArrowDimensions = popup.arrow.getBoundingClientRect();
592
+ const elementDimensions = element.getBoundingClientRect();
593
+
594
+ const topValue = elementDimensions.top - popupDimensions.height;
595
+ let isTopOptimal = topValue >= 0;
596
+
597
+ const bottomValue =
598
+ window.innerHeight -
599
+ (elementDimensions.bottom + popupDimensions.height);
600
+ let isBottomOptimal = bottomValue >= 0;
601
+
602
+ const leftValue = elementDimensions.left - popupDimensions.width;
603
+ let isLeftOptimal = leftValue >= 0;
604
+
605
+ const rightValue =
606
+ window.innerWidth - (elementDimensions.right + popupDimensions.width);
607
+ let isRightOptimal = rightValue >= 0;
608
+
609
+ const noneOptimal =
610
+ !isTopOptimal && !isBottomOptimal && !isLeftOptimal && !isRightOptimal;
611
+ let popupRenderedSide = requiredSide;
612
+
613
+ if (requiredSide === "top" && isTopOptimal) {
614
+ isRightOptimal = isLeftOptimal = isBottomOptimal = false;
615
+ } else if (requiredSide === "bottom" && isBottomOptimal) {
616
+ isRightOptimal = isLeftOptimal = isTopOptimal = false;
617
+ } else if (requiredSide === "left" && isLeftOptimal) {
618
+ isRightOptimal = isTopOptimal = isBottomOptimal = false;
619
+ } else if (requiredSide === "right" && isRightOptimal) {
620
+ isLeftOptimal = isTopOptimal = isBottomOptimal = false;
621
+ }
622
+
623
+ if (requiredSide === "over") {
624
+ const leftToSet = window.innerWidth / 2 - popupDimensions.realWidth / 2;
625
+ const topToSet =
626
+ window.innerHeight / 2 - popupDimensions.realHeight / 2;
627
+
628
+ popup.wrapper.style.left = `${leftToSet}px`;
629
+ popup.wrapper.style.right = `auto`;
630
+ popup.wrapper.style.top = `${topToSet}px`;
631
+ popup.wrapper.style.bottom = `auto`;
632
+ } else if (noneOptimal) {
633
+ const leftValue =
634
+ window.innerWidth / 2 - popupDimensions?.realWidth / 2;
635
+ const bottomValue = 10;
636
+
637
+ popup.wrapper.style.left = `${leftValue}px`;
638
+ popup.wrapper.style.right = `auto`;
639
+ popup.wrapper.style.bottom = `${bottomValue}px`;
640
+ popup.wrapper.style.top = `auto`;
641
+ } else if (isLeftOptimal) {
642
+ const leftToSet = Math.min(
643
+ leftValue,
644
+ window.innerWidth -
645
+ popupDimensions?.realWidth -
646
+ popupArrowDimensions.width
647
+ );
648
+
649
+ const topToSet = this.calculateTopForLeftRight(requiredAlignment, {
650
+ elementDimensions,
651
+ popupDimensions,
652
+ popupPadding,
653
+ popupArrowDimensions,
654
+ });
655
+
656
+ popup.wrapper.style.left = `${leftToSet}px`;
657
+ popup.wrapper.style.top = `${topToSet}px`;
658
+ popup.wrapper.style.bottom = `auto`;
659
+ popup.wrapper.style.right = "auto";
660
+
661
+ popupRenderedSide = "left";
662
+ } else if (isRightOptimal) {
663
+ const rightToSet = Math.min(
664
+ rightValue,
665
+ window.innerWidth -
666
+ popupDimensions?.realWidth -
667
+ popupArrowDimensions.width
668
+ );
669
+ const topToSet = this.calculateTopForLeftRight(requiredAlignment, {
670
+ elementDimensions,
671
+ popupDimensions,
672
+ popupPadding,
673
+ popupArrowDimensions,
674
+ });
675
+
676
+ popup.wrapper.style.right = `${rightToSet}px`;
677
+ popup.wrapper.style.top = `${topToSet}px`;
678
+ popup.wrapper.style.bottom = `auto`;
679
+ popup.wrapper.style.left = "auto";
680
+
681
+ popupRenderedSide = "right";
682
+ } else if (isTopOptimal) {
683
+ const topToSet = Math.min(
684
+ topValue,
685
+ window.innerHeight -
686
+ popupDimensions.realHeight -
687
+ popupArrowDimensions.width
688
+ );
689
+ let leftToSet = this.calculateLeftForTopBottom(requiredAlignment, {
690
+ elementDimensions,
691
+ popupDimensions,
692
+ popupPadding,
693
+ popupArrowDimensions,
694
+ });
695
+
696
+ popup.wrapper.style.top = `${topToSet}px`;
697
+ popup.wrapper.style.left = `${leftToSet}px`;
698
+ popup.wrapper.style.bottom = `auto`;
699
+ popup.wrapper.style.right = "auto";
700
+
701
+ popupRenderedSide = "top";
702
+ } else if (isBottomOptimal) {
703
+ const bottomToSet = Math.min(
704
+ bottomValue,
705
+ window.innerHeight -
706
+ popupDimensions?.realHeight -
707
+ popupArrowDimensions.width
708
+ );
709
+
710
+ let leftToSet = this.calculateLeftForTopBottom(requiredAlignment, {
711
+ elementDimensions,
712
+ popupDimensions,
713
+ popupPadding,
714
+ popupArrowDimensions,
715
+ });
716
+
717
+ popup.wrapper.style.left = `${leftToSet}px`;
718
+ popup.wrapper.style.bottom = `${bottomToSet}px`;
719
+ popup.wrapper.style.top = `auto`;
720
+ popup.wrapper.style.right = "auto";
721
+
722
+ popupRenderedSide = "bottom";
723
+ }
724
+
725
+ if (!noneOptimal) {
726
+ this.renderPopupArrow(requiredAlignment, popupRenderedSide, element);
727
+ } else {
728
+ popup.arrow.classList.add("zt-tour-d-none");
729
+ }
730
+ }
731
+
732
+ calculateLeftForTopBottom(alignment, config) {
733
+ const {
734
+ elementDimensions,
735
+ popupDimensions,
736
+ popupPadding,
737
+ popupArrowDimensions,
738
+ } = config;
739
+
740
+ if (alignment === "start") {
741
+ return Math.max(
742
+ Math.min(
743
+ elementDimensions.left - popupPadding,
744
+ window.innerWidth -
745
+ popupDimensions.realWidth -
746
+ popupArrowDimensions.width
747
+ ),
748
+ popupArrowDimensions.width
749
+ );
750
+ }
751
+
752
+ if (alignment === "end") {
753
+ return Math.max(
754
+ Math.min(
755
+ elementDimensions.left -
756
+ popupDimensions?.realWidth +
757
+ elementDimensions.width +
758
+ popupPadding,
759
+ window.innerWidth -
760
+ popupDimensions?.realWidth -
761
+ popupArrowDimensions.width
762
+ ),
763
+ popupArrowDimensions.width
764
+ );
765
+ }
766
+
767
+ if (alignment === "center") {
768
+ return Math.max(
769
+ Math.min(
770
+ elementDimensions.left +
771
+ elementDimensions.width / 2 -
772
+ popupDimensions?.realWidth / 2,
773
+ window.innerWidth -
774
+ popupDimensions?.realWidth -
775
+ popupArrowDimensions.width
776
+ ),
777
+ popupArrowDimensions.width
778
+ );
779
+ }
780
+
781
+ return 0;
782
+ }
783
+
784
+ calculateTopForLeftRight(alignment, config) {
785
+ const {
786
+ elementDimensions,
787
+ popupDimensions,
788
+ popupPadding,
789
+ popupArrowDimensions,
790
+ } = config;
791
+
792
+ if (alignment === "start") {
793
+ return Math.max(
794
+ Math.min(
795
+ elementDimensions.top - popupPadding,
796
+ window.innerHeight -
797
+ popupDimensions.realHeight -
798
+ popupArrowDimensions.width
799
+ ),
800
+ popupArrowDimensions.width
801
+ );
802
+ }
803
+
804
+ if (alignment === "end") {
805
+ return Math.max(
806
+ Math.min(
807
+ elementDimensions.top -
808
+ popupDimensions?.realHeight +
809
+ elementDimensions.height +
810
+ popupPadding,
811
+ window.innerHeight -
812
+ popupDimensions?.realHeight -
813
+ popupArrowDimensions.width
814
+ ),
815
+ popupArrowDimensions.width
816
+ );
817
+ }
818
+
819
+ if (alignment === "center") {
820
+ return Math.max(
821
+ Math.min(
822
+ elementDimensions.top +
823
+ elementDimensions.height / 2 -
824
+ popupDimensions?.realHeight / 2,
825
+ window.innerHeight -
826
+ popupDimensions?.realHeight -
827
+ popupArrowDimensions.width
828
+ ),
829
+ popupArrowDimensions.width
830
+ );
831
+ }
832
+
833
+ return 0;
834
+ }
835
+
836
+ destroyTour() {
837
+ let popup = this.options.popup;
838
+ const overlaySvg = document.querySelector(".zt-tour-overlay");
839
+
840
+ if (popup.wrapper) {
841
+ popup.wrapper.remove();
842
+ }
843
+ if (overlaySvg) {
844
+ overlaySvg.remove();
845
+ }
846
+
847
+ this.options.activeStagePosition = null;
848
+ this.options.overlaySvg = null;
849
+ this.destroyEvents();
850
+ }
851
+
852
+ transitionStage(timeDiff, duration, from, to) {
853
+ let activeStagePosition = this.getOption("activeStagePosition");
854
+
855
+ const fromDefinition = activeStagePosition
856
+ ? activeStagePosition
857
+ : from.getBoundingClientRect();
858
+ const toDefinition = to.getBoundingClientRect();
859
+
860
+ const x = this.easeInOutQuad(
861
+ timeDiff,
862
+ fromDefinition.x,
863
+ toDefinition.x - fromDefinition.x,
864
+ duration
865
+ );
866
+ const y = this.easeInOutQuad(
867
+ timeDiff,
868
+ fromDefinition.y,
869
+ toDefinition.y - fromDefinition.y,
870
+ duration
871
+ );
872
+ const width = this.easeInOutQuad(
873
+ timeDiff,
874
+ fromDefinition.width,
875
+ toDefinition.width - fromDefinition.width,
876
+ duration
877
+ );
878
+ const height = this.easeInOutQuad(
879
+ timeDiff,
880
+ fromDefinition.height,
881
+ toDefinition.height - fromDefinition.height,
882
+ duration
883
+ );
884
+
885
+ activeStagePosition = {
886
+ x,
887
+ y,
888
+ width,
889
+ height,
890
+ };
891
+
892
+ this.renderOverlay(activeStagePosition);
893
+ this.setOption("activeStagePosition", activeStagePosition);
894
+ }
895
+
896
+ renderOverlay(stagePosition) {
897
+ const overlaySvg = this.getOption("overlaySvg");
898
+ if (!overlaySvg) {
899
+ this.addOverlay(stagePosition);
900
+ return;
901
+ }
902
+
903
+ const pathElement = overlaySvg.firstElementChild;
904
+ if (pathElement?.tagName !== "path") {
905
+ throw new Error("no path element found in stage svg");
906
+ }
907
+
908
+ pathElement.setAttribute(
909
+ "d",
910
+ this.generateStageSvgPathString(stagePosition)
911
+ );
912
+ }
913
+
914
+ changeHighlight(toElement, toStep, toStepIndex) {
915
+ const duration = this.getOption("animationDuration");
916
+ let currentStepEle =
917
+ this.options.steps[this.options.currentStep]?.element;
918
+
919
+ const start = Date.now();
920
+
921
+ const fromElement = currentStepEle
922
+ ? document.querySelector(currentStepEle)
923
+ : toElement;
924
+
925
+ const isFirstHighlight = !fromElement || fromElement === toElement;
926
+
927
+ const isAnimatedTour = this.getOption("animate");
928
+
929
+ const hasDelayedPopup = !isFirstHighlight && isAnimatedTour;
930
+ let isPopupRendered = false;
931
+
932
+ this.hidePopup();
933
+
934
+ this.setOption("currentStep", toStepIndex);
935
+
936
+ const animate = () => {
937
+ const timeDiff = Date.now() - start;
938
+ const timeRemaining = duration - timeDiff;
939
+ const isHalfwayThrough = timeRemaining <= duration / 2;
940
+ if (
941
+ toStep.popup &&
942
+ isHalfwayThrough &&
943
+ !isPopupRendered &&
944
+ hasDelayedPopup
945
+ ) {
946
+ this.renderPopup(toElement, toStep);
947
+ isPopupRendered = true;
948
+ }
949
+
950
+ if (this.getOption("animate") && timeDiff < duration) {
951
+ this.transitionStage(timeDiff, duration, fromElement, toElement);
952
+ } else {
953
+ this.trackActiveElement(toElement);
954
+ }
955
+ if (timeRemaining >= 0 && this.getOption("animate")) {
956
+ window.requestAnimationFrame(animate);
957
+ }
958
+ };
959
+
960
+ window.requestAnimationFrame(animate);
961
+
962
+ this.bringInView(toElement);
963
+ if (!hasDelayedPopup && toStep.popup) {
964
+ this.renderPopup(toElement, toStep);
965
+ }
966
+ }
967
+
968
+ bringInView(element) {
969
+ if (!element || this.isElementInView(element)) {
970
+ return;
971
+ }
972
+
973
+ const shouldSmoothScroll = this.getOption("smoothScroll");
974
+
975
+ element.scrollIntoView({
976
+ behavior:
977
+ !shouldSmoothScroll || this.isScrollableParent(element)
978
+ ? "auto"
979
+ : "smooth",
980
+ inline: "center",
981
+ block: "center",
982
+ });
983
+ }
984
+
985
+ isElementInView(element) {
986
+ const rect = element.getBoundingClientRect();
987
+
988
+ return (
989
+ rect.top >= 0 &&
990
+ rect.left >= 0 &&
991
+ rect.bottom <=
992
+ (window.innerHeight || document.documentElement.clientHeight) &&
993
+ rect.right <=
994
+ (window.innerWidth || document.documentElement.clientWidth)
995
+ );
996
+ }
997
+
998
+ isScrollableParent(e) {
999
+ if (!e || !e.parentElement) {
1000
+ return;
1001
+ }
1002
+ const parent = e.parentElement;
1003
+ return parent.scrollHeight > parent.clientHeight;
1004
+ }
1005
+
1006
+ trackActiveElement(element) {
1007
+ if (!element) {
1008
+ return;
1009
+ }
1010
+
1011
+ const definition = element.getBoundingClientRect();
1012
+
1013
+ const activeStagePosition = {
1014
+ x: definition.x,
1015
+ y: definition.y,
1016
+ width: definition.width,
1017
+ height: definition.height,
1018
+ };
1019
+
1020
+ this.setOption("activeStagePosition", activeStagePosition);
1021
+
1022
+ this.renderOverlay(activeStagePosition);
1023
+ }
1024
+
1025
+ addOverlay(stagePosition) {
1026
+ const overlaySvg = this.createOverlaySvg(stagePosition);
1027
+ document.body.appendChild(overlaySvg);
1028
+
1029
+ this.setOption("overlaySvg", overlaySvg);
1030
+ }
1031
+
1032
+ easeInOutQuad(timeDiff, initialValue, amountOfChange, duration) {
1033
+ if ((timeDiff /= duration / 2) < 1) {
1034
+ return (amountOfChange / 2) * timeDiff * timeDiff + initialValue;
1035
+ }
1036
+ return (
1037
+ (-amountOfChange / 2) * (--timeDiff * (timeDiff - 2) - 1) + initialValue
1038
+ );
1039
+ }
1040
+
1041
+ refreshOverlay() {
1042
+ const activeStagePosition = this.getOption("activeStagePosition");
1043
+ const overlaySvg = this.getOption("overlaySvg");
1044
+
1045
+ if (!activeStagePosition) {
1046
+ return;
1047
+ }
1048
+
1049
+ if (!overlaySvg) {
1050
+ console.warn("No svg found.");
1051
+ return;
1052
+ }
1053
+
1054
+ const windowX = window.innerWidth;
1055
+ const windowY = window.innerHeight;
1056
+
1057
+ overlaySvg.setAttribute("viewBox", `0 0 ${windowX} ${windowY}`);
1058
+ }
1059
+
1060
+ refreshStep() {
1061
+ const currentStep = this.getOption("currentStep");
1062
+ if(!currentStep) return;
1063
+ const step = this.getOption("steps")[currentStep];
1064
+ const element = document.querySelector(step.element);
1065
+ this.trackActiveElement(element);
1066
+ this.refreshOverlay();
1067
+ this.repositionPopup(element, step);
1068
+ }
1069
+
1070
+ addDummyElement() {
1071
+ const isDummyElement = document.getElementById("zt-popup-dummy-element");
1072
+ if (isDummyElement) {
1073
+ return isDummyElement;
1074
+ }
1075
+
1076
+ let element = document.createElement("div");
1077
+
1078
+ element.id = "zt-popup-dummy-element";
1079
+ element.style.width = "0";
1080
+ element.style.height = "0";
1081
+ element.style.pointerEvents = "none";
1082
+ element.style.opacity = "0";
1083
+ element.style.position = "fixed";
1084
+ element.style.top = "50%";
1085
+ element.style.left = "50%";
1086
+
1087
+ document.body.appendChild(element);
1088
+
1089
+ return element;
1090
+ }
1091
+ //
1092
+ }
1093
+
1094
+ global.ztTour = ztTour;
1095
+ })(this);