javascriptgantt 1.0.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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);