showoff 0.18.2 → 0.19.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -8,6 +8,7 @@
8
8
  flex-flow: column;
9
9
  min-width: 630px;
10
10
  min-height: 675px;
11
+ top: 0; /* this is why we can't have nice things. See #776 */
11
12
  }
12
13
 
13
14
  #timerSection,
@@ -53,6 +53,9 @@ pre code {
53
53
  padding:0;
54
54
  overflow: hidden;
55
55
  }
56
+ html.floatingNotes {
57
+ overflow: scroll;
58
+ }
56
59
 
57
60
  body.busy {
58
61
  cursor: progress;
@@ -598,6 +601,7 @@ pre.highlight code.language-powershellconsole.nochrome {
598
601
 
599
602
  pre.highlight code .highlightedLine {
600
603
  background-color: #f5e2e2;
604
+ color: #000;
601
605
  display: inline-block;
602
606
  width: 100%;
603
607
  }
@@ -855,6 +859,15 @@ form .element {
855
859
  margin-bottom: .5em;
856
860
  }
857
861
 
862
+ .ui-dialog .auxillary-buttons {
863
+ background: none;
864
+ border: none;
865
+ float: right;
866
+ }
867
+ .ui-dialog .auxillary-buttons:active {
868
+ color: #ccc;
869
+ }
870
+
858
871
  .no-close .ui-dialog-titlebar-close {
859
872
  display: none;
860
873
  }
@@ -864,9 +877,9 @@ form .element {
864
877
  font-size: .9em;
865
878
  }
866
879
 
867
- #help-modal .ui-dialog .ui-dialog-title {
868
- font-size: 1em;
869
- }
880
+ #help-modal .ui-dialog .ui-dialog-title {
881
+ font-size: 1em;
882
+ }
870
883
 
871
884
  #help div {
872
885
  padding: 6px 0;
@@ -908,6 +921,46 @@ form .element {
908
921
  *** end modal ***
909
922
  *****************/
910
923
 
924
+ /******************************
925
+ *** Tour customization ***
926
+ ******************************/
927
+ .introjs-skipbutton {
928
+ color: red;
929
+ }
930
+ .introjs-nextbutton {
931
+ font-weight: bold;
932
+ }
933
+ .introjs-donebutton {
934
+ color: white;
935
+ font-weight: bold;
936
+ background-image: linear-gradient(to bottom, #62c462, #51a351);
937
+ border-color: #51a351 #51a351 #387038;
938
+ text-shadow: none;
939
+ }
940
+ .introjs-donebutton:focus,
941
+ .introjs-donebutton:active {
942
+ background-color: #51a351;
943
+ background-image: none;
944
+ }
945
+ .tourDark {
946
+ background-color: rgba(183, 183, 183, 0.9);
947
+ }
948
+ .introjs-tooltip h1,
949
+ .introjs-tooltip h2,
950
+ .introjs-tooltip h3,
951
+ .introjs-tooltip h4,
952
+ .introjs-tooltip h5,
953
+ .introjs-tooltip h6 {
954
+ border-bottom: 2px solid black;
955
+ margin: 0 0 0.5em 0;
956
+ }
957
+ .introjs-tooltip h1 { font-size: 1.3em; }
958
+ .introjs-tooltip h2 { font-size: 1.2em; }
959
+ .introjs-tooltip h3 { font-size: 1.1em; }
960
+ .introjs-tooltip h4 { font-size: 1em; }
961
+
962
+
963
+
911
964
  /**********************************
912
965
  *** supplemental materials ***
913
966
  **********************************/
@@ -0,0 +1,2043 @@
1
+ /**
2
+ * Intro.js v2.6.0
3
+ * https://github.com/usablica/intro.js
4
+ *
5
+ * Copyright (C) 2016 Afshin Mehrabani (@afshinmeh)
6
+ */
7
+
8
+ (function (root, factory) {
9
+ if (typeof exports === 'object') {
10
+ // CommonJS
11
+ factory(exports);
12
+ } else if (typeof define === 'function' && define.amd) {
13
+ // AMD. Register as an anonymous module.
14
+ define(['exports'], factory);
15
+ } else {
16
+ // Browser globals
17
+ factory(root);
18
+ }
19
+ } (this, function (exports) {
20
+ //Default config/variables
21
+ var VERSION = '2.6.0';
22
+
23
+ /**
24
+ * IntroJs main class
25
+ *
26
+ * @class IntroJs
27
+ */
28
+ function IntroJs(obj) {
29
+ this._targetElement = obj;
30
+ this._introItems = [];
31
+
32
+ this._options = {
33
+ /* Next button label in tooltip box */
34
+ nextLabel: 'Next →',
35
+ /* Previous button label in tooltip box */
36
+ prevLabel: '← Back',
37
+ /* Skip button label in tooltip box */
38
+ skipLabel: 'Skip',
39
+ /* Done button label in tooltip box */
40
+ doneLabel: 'Done',
41
+ /* Hide previous button in the first step? Otherwise, it will be disabled button. */
42
+ hidePrev: false,
43
+ /* Hide next button in the last step? Otherwise, it will be disabled button. */
44
+ hideNext: false,
45
+ /* Default tooltip box position */
46
+ tooltipPosition: 'bottom',
47
+ /* Next CSS class for tooltip boxes */
48
+ tooltipClass: '',
49
+ /* CSS class that is added to the helperLayer */
50
+ highlightClass: '',
51
+ /* Close introduction when pressing Escape button? */
52
+ exitOnEsc: true,
53
+ /* Close introduction when clicking on overlay layer? */
54
+ exitOnOverlayClick: true,
55
+ /* Show step numbers in introduction? */
56
+ showStepNumbers: true,
57
+ /* Let user use keyboard to navigate the tour? */
58
+ keyboardNavigation: true,
59
+ /* Show tour control buttons? */
60
+ showButtons: true,
61
+ /* Show tour bullets? */
62
+ showBullets: true,
63
+ /* Show tour progress? */
64
+ showProgress: false,
65
+ /* Scroll to highlighted element? */
66
+ scrollToElement: true,
67
+ /* Set the overlay opacity */
68
+ overlayOpacity: 0.8,
69
+ /* Padding to add after scrolling when element is not in the viewport (in pixels) */
70
+ scrollPadding: 30,
71
+ /* Precedence of positions, when auto is enabled */
72
+ positionPrecedence: ["bottom", "top", "right", "left"],
73
+ /* Disable an interaction with element? */
74
+ disableInteraction: false,
75
+ /* Default hint position */
76
+ hintPosition: 'top-middle',
77
+ /* Hint button label */
78
+ hintButtonLabel: 'Got it',
79
+ /* Adding animation to hints? */
80
+ hintAnimation: true
81
+ };
82
+ }
83
+
84
+ /**
85
+ * Initiate a new introduction/guide from an element in the page
86
+ *
87
+ * @api private
88
+ * @method _introForElement
89
+ * @param {Object} targetElm
90
+ * @returns {Boolean} Success or not?
91
+ */
92
+ function _introForElement(targetElm) {
93
+ var introItems = [],
94
+ self = this;
95
+
96
+ if (this._options.steps) {
97
+ //use steps passed programmatically
98
+ for (var i = 0, stepsLength = this._options.steps.length; i < stepsLength; i++) {
99
+ var currentItem = _cloneObject(this._options.steps[i]);
100
+ //set the step
101
+ currentItem.step = introItems.length + 1;
102
+ //use querySelector function only when developer used CSS selector
103
+ if (typeof(currentItem.element) === 'string') {
104
+ //grab the element with given selector from the page
105
+ currentItem.element = document.querySelector(currentItem.element);
106
+ }
107
+
108
+ //intro without element
109
+ if (typeof(currentItem.element) === 'undefined' || currentItem.element == null) {
110
+ var floatingElementQuery = document.querySelector(".introjsFloatingElement");
111
+
112
+ if (floatingElementQuery == null) {
113
+ floatingElementQuery = document.createElement('div');
114
+ floatingElementQuery.className = 'introjsFloatingElement';
115
+
116
+ document.body.appendChild(floatingElementQuery);
117
+ }
118
+
119
+ currentItem.element = floatingElementQuery;
120
+ currentItem.position = 'floating';
121
+ }
122
+
123
+ if (currentItem.element != null) {
124
+ introItems.push(currentItem);
125
+ }
126
+ }
127
+
128
+ } else {
129
+ //use steps from data-* annotations
130
+ var allIntroSteps = targetElm.querySelectorAll('*[data-intro]');
131
+ //if there's no element to intro
132
+ if (allIntroSteps.length < 1) {
133
+ return false;
134
+ }
135
+
136
+ //first add intro items with data-step
137
+ for (var i = 0, elmsLength = allIntroSteps.length; i < elmsLength; i++) {
138
+ var currentElement = allIntroSteps[i];
139
+
140
+ // skip hidden elements
141
+ if (currentElement.style.display == 'none') {
142
+ continue;
143
+ }
144
+
145
+ var step = parseInt(currentElement.getAttribute('data-step'), 10);
146
+
147
+ if (step > 0) {
148
+ introItems[step - 1] = {
149
+ element: currentElement,
150
+ intro: currentElement.getAttribute('data-intro'),
151
+ step: parseInt(currentElement.getAttribute('data-step'), 10),
152
+ tooltipClass: currentElement.getAttribute('data-tooltipClass'),
153
+ highlightClass: currentElement.getAttribute('data-highlightClass'),
154
+ position: currentElement.getAttribute('data-position') || this._options.tooltipPosition
155
+ };
156
+ }
157
+ }
158
+
159
+ //next add intro items without data-step
160
+ //todo: we need a cleanup here, two loops are redundant
161
+ var nextStep = 0;
162
+ for (var i = 0, elmsLength = allIntroSteps.length; i < elmsLength; i++) {
163
+ var currentElement = allIntroSteps[i];
164
+
165
+ if (currentElement.getAttribute('data-step') == null) {
166
+
167
+ while (true) {
168
+ if (typeof introItems[nextStep] == 'undefined') {
169
+ break;
170
+ } else {
171
+ nextStep++;
172
+ }
173
+ }
174
+
175
+ introItems[nextStep] = {
176
+ element: currentElement,
177
+ intro: currentElement.getAttribute('data-intro'),
178
+ step: nextStep + 1,
179
+ tooltipClass: currentElement.getAttribute('data-tooltipClass'),
180
+ highlightClass: currentElement.getAttribute('data-highlightClass'),
181
+ position: currentElement.getAttribute('data-position') || this._options.tooltipPosition
182
+ };
183
+ }
184
+ }
185
+ }
186
+
187
+ //removing undefined/null elements
188
+ var tempIntroItems = [];
189
+ for (var z = 0; z < introItems.length; z++) {
190
+ introItems[z] && tempIntroItems.push(introItems[z]); // copy non-empty values to the end of the array
191
+ }
192
+
193
+ introItems = tempIntroItems;
194
+
195
+ //Ok, sort all items with given steps
196
+ introItems.sort(function (a, b) {
197
+ return a.step - b.step;
198
+ });
199
+
200
+ //set it to the introJs object
201
+ self._introItems = introItems;
202
+
203
+ //add overlay layer to the page
204
+ if(_addOverlayLayer.call(self, targetElm)) {
205
+ //then, start the show
206
+ _nextStep.call(self);
207
+
208
+ var skipButton = targetElm.querySelector('.introjs-skipbutton'),
209
+ nextStepButton = targetElm.querySelector('.introjs-nextbutton');
210
+
211
+ self._onKeyDown = function(e) {
212
+ if (e.keyCode === 27 && self._options.exitOnEsc == true) {
213
+ //escape key pressed, exit the intro
214
+ //check if exit callback is defined
215
+ _exitIntro.call(self, targetElm);
216
+ } else if(e.keyCode === 37) {
217
+ //left arrow
218
+ _previousStep.call(self);
219
+ } else if (e.keyCode === 39) {
220
+ //right arrow
221
+ _nextStep.call(self);
222
+ } else if (e.keyCode === 13) {
223
+ //srcElement === ie
224
+ var target = e.target || e.srcElement;
225
+ if (target && target.className.indexOf('introjs-prevbutton') > 0) {
226
+ //user hit enter while focusing on previous button
227
+ _previousStep.call(self);
228
+ } else if (target && target.className.indexOf('introjs-skipbutton') > 0) {
229
+ //user hit enter while focusing on skip button
230
+ if (self._introItems.length - 1 == self._currentStep && typeof (self._introCompleteCallback) === 'function') {
231
+ self._introCompleteCallback.call(self);
232
+ }
233
+
234
+ _exitIntro.call(self, targetElm);
235
+ } else {
236
+ //default behavior for responding to enter
237
+ _nextStep.call(self);
238
+ }
239
+
240
+ //prevent default behaviour on hitting Enter, to prevent steps being skipped in some browsers
241
+ if(e.preventDefault) {
242
+ e.preventDefault();
243
+ } else {
244
+ e.returnValue = false;
245
+ }
246
+ }
247
+ };
248
+
249
+ self._onResize = function(e) {
250
+ self.refresh.call(self);
251
+ };
252
+
253
+ if (window.addEventListener) {
254
+ if (this._options.keyboardNavigation) {
255
+ window.addEventListener('keydown', self._onKeyDown, true);
256
+ }
257
+ //for window resize
258
+ window.addEventListener('resize', self._onResize, true);
259
+ } else if (document.attachEvent) { //IE
260
+ if (this._options.keyboardNavigation) {
261
+ document.attachEvent('onkeydown', self._onKeyDown);
262
+ }
263
+ //for window resize
264
+ document.attachEvent('onresize', self._onResize);
265
+ }
266
+ }
267
+ return false;
268
+ }
269
+
270
+ /*
271
+ * makes a copy of the object
272
+ * @api private
273
+ * @method _cloneObject
274
+ */
275
+ function _cloneObject(object) {
276
+ if (object == null || typeof (object) != 'object' || typeof (object.nodeType) != 'undefined') {
277
+ return object;
278
+ }
279
+ var temp = {};
280
+ for (var key in object) {
281
+ if (typeof (jQuery) != 'undefined' && object[key] instanceof jQuery) {
282
+ temp[key] = object[key];
283
+ } else {
284
+ temp[key] = _cloneObject(object[key]);
285
+ }
286
+ }
287
+ return temp;
288
+ }
289
+ /**
290
+ * Go to specific step of introduction
291
+ *
292
+ * @api private
293
+ * @method _goToStep
294
+ */
295
+ function _goToStep(step) {
296
+ //because steps starts with zero
297
+ this._currentStep = step - 2;
298
+ if (typeof (this._introItems) !== 'undefined') {
299
+ _nextStep.call(this);
300
+ }
301
+ }
302
+
303
+ /**
304
+ * Go to the specific step of introduction with the explicit [data-step] number
305
+ *
306
+ * @api private
307
+ * @method _goToStepNumber
308
+ */
309
+ function _goToStepNumber(step) {
310
+ this._currentStepNumber = step;
311
+ if (typeof (this._introItems) !== 'undefined') {
312
+ _nextStep.call(this);
313
+ }
314
+ }
315
+
316
+ /**
317
+ * Go to next step on intro
318
+ *
319
+ * @api private
320
+ * @method _nextStep
321
+ */
322
+ function _nextStep() {
323
+ this._direction = 'forward';
324
+
325
+ if (typeof (this._currentStepNumber) !== 'undefined') {
326
+ for( var i = 0, len = this._introItems.length; i < len; i++ ) {
327
+ var item = this._introItems[i];
328
+ if( item.step === this._currentStepNumber ) {
329
+ this._currentStep = i - 1;
330
+ this._currentStepNumber = undefined;
331
+ }
332
+ }
333
+ }
334
+
335
+ if (typeof (this._currentStep) === 'undefined') {
336
+ this._currentStep = 0;
337
+ } else {
338
+ ++this._currentStep;
339
+ }
340
+
341
+ if ((this._introItems.length) <= this._currentStep) {
342
+ //end of the intro
343
+ //check if any callback is defined
344
+ if (typeof (this._introCompleteCallback) === 'function') {
345
+ this._introCompleteCallback.call(this);
346
+ }
347
+ _exitIntro.call(this, this._targetElement);
348
+ return;
349
+ }
350
+
351
+ var nextStep = this._introItems[this._currentStep];
352
+ if (typeof (this._introBeforeChangeCallback) !== 'undefined') {
353
+ this._introBeforeChangeCallback.call(this, nextStep.element);
354
+ }
355
+
356
+ _showElement.call(this, nextStep);
357
+ }
358
+
359
+ /**
360
+ * Go to previous step on intro
361
+ *
362
+ * @api private
363
+ * @method _previousStep
364
+ */
365
+ function _previousStep() {
366
+ this._direction = 'backward';
367
+
368
+ if (this._currentStep === 0) {
369
+ return false;
370
+ }
371
+
372
+ var nextStep = this._introItems[--this._currentStep];
373
+ if (typeof (this._introBeforeChangeCallback) !== 'undefined') {
374
+ this._introBeforeChangeCallback.call(this, nextStep.element);
375
+ }
376
+
377
+ _showElement.call(this, nextStep);
378
+ }
379
+
380
+ /**
381
+ * Update placement of the intro objects on the screen
382
+ * @api private
383
+ */
384
+ function _refresh() {
385
+ // re-align intros
386
+ _setHelperLayerPosition.call(this, document.querySelector('.introjs-helperLayer'));
387
+ _setHelperLayerPosition.call(this, document.querySelector('.introjs-tooltipReferenceLayer'));
388
+
389
+ // re-align tooltip
390
+ if(this._currentStep !== undefined && this._currentStep !== null) {
391
+ var oldHelperNumberLayer = document.querySelector('.introjs-helperNumberLayer'),
392
+ oldArrowLayer = document.querySelector('.introjs-arrow'),
393
+ oldtooltipContainer = document.querySelector('.introjs-tooltip');
394
+ _placeTooltip.call(this, this._introItems[this._currentStep].element, oldtooltipContainer, oldArrowLayer, oldHelperNumberLayer);
395
+ }
396
+
397
+ //re-align hints
398
+ _reAlignHints.call(this);
399
+ return this;
400
+ }
401
+
402
+ /**
403
+ * Exit from intro
404
+ *
405
+ * @api private
406
+ * @method _exitIntro
407
+ * @param {Object} targetElement
408
+ */
409
+ function _exitIntro(targetElement) {
410
+ //remove overlay layers from the page
411
+ var overlayLayers = targetElement.querySelectorAll('.introjs-overlay');
412
+
413
+ if (overlayLayers && overlayLayers.length > 0) {
414
+ for (var i = overlayLayers.length - 1; i >= 0; i--) {
415
+ //for fade-out animation
416
+ var overlayLayer = overlayLayers[i];
417
+ overlayLayer.style.opacity = 0;
418
+ setTimeout(function () {
419
+ if (this.parentNode) {
420
+ this.parentNode.removeChild(this);
421
+ }
422
+ }.bind(overlayLayer), 500);
423
+ };
424
+ }
425
+
426
+ //remove all helper layers
427
+ var helperLayer = targetElement.querySelector('.introjs-helperLayer');
428
+ if (helperLayer) {
429
+ helperLayer.parentNode.removeChild(helperLayer);
430
+ }
431
+
432
+ var referenceLayer = targetElement.querySelector('.introjs-tooltipReferenceLayer');
433
+ if (referenceLayer) {
434
+ referenceLayer.parentNode.removeChild(referenceLayer);
435
+ }
436
+ //remove disableInteractionLayer
437
+ var disableInteractionLayer = targetElement.querySelector('.introjs-disableInteraction');
438
+ if (disableInteractionLayer) {
439
+ disableInteractionLayer.parentNode.removeChild(disableInteractionLayer);
440
+ }
441
+
442
+ //remove intro floating element
443
+ var floatingElement = document.querySelector('.introjsFloatingElement');
444
+ if (floatingElement) {
445
+ floatingElement.parentNode.removeChild(floatingElement);
446
+ }
447
+
448
+ _removeShowElement();
449
+
450
+ //remove `introjs-fixParent` class from the elements
451
+ var fixParents = document.querySelectorAll('.introjs-fixParent');
452
+ if (fixParents && fixParents.length > 0) {
453
+ for (var i = fixParents.length - 1; i >= 0; i--) {
454
+ fixParents[i].className = fixParents[i].className.replace(/introjs-fixParent/g, '').replace(/^\s+|\s+$/g, '');
455
+ }
456
+ }
457
+
458
+ //clean listeners
459
+ if (window.removeEventListener) {
460
+ window.removeEventListener('keydown', this._onKeyDown, true);
461
+ } else if (document.detachEvent) { //IE
462
+ document.detachEvent('onkeydown', this._onKeyDown);
463
+ }
464
+
465
+ //check if any callback is defined
466
+ if (this._introExitCallback != undefined) {
467
+ this._introExitCallback.call(self);
468
+ }
469
+
470
+ //set the step to zero
471
+ this._currentStep = undefined;
472
+ }
473
+
474
+ /**
475
+ * Render tooltip box in the page
476
+ *
477
+ * @api private
478
+ * @method _placeTooltip
479
+ * @param {HTMLElement} targetElement
480
+ * @param {HTMLElement} tooltipLayer
481
+ * @param {HTMLElement} arrowLayer
482
+ * @param {HTMLElement} helperNumberLayer
483
+ * @param {Boolean} hintMode
484
+ */
485
+ function _placeTooltip(targetElement, tooltipLayer, arrowLayer, helperNumberLayer, hintMode) {
486
+ var tooltipCssClass = '',
487
+ currentStepObj,
488
+ tooltipOffset,
489
+ targetOffset,
490
+ windowSize,
491
+ currentTooltipPosition;
492
+
493
+ hintMode = hintMode || false;
494
+
495
+ //reset the old style
496
+ tooltipLayer.style.top = null;
497
+ tooltipLayer.style.right = null;
498
+ tooltipLayer.style.bottom = null;
499
+ tooltipLayer.style.left = null;
500
+ tooltipLayer.style.marginLeft = null;
501
+ tooltipLayer.style.marginTop = null;
502
+
503
+ arrowLayer.style.display = 'inherit';
504
+
505
+ if (typeof(helperNumberLayer) != 'undefined' && helperNumberLayer != null) {
506
+ helperNumberLayer.style.top = null;
507
+ helperNumberLayer.style.left = null;
508
+ }
509
+
510
+ //prevent error when `this._currentStep` is undefined
511
+ if (!this._introItems[this._currentStep]) return;
512
+
513
+ //if we have a custom css class for each step
514
+ currentStepObj = this._introItems[this._currentStep];
515
+ if (typeof (currentStepObj.tooltipClass) === 'string') {
516
+ tooltipCssClass = currentStepObj.tooltipClass;
517
+ } else {
518
+ tooltipCssClass = this._options.tooltipClass;
519
+ }
520
+
521
+ tooltipLayer.className = ('introjs-tooltip ' + tooltipCssClass).replace(/^\s+|\s+$/g, '');
522
+
523
+ currentTooltipPosition = this._introItems[this._currentStep].position;
524
+
525
+ if (currentTooltipPosition != "floating") { // Floating is always valid, no point in calculating
526
+ if (currentTooltipPosition === "auto") {
527
+ currentTooltipPosition = _determineAutoPosition.call(this, targetElement, tooltipLayer);
528
+ } else {
529
+ currentTooltipPosition = _determineAutoPosition.call(this, targetElement, tooltipLayer, currentTooltipPosition);
530
+ }
531
+ }
532
+
533
+ targetOffset = _getOffset(targetElement);
534
+ tooltipOffset = _getOffset(tooltipLayer);
535
+ windowSize = _getWinSize();
536
+
537
+ switch (currentTooltipPosition) {
538
+ case 'top':
539
+ arrowLayer.className = 'introjs-arrow bottom';
540
+
541
+ if (hintMode) {
542
+ var tooltipLayerStyleLeft = 0;
543
+ } else {
544
+ var tooltipLayerStyleLeft = 15;
545
+ }
546
+
547
+ _checkRight(targetOffset, tooltipLayerStyleLeft, tooltipOffset, windowSize, tooltipLayer);
548
+ tooltipLayer.style.bottom = (targetOffset.height + 20) + 'px';
549
+ break;
550
+ case 'right':
551
+ tooltipLayer.style.left = (targetOffset.width + 20) + 'px';
552
+ if (targetOffset.top + tooltipOffset.height > windowSize.height) {
553
+ // In this case, right would have fallen below the bottom of the screen.
554
+ // Modify so that the bottom of the tooltip connects with the target
555
+ arrowLayer.className = "introjs-arrow left-bottom";
556
+ tooltipLayer.style.top = "-" + (tooltipOffset.height - targetOffset.height - 20) + "px";
557
+ } else {
558
+ arrowLayer.className = 'introjs-arrow left';
559
+ }
560
+ break;
561
+ case 'left':
562
+ if (!hintMode && this._options.showStepNumbers == true) {
563
+ tooltipLayer.style.top = '15px';
564
+ }
565
+
566
+ if (targetOffset.top + tooltipOffset.height > windowSize.height) {
567
+ // In this case, left would have fallen below the bottom of the screen.
568
+ // Modify so that the bottom of the tooltip connects with the target
569
+ tooltipLayer.style.top = "-" + (tooltipOffset.height - targetOffset.height - 20) + "px";
570
+ arrowLayer.className = 'introjs-arrow right-bottom';
571
+ } else {
572
+ arrowLayer.className = 'introjs-arrow right';
573
+ }
574
+ tooltipLayer.style.right = (targetOffset.width + 20) + 'px';
575
+
576
+ break;
577
+ case 'floating':
578
+ arrowLayer.style.display = 'none';
579
+
580
+ //we have to adjust the top and left of layer manually for intro items without element
581
+ tooltipLayer.style.left = '50%';
582
+ tooltipLayer.style.top = '50%';
583
+ tooltipLayer.style.marginLeft = '-' + (tooltipOffset.width / 2) + 'px';
584
+ tooltipLayer.style.marginTop = '-' + (tooltipOffset.height / 2) + 'px';
585
+
586
+ if (typeof(helperNumberLayer) != 'undefined' && helperNumberLayer != null) {
587
+ helperNumberLayer.style.left = '-' + ((tooltipOffset.width / 2) + 18) + 'px';
588
+ helperNumberLayer.style.top = '-' + ((tooltipOffset.height / 2) + 18) + 'px';
589
+ }
590
+
591
+ break;
592
+ case 'bottom-right-aligned':
593
+ arrowLayer.className = 'introjs-arrow top-right';
594
+
595
+ var tooltipLayerStyleRight = 0;
596
+ _checkLeft(targetOffset, tooltipLayerStyleRight, tooltipOffset, tooltipLayer);
597
+ tooltipLayer.style.top = (targetOffset.height + 20) + 'px';
598
+ break;
599
+
600
+ case 'bottom-middle-aligned':
601
+ arrowLayer.className = 'introjs-arrow top-middle';
602
+
603
+ var tooltipLayerStyleLeftRight = targetOffset.width / 2 - tooltipOffset.width / 2;
604
+
605
+ // a fix for middle aligned hints
606
+ if (hintMode) {
607
+ tooltipLayerStyleLeftRight += 5;
608
+ }
609
+
610
+ if (_checkLeft(targetOffset, tooltipLayerStyleLeftRight, tooltipOffset, tooltipLayer)) {
611
+ tooltipLayer.style.right = null;
612
+ _checkRight(targetOffset, tooltipLayerStyleLeftRight, tooltipOffset, windowSize, tooltipLayer);
613
+ }
614
+ tooltipLayer.style.top = (targetOffset.height + 20) + 'px';
615
+ break;
616
+
617
+ case 'bottom-left-aligned':
618
+ // Bottom-left-aligned is the same as the default bottom
619
+ case 'bottom':
620
+ // Bottom going to follow the default behavior
621
+ default:
622
+ arrowLayer.className = 'introjs-arrow top';
623
+
624
+ var tooltipLayerStyleLeft = 0;
625
+ _checkRight(targetOffset, tooltipLayerStyleLeft, tooltipOffset, windowSize, tooltipLayer);
626
+ tooltipLayer.style.top = (targetOffset.height + 20) + 'px';
627
+ break;
628
+ }
629
+ }
630
+
631
+ /**
632
+ * Set tooltip left so it doesn't go off the right side of the window
633
+ *
634
+ * @return boolean true, if tooltipLayerStyleLeft is ok. false, otherwise.
635
+ */
636
+ function _checkRight(targetOffset, tooltipLayerStyleLeft, tooltipOffset, windowSize, tooltipLayer) {
637
+ if (targetOffset.left + tooltipLayerStyleLeft + tooltipOffset.width > windowSize.width) {
638
+ // off the right side of the window
639
+ tooltipLayer.style.left = (windowSize.width - tooltipOffset.width - targetOffset.left) + 'px';
640
+ return false;
641
+ }
642
+ tooltipLayer.style.left = tooltipLayerStyleLeft + 'px';
643
+ return true;
644
+ }
645
+
646
+ /**
647
+ * Set tooltip right so it doesn't go off the left side of the window
648
+ *
649
+ * @return boolean true, if tooltipLayerStyleRight is ok. false, otherwise.
650
+ */
651
+ function _checkLeft(targetOffset, tooltipLayerStyleRight, tooltipOffset, tooltipLayer) {
652
+ if (targetOffset.left + targetOffset.width - tooltipLayerStyleRight - tooltipOffset.width < 0) {
653
+ // off the left side of the window
654
+ tooltipLayer.style.left = (-targetOffset.left) + 'px';
655
+ return false;
656
+ }
657
+ tooltipLayer.style.right = tooltipLayerStyleRight + 'px';
658
+ return true;
659
+ }
660
+
661
+ /**
662
+ * Determines the position of the tooltip based on the position precedence and availability
663
+ * of screen space.
664
+ *
665
+ * @param {Object} targetElement
666
+ * @param {Object} tooltipLayer
667
+ * @param {Object} desiredTooltipPosition
668
+ *
669
+ */
670
+ function _determineAutoPosition(targetElement, tooltipLayer, desiredTooltipPosition) {
671
+
672
+ // Take a clone of position precedence. These will be the available
673
+ var possiblePositions = this._options.positionPrecedence.slice();
674
+
675
+ var windowSize = _getWinSize();
676
+ var tooltipHeight = _getOffset(tooltipLayer).height + 10;
677
+ var tooltipWidth = _getOffset(tooltipLayer).width + 20;
678
+ var targetOffset = _getOffset(targetElement);
679
+
680
+ // If we check all the possible areas, and there are no valid places for the tooltip, the element
681
+ // must take up most of the screen real estate. Show the tooltip floating in the middle of the screen.
682
+ var calculatedPosition = "floating";
683
+
684
+ // Check if the width of the tooltip + the starting point would spill off the right side of the screen
685
+ // If no, neither bottom or top are valid
686
+ if (targetOffset.left + tooltipWidth > windowSize.width || ((targetOffset.left + (targetOffset.width / 2)) - tooltipWidth) < 0) {
687
+ _removeEntry(possiblePositions, "bottom");
688
+ _removeEntry(possiblePositions, "top");
689
+ } else {
690
+ // Check for space below
691
+ if ((targetOffset.height + targetOffset.top + tooltipHeight) > windowSize.height) {
692
+ _removeEntry(possiblePositions, "bottom");
693
+ }
694
+
695
+ // Check for space above
696
+ if (targetOffset.top - tooltipHeight < 0) {
697
+ _removeEntry(possiblePositions, "top");
698
+ }
699
+ }
700
+
701
+ // Check for space to the right
702
+ if (targetOffset.width + targetOffset.left + tooltipWidth > windowSize.width) {
703
+ _removeEntry(possiblePositions, "right");
704
+ }
705
+
706
+ // Check for space to the left
707
+ if (targetOffset.left - tooltipWidth < 0) {
708
+ _removeEntry(possiblePositions, "left");
709
+ }
710
+
711
+ // At this point, our array only has positions that are valid. Pick the first one, as it remains in order
712
+ if (possiblePositions.length > 0) {
713
+ calculatedPosition = possiblePositions[0];
714
+ }
715
+
716
+ // If the requested position is in the list, replace our calculated choice with that
717
+ if (desiredTooltipPosition && desiredTooltipPosition != "auto") {
718
+ if (possiblePositions.indexOf(desiredTooltipPosition) > -1) {
719
+ calculatedPosition = desiredTooltipPosition;
720
+ }
721
+ }
722
+
723
+ return calculatedPosition;
724
+ }
725
+
726
+ /**
727
+ * Remove an entry from a string array if it's there, does nothing if it isn't there.
728
+ *
729
+ * @param {Array} stringArray
730
+ * @param {String} stringToRemove
731
+ */
732
+ function _removeEntry(stringArray, stringToRemove) {
733
+ if (stringArray.indexOf(stringToRemove) > -1) {
734
+ stringArray.splice(stringArray.indexOf(stringToRemove), 1);
735
+ }
736
+ }
737
+
738
+ /**
739
+ * Update the position of the helper layer on the screen
740
+ *
741
+ * @api private
742
+ * @method _setHelperLayerPosition
743
+ * @param {Object} helperLayer
744
+ */
745
+ function _setHelperLayerPosition(helperLayer) {
746
+ if (helperLayer) {
747
+ //prevent error when `this._currentStep` in undefined
748
+ if (!this._introItems[this._currentStep]) return;
749
+
750
+ var currentElement = this._introItems[this._currentStep],
751
+ elementPosition = _getOffset(currentElement.element),
752
+ widthHeightPadding = 10;
753
+
754
+ // If the target element is fixed, the tooltip should be fixed as well.
755
+ // Otherwise, remove a fixed class that may be left over from the previous
756
+ // step.
757
+ if (_isFixed(currentElement.element)) {
758
+ helperLayer.className += ' introjs-fixedTooltip';
759
+ } else {
760
+ helperLayer.className = helperLayer.className.replace(' introjs-fixedTooltip', '');
761
+ }
762
+
763
+ if (currentElement.position == 'floating') {
764
+ widthHeightPadding = 0;
765
+ }
766
+
767
+ //set new position to helper layer
768
+ helperLayer.setAttribute('style', 'width: ' + (elementPosition.width + widthHeightPadding) + 'px; ' +
769
+ 'height:' + (elementPosition.height + widthHeightPadding) + 'px; ' +
770
+ 'top:' + (elementPosition.top - 5) + 'px;' +
771
+ 'left: ' + (elementPosition.left - 5) + 'px;');
772
+
773
+ }
774
+ }
775
+
776
+ /**
777
+ * Add disableinteraction layer and adjust the size and position of the layer
778
+ *
779
+ * @api private
780
+ * @method _disableInteraction
781
+ */
782
+ function _disableInteraction() {
783
+ var disableInteractionLayer = document.querySelector('.introjs-disableInteraction');
784
+ if (disableInteractionLayer === null) {
785
+ disableInteractionLayer = document.createElement('div');
786
+ disableInteractionLayer.className = 'introjs-disableInteraction';
787
+ this._targetElement.appendChild(disableInteractionLayer);
788
+ }
789
+
790
+ _setHelperLayerPosition.call(this, disableInteractionLayer);
791
+ }
792
+
793
+ /**
794
+ * Setting anchors to behave like buttons
795
+ *
796
+ * @api private
797
+ * @method _setAnchorAsButton
798
+ */
799
+ function _setAnchorAsButton(anchor){
800
+ anchor.setAttribute('role', 'button');
801
+ anchor.tabIndex = 0;
802
+ }
803
+
804
+ /**
805
+ * Show an element on the page
806
+ *
807
+ * @api private
808
+ * @method _showElement
809
+ * @param {Object} targetElement
810
+ */
811
+ function _showElement(targetElement) {
812
+ if (typeof (this._introChangeCallback) !== 'undefined') {
813
+ this._introChangeCallback.call(this, targetElement.element);
814
+ }
815
+
816
+ var self = this,
817
+ oldHelperLayer = document.querySelector('.introjs-helperLayer'),
818
+ oldReferenceLayer = document.querySelector('.introjs-tooltipReferenceLayer'),
819
+ highlightClass = 'introjs-helperLayer',
820
+ elementPosition = _getOffset(targetElement.element);
821
+
822
+ //check for a current step highlight class
823
+ if (typeof (targetElement.highlightClass) === 'string') {
824
+ highlightClass += (' ' + targetElement.highlightClass);
825
+ }
826
+ //check for options highlight class
827
+ if (typeof (this._options.highlightClass) === 'string') {
828
+ highlightClass += (' ' + this._options.highlightClass);
829
+ }
830
+
831
+ if (oldHelperLayer != null) {
832
+ var oldHelperNumberLayer = oldReferenceLayer.querySelector('.introjs-helperNumberLayer'),
833
+ oldtooltipLayer = oldReferenceLayer.querySelector('.introjs-tooltiptext'),
834
+ oldArrowLayer = oldReferenceLayer.querySelector('.introjs-arrow'),
835
+ oldtooltipContainer = oldReferenceLayer.querySelector('.introjs-tooltip'),
836
+ skipTooltipButton = oldReferenceLayer.querySelector('.introjs-skipbutton'),
837
+ prevTooltipButton = oldReferenceLayer.querySelector('.introjs-prevbutton'),
838
+ nextTooltipButton = oldReferenceLayer.querySelector('.introjs-nextbutton');
839
+
840
+ //update or reset the helper highlight class
841
+ oldHelperLayer.className = highlightClass;
842
+ //hide the tooltip
843
+ oldtooltipContainer.style.opacity = 0;
844
+ oldtooltipContainer.style.display = "none";
845
+
846
+ if (oldHelperNumberLayer != null) {
847
+ var lastIntroItem = this._introItems[(targetElement.step - 2 >= 0 ? targetElement.step - 2 : 0)];
848
+
849
+ if (lastIntroItem != null && (this._direction == 'forward' && lastIntroItem.position == 'floating') || (this._direction == 'backward' && targetElement.position == 'floating')) {
850
+ oldHelperNumberLayer.style.opacity = 0;
851
+ }
852
+ }
853
+
854
+ //set new position to helper layer
855
+ _setHelperLayerPosition.call(self, oldHelperLayer);
856
+ _setHelperLayerPosition.call(self, oldReferenceLayer);
857
+
858
+ //remove `introjs-fixParent` class from the elements
859
+ var fixParents = document.querySelectorAll('.introjs-fixParent');
860
+ if (fixParents && fixParents.length > 0) {
861
+ for (var i = fixParents.length - 1; i >= 0; i--) {
862
+ fixParents[i].className = fixParents[i].className.replace(/introjs-fixParent/g, '').replace(/^\s+|\s+$/g, '');
863
+ };
864
+ }
865
+
866
+ //remove old classes if the element still exist
867
+ _removeShowElement();
868
+
869
+ //we should wait until the CSS3 transition is competed (it's 0.3 sec) to prevent incorrect `height` and `width` calculation
870
+ if (self._lastShowElementTimer) {
871
+ clearTimeout(self._lastShowElementTimer);
872
+ }
873
+ self._lastShowElementTimer = setTimeout(function() {
874
+ //set current step to the label
875
+ if (oldHelperNumberLayer != null) {
876
+ oldHelperNumberLayer.innerHTML = targetElement.step;
877
+ }
878
+ //set current tooltip text
879
+ oldtooltipLayer.innerHTML = targetElement.intro;
880
+ //set the tooltip position
881
+ oldtooltipContainer.style.display = "block";
882
+ _placeTooltip.call(self, targetElement.element, oldtooltipContainer, oldArrowLayer, oldHelperNumberLayer);
883
+
884
+ //change active bullet
885
+ if (self._options.showBullets) {
886
+ oldReferenceLayer.querySelector('.introjs-bullets li > a.active').className = '';
887
+ oldReferenceLayer.querySelector('.introjs-bullets li > a[data-stepnumber="' + targetElement.step + '"]').className = 'active';
888
+ }
889
+ oldReferenceLayer.querySelector('.introjs-progress .introjs-progressbar').setAttribute('style', 'width:' + _getProgress.call(self) + '%;');
890
+
891
+ //show the tooltip
892
+ oldtooltipContainer.style.opacity = 1;
893
+ if (oldHelperNumberLayer) oldHelperNumberLayer.style.opacity = 1;
894
+
895
+ //reset button focus
896
+ if (typeof skipTooltipButton !== "undefined" && skipTooltipButton != null && /introjs-donebutton/gi.test(skipTooltipButton.className)) {
897
+ // skip button is now "done" button
898
+ skipTooltipButton.focus();
899
+ } else if (typeof nextTooltipButton !== "undefined" && nextTooltipButton != null) {
900
+ //still in the tour, focus on next
901
+ nextTooltipButton.focus();
902
+ }
903
+ }, 350);
904
+
905
+ // end of old element if-else condition
906
+ } else {
907
+ var helperLayer = document.createElement('div'),
908
+ referenceLayer = document.createElement('div'),
909
+ arrowLayer = document.createElement('div'),
910
+ tooltipLayer = document.createElement('div'),
911
+ tooltipTextLayer = document.createElement('div'),
912
+ bulletsLayer = document.createElement('div'),
913
+ progressLayer = document.createElement('div'),
914
+ buttonsLayer = document.createElement('div');
915
+
916
+ helperLayer.className = highlightClass;
917
+ referenceLayer.className = 'introjs-tooltipReferenceLayer';
918
+
919
+ //set new position to helper layer
920
+ _setHelperLayerPosition.call(self, helperLayer);
921
+ _setHelperLayerPosition.call(self, referenceLayer);
922
+
923
+ //add helper layer to target element
924
+ this._targetElement.appendChild(helperLayer);
925
+ this._targetElement.appendChild(referenceLayer);
926
+
927
+ arrowLayer.className = 'introjs-arrow';
928
+
929
+ tooltipTextLayer.className = 'introjs-tooltiptext';
930
+ tooltipTextLayer.innerHTML = targetElement.intro;
931
+
932
+ bulletsLayer.className = 'introjs-bullets';
933
+
934
+ if (this._options.showBullets === false) {
935
+ bulletsLayer.style.display = 'none';
936
+ }
937
+
938
+ var ulContainer = document.createElement('ul');
939
+
940
+ for (var i = 0, stepsLength = this._introItems.length; i < stepsLength; i++) {
941
+ var innerLi = document.createElement('li');
942
+ var anchorLink = document.createElement('a');
943
+
944
+ anchorLink.onclick = function() {
945
+ self.goToStep(this.getAttribute('data-stepnumber'));
946
+ };
947
+
948
+ if (i === (targetElement.step-1)) anchorLink.className = 'active';
949
+
950
+ _setAnchorAsButton(anchorLink);
951
+ anchorLink.innerHTML = "&nbsp;";
952
+ anchorLink.setAttribute('data-stepnumber', this._introItems[i].step);
953
+
954
+ innerLi.appendChild(anchorLink);
955
+ ulContainer.appendChild(innerLi);
956
+ }
957
+
958
+ bulletsLayer.appendChild(ulContainer);
959
+
960
+ progressLayer.className = 'introjs-progress';
961
+
962
+ if (this._options.showProgress === false) {
963
+ progressLayer.style.display = 'none';
964
+ }
965
+ var progressBar = document.createElement('div');
966
+ progressBar.className = 'introjs-progressbar';
967
+ progressBar.setAttribute('style', 'width:' + _getProgress.call(this) + '%;');
968
+
969
+ progressLayer.appendChild(progressBar);
970
+
971
+ buttonsLayer.className = 'introjs-tooltipbuttons';
972
+ if (this._options.showButtons === false) {
973
+ buttonsLayer.style.display = 'none';
974
+ }
975
+
976
+ tooltipLayer.className = 'introjs-tooltip';
977
+ tooltipLayer.appendChild(tooltipTextLayer);
978
+ tooltipLayer.appendChild(bulletsLayer);
979
+ tooltipLayer.appendChild(progressLayer);
980
+
981
+ //add helper layer number
982
+ if (this._options.showStepNumbers == true) {
983
+ var helperNumberLayer = document.createElement('span');
984
+ helperNumberLayer.className = 'introjs-helperNumberLayer';
985
+ helperNumberLayer.innerHTML = targetElement.step;
986
+ referenceLayer.appendChild(helperNumberLayer);
987
+ }
988
+
989
+ tooltipLayer.appendChild(arrowLayer);
990
+ referenceLayer.appendChild(tooltipLayer);
991
+
992
+ //next button
993
+ var nextTooltipButton = document.createElement('a');
994
+
995
+ nextTooltipButton.onclick = function() {
996
+ if (self._introItems.length - 1 != self._currentStep) {
997
+ _nextStep.call(self);
998
+ }
999
+ };
1000
+
1001
+ _setAnchorAsButton(nextTooltipButton);
1002
+ nextTooltipButton.innerHTML = this._options.nextLabel;
1003
+
1004
+ //previous button
1005
+ var prevTooltipButton = document.createElement('a');
1006
+
1007
+ prevTooltipButton.onclick = function() {
1008
+ if (self._currentStep != 0) {
1009
+ _previousStep.call(self);
1010
+ }
1011
+ };
1012
+
1013
+ _setAnchorAsButton(prevTooltipButton);
1014
+ prevTooltipButton.innerHTML = this._options.prevLabel;
1015
+
1016
+ //skip button
1017
+ var skipTooltipButton = document.createElement('a');
1018
+ skipTooltipButton.className = 'introjs-button introjs-skipbutton';
1019
+ _setAnchorAsButton(skipTooltipButton);
1020
+ skipTooltipButton.innerHTML = this._options.skipLabel;
1021
+
1022
+ skipTooltipButton.onclick = function() {
1023
+ if (self._introItems.length - 1 == self._currentStep && typeof (self._introCompleteCallback) === 'function') {
1024
+ self._introCompleteCallback.call(self);
1025
+ }
1026
+
1027
+ _exitIntro.call(self, self._targetElement);
1028
+ };
1029
+
1030
+ buttonsLayer.appendChild(skipTooltipButton);
1031
+
1032
+ //in order to prevent displaying next/previous button always
1033
+ if (this._introItems.length > 1) {
1034
+ buttonsLayer.appendChild(prevTooltipButton);
1035
+ buttonsLayer.appendChild(nextTooltipButton);
1036
+ }
1037
+
1038
+ tooltipLayer.appendChild(buttonsLayer);
1039
+
1040
+ //set proper position
1041
+ _placeTooltip.call(self, targetElement.element, tooltipLayer, arrowLayer, helperNumberLayer);
1042
+
1043
+ //end of new element if-else condition
1044
+ }
1045
+
1046
+ //disable interaction
1047
+ if (this._options.disableInteraction === true) {
1048
+ _disableInteraction.call(self);
1049
+ }
1050
+
1051
+ if (typeof nextTooltipButton !== "undefined" && nextTooltipButton != null) {
1052
+ nextTooltipButton.removeAttribute('tabIndex');
1053
+ }
1054
+ if (typeof prevTooltipButton !== "undefined" && prevTooltipButton != null) {
1055
+ prevTooltipButton.removeAttribute('tabIndex');
1056
+ }
1057
+
1058
+ // when it's the first step of tour
1059
+ if (this._currentStep == 0 && this._introItems.length > 1) {
1060
+ if (typeof skipTooltipButton !== "undefined" && skipTooltipButton != null) {
1061
+ skipTooltipButton.className = 'introjs-button introjs-skipbutton';
1062
+ }
1063
+ if (typeof nextTooltipButton !== "undefined" && nextTooltipButton != null) {
1064
+ nextTooltipButton.className = 'introjs-button introjs-nextbutton';
1065
+ }
1066
+
1067
+ if (this._options.hidePrev == true) {
1068
+ if (typeof prevTooltipButton !== "undefined" && prevTooltipButton != null) {
1069
+ prevTooltipButton.className = 'introjs-button introjs-prevbutton introjs-hidden';
1070
+ }
1071
+ if (typeof nextTooltipButton !== "undefined" && nextTooltipButton != null) {
1072
+ nextTooltipButton.className += ' introjs-fullbutton';
1073
+ }
1074
+ } else {
1075
+ if (typeof prevTooltipButton !== "undefined" && prevTooltipButton != null) {
1076
+ prevTooltipButton.className = 'introjs-button introjs-prevbutton introjs-disabled';
1077
+ }
1078
+ }
1079
+
1080
+ if (typeof prevTooltipButton !== "undefined" && prevTooltipButton != null) {
1081
+ prevTooltipButton.tabIndex = '-1';
1082
+ }
1083
+ if (typeof skipTooltipButton !== "undefined" && skipTooltipButton != null) {
1084
+ skipTooltipButton.innerHTML = this._options.skipLabel;
1085
+ }
1086
+ } else if (this._introItems.length - 1 == this._currentStep || this._introItems.length == 1) {
1087
+ // last step of tour
1088
+ if (typeof skipTooltipButton !== "undefined" && skipTooltipButton != null) {
1089
+ skipTooltipButton.innerHTML = this._options.doneLabel;
1090
+ // adding donebutton class in addition to skipbutton
1091
+ skipTooltipButton.className += ' introjs-donebutton';
1092
+ }
1093
+ if (typeof prevTooltipButton !== "undefined" && prevTooltipButton != null) {
1094
+ prevTooltipButton.className = 'introjs-button introjs-prevbutton';
1095
+ }
1096
+
1097
+ if (this._options.hideNext == true) {
1098
+ if (typeof nextTooltipButton !== "undefined" && nextTooltipButton != null) {
1099
+ nextTooltipButton.className = 'introjs-button introjs-nextbutton introjs-hidden';
1100
+ }
1101
+ if (typeof prevTooltipButton !== "undefined" && prevTooltipButton != null) {
1102
+ prevTooltipButton.className += ' introjs-fullbutton';
1103
+ }
1104
+ } else {
1105
+ if (typeof nextTooltipButton !== "undefined" && nextTooltipButton != null) {
1106
+ nextTooltipButton.className = 'introjs-button introjs-nextbutton introjs-disabled';
1107
+ }
1108
+ }
1109
+
1110
+ if (typeof nextTooltipButton !== "undefined" && nextTooltipButton != null) {
1111
+ nextTooltipButton.tabIndex = '-1';
1112
+ }
1113
+ } else {
1114
+ // steps between start and end
1115
+ if (typeof skipTooltipButton !== "undefined" && skipTooltipButton != null) {
1116
+ skipTooltipButton.className = 'introjs-button introjs-skipbutton';
1117
+ }
1118
+ if (typeof prevTooltipButton !== "undefined" && prevTooltipButton != null) {
1119
+ prevTooltipButton.className = 'introjs-button introjs-prevbutton';
1120
+ }
1121
+ if (typeof nextTooltipButton !== "undefined" && nextTooltipButton != null) {
1122
+ nextTooltipButton.className = 'introjs-button introjs-nextbutton';
1123
+ }
1124
+ if (typeof skipTooltipButton !== "undefined" && skipTooltipButton != null) {
1125
+ skipTooltipButton.innerHTML = this._options.skipLabel;
1126
+ }
1127
+ }
1128
+
1129
+ //Set focus on "next" button, so that hitting Enter always moves you onto the next step
1130
+ if (typeof nextTooltipButton !== "undefined" && nextTooltipButton != null) {
1131
+ nextTooltipButton.focus();
1132
+ }
1133
+
1134
+ //Pass in whichever helperlayer we're using because _removeShowElement strips the child node classes
1135
+ _setShowElement(targetElement, helperLayer || oldHelperLayer);
1136
+
1137
+ if (!_elementInViewport(targetElement.element) && this._options.scrollToElement === true) {
1138
+ var rect = targetElement.element.getBoundingClientRect(),
1139
+ winHeight = _getWinSize().height,
1140
+ top = rect.bottom - (rect.bottom - rect.top),
1141
+ bottom = rect.bottom - winHeight;
1142
+
1143
+ //Scroll up
1144
+ if (top < 0 || targetElement.element.clientHeight > winHeight) {
1145
+ window.scrollBy(0, top - this._options.scrollPadding); // 30px padding from edge to look nice
1146
+
1147
+ //Scroll down
1148
+ } else {
1149
+ window.scrollBy(0, bottom + 70 + this._options.scrollPadding); // 70px + 30px padding from edge to look nice
1150
+ }
1151
+ }
1152
+
1153
+ if (typeof (this._introAfterChangeCallback) !== 'undefined') {
1154
+ this._introAfterChangeCallback.call(this, targetElement.element);
1155
+ }
1156
+ }
1157
+
1158
+ /**
1159
+ * To remove all show element(s)
1160
+ *
1161
+ * @api private
1162
+ * @method _removeShowElement
1163
+ */
1164
+ function _removeShowElement() {
1165
+ var elms = document.querySelectorAll('.introjs-showElement');
1166
+
1167
+ for (var i = 0, l = elms.length; i < l; i++) {
1168
+ var elm = elms[i];
1169
+ _removeClass(elm, /introjs-[a-zA-Z]+/g);
1170
+ }
1171
+ }
1172
+
1173
+ /**
1174
+ * To set the show element
1175
+ * This function set a relative (in most cases) position and changes the z-index
1176
+ *
1177
+ * @api private
1178
+ * @method _setShowElement
1179
+ * @param {Object} targetElement
1180
+ */
1181
+ function _setShowElement(targetElement, helperLayer) {
1182
+ // we need to add this show element class to the parent of SVG elements
1183
+ // because the SVG elements can't have independent z-index
1184
+ if (targetElement.element instanceof SVGElement) {
1185
+ var parentElm = targetElement.element.parentNode;
1186
+
1187
+ while (targetElement.element.parentNode != null) {
1188
+ if (!parentElm.tagName || parentElm.tagName.toLowerCase() === 'body') break;
1189
+
1190
+ if (parentElm.tagName.toLowerCase() === 'svg') {
1191
+ _setClass(parentElm, 'introjs-showElement introjs-relativePosition');
1192
+ }
1193
+
1194
+ parentElm = parentElm.parentNode;
1195
+ }
1196
+ }
1197
+
1198
+ _setClass(targetElement.element, 'introjs-showElement');
1199
+
1200
+ var currentElementPosition = _getPropValue(targetElement.element, 'position');
1201
+ if (currentElementPosition !== 'absolute' &&
1202
+ currentElementPosition !== 'relative' &&
1203
+ currentElementPosition !== 'fixed') {
1204
+ //change to new intro item
1205
+ //targetElement.element.className += ' introjs-relativePosition';
1206
+ _setClass(targetElement.element, 'introjs-relativePosition')
1207
+ }
1208
+
1209
+ var transformString = '';
1210
+ var parentElm = targetElement.element.parentNode;
1211
+ while (parentElm != null) {
1212
+ if (!parentElm.tagName || parentElm.tagName.toLowerCase() === 'body') break;
1213
+
1214
+ //fix The Stacking Context problem.
1215
+ //More detail: https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Understanding_z_index/The_stacking_context
1216
+ var zIndex = _getPropValue(parentElm, 'z-index');
1217
+ var opacity = parseFloat(_getPropValue(parentElm, 'opacity'));
1218
+ var transform = _getPropValue(parentElm, 'transform') || _getPropValue(parentElm, '-webkit-transform') || _getPropValue(parentElm, '-moz-transform') || _getPropValue(parentElm, '-ms-transform') || _getPropValue(parentElm, '-o-transform');
1219
+ if (/[0-9]+/.test(zIndex) || opacity < 1 || (transform !== 'none' && transform !== undefined)) {
1220
+ parentElm.className += ' introjs-fixParent';
1221
+ transformString += ' ' + parentElm.style.transform;
1222
+ }
1223
+
1224
+ parentElm = parentElm.parentNode;
1225
+ }
1226
+
1227
+ // Since we cannot rely on z-index stacking, let's clone the element and put it above the helperLayer
1228
+ if(transformString.trim() != '') {
1229
+ while (helperLayer.lastChild) {
1230
+ helperLayer.removeChild(helperLayer.lastChild);
1231
+ };
1232
+
1233
+ var clone = targetElement.element.cloneNode(true);
1234
+
1235
+ clone.style.cssText = document.defaultView.getComputedStyle(targetElement.element).cssText;
1236
+ clone.className += ' introjs-fixElement';
1237
+ clone.style.transform = transformString;
1238
+
1239
+ helperLayer.appendChild(clone);
1240
+ }
1241
+ }
1242
+
1243
+ function _setClass(element, className) {
1244
+ if (element instanceof SVGElement) {
1245
+ var pre = element.getAttribute('class') || '';
1246
+
1247
+ element.setAttribute('class', pre + ' ' + className);
1248
+ } else {
1249
+ element.className += ' ' + className;
1250
+ }
1251
+ }
1252
+
1253
+ function _removeClass(element, classNameRegex) {
1254
+ if (element instanceof SVGElement) {
1255
+ var pre = element.getAttribute('class') || '';
1256
+
1257
+ element.setAttribute('class', pre.replace(classNameRegex, '').replace(/^\s+|\s+$/g, ''));
1258
+ } else {
1259
+ element.className = element.className.replace(classNameRegex, '').replace(/^\s+|\s+$/g, '');
1260
+ }
1261
+ }
1262
+
1263
+ /**
1264
+ * Get an element CSS property on the page
1265
+ * Thanks to JavaScript Kit: http://www.javascriptkit.com/dhtmltutors/dhtmlcascade4.shtml
1266
+ *
1267
+ * @api private
1268
+ * @method _getPropValue
1269
+ * @param {Object} element
1270
+ * @param {String} propName
1271
+ * @returns Element's property value
1272
+ */
1273
+ function _getPropValue (element, propName) {
1274
+ var propValue = '';
1275
+ if (element.currentStyle) { //IE
1276
+ propValue = element.currentStyle[propName];
1277
+ } else if (document.defaultView && document.defaultView.getComputedStyle) { //Others
1278
+ propValue = document.defaultView.getComputedStyle(element, null).getPropertyValue(propName);
1279
+ }
1280
+
1281
+ //Prevent exception in IE
1282
+ if (propValue && propValue.toLowerCase) {
1283
+ return propValue.toLowerCase();
1284
+ } else {
1285
+ return propValue;
1286
+ }
1287
+ }
1288
+
1289
+ /**
1290
+ * Checks to see if target element (or parents) position is fixed or not
1291
+ *
1292
+ * @api private
1293
+ * @method _isFixed
1294
+ * @param {Object} element
1295
+ * @returns Boolean
1296
+ */
1297
+ function _isFixed (element) {
1298
+ var p = element.parentNode;
1299
+
1300
+ if (!p || p.nodeName === 'HTML') {
1301
+ return false;
1302
+ }
1303
+
1304
+ if (_getPropValue(element, 'position') == 'fixed') {
1305
+ return true;
1306
+ }
1307
+
1308
+ return _isFixed(p);
1309
+ }
1310
+
1311
+ /**
1312
+ * Provides a cross-browser way to get the screen dimensions
1313
+ * via: http://stackoverflow.com/questions/5864467/internet-explorer-innerheight
1314
+ *
1315
+ * @api private
1316
+ * @method _getWinSize
1317
+ * @returns {Object} width and height attributes
1318
+ */
1319
+ function _getWinSize() {
1320
+ if (window.innerWidth != undefined) {
1321
+ return { width: window.innerWidth, height: window.innerHeight };
1322
+ } else {
1323
+ var D = document.documentElement;
1324
+ return { width: D.clientWidth, height: D.clientHeight };
1325
+ }
1326
+ }
1327
+
1328
+ /**
1329
+ * Check to see if the element is in the viewport or not
1330
+ * http://stackoverflow.com/questions/123999/how-to-tell-if-a-dom-element-is-visible-in-the-current-viewport
1331
+ *
1332
+ * @api private
1333
+ * @method _elementInViewport
1334
+ * @param {Object} el
1335
+ */
1336
+ function _elementInViewport(el) {
1337
+ var rect = el.getBoundingClientRect();
1338
+
1339
+ return (
1340
+ rect.top >= 0 &&
1341
+ rect.left >= 0 &&
1342
+ (rect.bottom+80) <= window.innerHeight && // add 80 to get the text right
1343
+ rect.right <= window.innerWidth
1344
+ );
1345
+ }
1346
+
1347
+ /**
1348
+ * Add overlay layer to the page
1349
+ *
1350
+ * @api private
1351
+ * @method _addOverlayLayer
1352
+ * @param {Object} targetElm
1353
+ */
1354
+ function _addOverlayLayer(targetElm) {
1355
+ var overlayLayer = document.createElement('div'),
1356
+ styleText = '',
1357
+ self = this;
1358
+
1359
+ //set css class name
1360
+ overlayLayer.className = 'introjs-overlay';
1361
+
1362
+ //check if the target element is body, we should calculate the size of overlay layer in a better way
1363
+ if (!targetElm.tagName || targetElm.tagName.toLowerCase() === 'body') {
1364
+ styleText += 'top: 0;bottom: 0; left: 0;right: 0;position: fixed;';
1365
+ overlayLayer.setAttribute('style', styleText);
1366
+ } else {
1367
+ //set overlay layer position
1368
+ var elementPosition = _getOffset(targetElm);
1369
+ if (elementPosition) {
1370
+ styleText += 'width: ' + elementPosition.width + 'px; height:' + elementPosition.height + 'px; top:' + elementPosition.top + 'px;left: ' + elementPosition.left + 'px;';
1371
+ overlayLayer.setAttribute('style', styleText);
1372
+ }
1373
+ }
1374
+
1375
+ targetElm.appendChild(overlayLayer);
1376
+
1377
+ overlayLayer.onclick = function() {
1378
+ if (self._options.exitOnOverlayClick == true) {
1379
+ _exitIntro.call(self, targetElm);
1380
+ }
1381
+ };
1382
+
1383
+ setTimeout(function() {
1384
+ styleText += 'opacity: ' + self._options.overlayOpacity.toString() + ';';
1385
+ overlayLayer.setAttribute('style', styleText);
1386
+ }, 10);
1387
+
1388
+ return true;
1389
+ }
1390
+
1391
+ /**
1392
+ * Removes open hint (tooltip hint)
1393
+ *
1394
+ * @api private
1395
+ * @method _removeHintTooltip
1396
+ */
1397
+ function _removeHintTooltip() {
1398
+ var tooltip = this._targetElement.querySelector('.introjs-hintReference');
1399
+
1400
+ if (tooltip) {
1401
+ var step = tooltip.getAttribute('data-step');
1402
+ tooltip.parentNode.removeChild(tooltip);
1403
+ return step;
1404
+ }
1405
+ }
1406
+
1407
+ /**
1408
+ * Start parsing hint items
1409
+ *
1410
+ * @api private
1411
+ * @param {Object} targetElm
1412
+ * @method _startHint
1413
+ */
1414
+ function _populateHints(targetElm) {
1415
+ var self = this;
1416
+ this._introItems = [];
1417
+
1418
+ if (this._options.hints) {
1419
+ for (var i = 0, l = this._options.hints.length; i < l; i++) {
1420
+ var currentItem = _cloneObject(this._options.hints[i]);
1421
+
1422
+ if (typeof(currentItem.element) === 'string') {
1423
+ //grab the element with given selector from the page
1424
+ currentItem.element = document.querySelector(currentItem.element);
1425
+ }
1426
+
1427
+ currentItem.hintPosition = currentItem.hintPosition || this._options.hintPosition;
1428
+ currentItem.hintAnimation = currentItem.hintAnimation || this._options.hintAnimation;
1429
+
1430
+ if (currentItem.element != null) {
1431
+ this._introItems.push(currentItem);
1432
+ }
1433
+ }
1434
+ } else {
1435
+ var hints = targetElm.querySelectorAll('*[data-hint]');
1436
+
1437
+ if (hints.length < 1) {
1438
+ return false;
1439
+ }
1440
+
1441
+ //first add intro items with data-step
1442
+ for (var i = 0, l = hints.length; i < l; i++) {
1443
+ var currentElement = hints[i];
1444
+
1445
+ // hint animation
1446
+ var hintAnimation = currentElement.getAttribute('data-hintAnimation');
1447
+
1448
+ if (hintAnimation) {
1449
+ hintAnimation = (hintAnimation == 'true');
1450
+ } else {
1451
+ hintAnimation = this._options.hintAnimation;
1452
+ }
1453
+
1454
+ this._introItems.push({
1455
+ element: currentElement,
1456
+ hint: currentElement.getAttribute('data-hint'),
1457
+ hintPosition: currentElement.getAttribute('data-hintPosition') || this._options.hintPosition,
1458
+ hintAnimation: hintAnimation,
1459
+ tooltipClass: currentElement.getAttribute('data-tooltipClass'),
1460
+ position: currentElement.getAttribute('data-position') || this._options.tooltipPosition
1461
+ });
1462
+ }
1463
+ }
1464
+
1465
+ _addHints.call(this);
1466
+
1467
+ if (document.addEventListener) {
1468
+ document.addEventListener('click', _removeHintTooltip.bind(this), false);
1469
+ //for window resize
1470
+ window.addEventListener('resize', _reAlignHints.bind(this), true);
1471
+ } else if (document.attachEvent) { //IE
1472
+ //for window resize
1473
+ document.attachEvent('onclick', _removeHintTooltip.bind(this));
1474
+ document.attachEvent('onresize', _reAlignHints.bind(this));
1475
+ }
1476
+ }
1477
+
1478
+ /**
1479
+ * Re-aligns all hint elements
1480
+ *
1481
+ * @api private
1482
+ * @method _reAlignHints
1483
+ */
1484
+ function _reAlignHints() {
1485
+ for (var i = 0, l = this._introItems.length; i < l; i++) {
1486
+ var item = this._introItems[i];
1487
+
1488
+ if (typeof (item.targetElement) == 'undefined') continue;
1489
+
1490
+ _alignHintPosition.call(this, item.hintPosition, item.element, item.targetElement)
1491
+ }
1492
+ }
1493
+
1494
+ /**
1495
+ * Hide a hint
1496
+ *
1497
+ * @api private
1498
+ * @method _hideHint
1499
+ */
1500
+ function _hideHint(stepId) {
1501
+ _removeHintTooltip.call(this);
1502
+ var hint = this._targetElement.querySelector('.introjs-hint[data-step="' + stepId + '"]');
1503
+
1504
+ if (hint) {
1505
+ hint.className += ' introjs-hidehint';
1506
+ }
1507
+
1508
+ // call the callback function (if any)
1509
+ if (typeof (this._hintCloseCallback) !== 'undefined') {
1510
+ this._hintCloseCallback.call(this, stepId);
1511
+ }
1512
+ }
1513
+
1514
+ /**
1515
+ * Hide all hints
1516
+ *
1517
+ * @api private
1518
+ * @method _hideHints
1519
+ */
1520
+ function _hideHints() {
1521
+ var hints = this._targetElement.querySelectorAll('.introjs-hint');
1522
+
1523
+ if (hints && hints.length > 0) {
1524
+ for (var i = 0; i < hints.length; i++) {
1525
+ _hideHint.call(this, hints[i].getAttribute('data-step'));
1526
+ }
1527
+ }
1528
+ }
1529
+
1530
+ /**
1531
+ * Show all hints
1532
+ *
1533
+ * @api private
1534
+ * @method _showHints
1535
+ */
1536
+ function _showHints() {
1537
+ var hints = this._targetElement.querySelectorAll('.introjs-hint');
1538
+
1539
+ if (hints && hints.length > 0) {
1540
+ for (var i = 0; i < hints.length; i++) {
1541
+ _showHint.call(this, hints[i].getAttribute('data-step'));
1542
+ }
1543
+ } else {
1544
+ _populateHints.call(this, this._targetElement);
1545
+ }
1546
+ };
1547
+
1548
+ /**
1549
+ * Show a hint
1550
+ *
1551
+ * @api private
1552
+ * @method _showHint
1553
+ */
1554
+ function _showHint(stepId) {
1555
+ var hint = this._targetElement.querySelector('.introjs-hint[data-step="' + stepId + '"]');
1556
+
1557
+ if (hint) {
1558
+ hint.className = hint.className.replace(/introjs\-hidehint/g, '');
1559
+ }
1560
+ };
1561
+
1562
+ /**
1563
+ * Removes all hint elements on the page
1564
+ * Useful when you want to destroy the elements and add them again (e.g. a modal or popup)
1565
+ *
1566
+ * @api private
1567
+ * @method _removeHints
1568
+ */
1569
+ function _removeHints() {
1570
+ var hints = this._targetElement.querySelectorAll('.introjs-hint');
1571
+
1572
+ if (hints && hints.length > 0) {
1573
+ for (var i = 0; i < hints.length; i++) {
1574
+ _removeHint.call(this, hints[i].getAttribute('data-step'));
1575
+ }
1576
+ }
1577
+ };
1578
+
1579
+ /**
1580
+ * Remove one single hint element from the page
1581
+ * Useful when you want to destroy the element and add them again (e.g. a modal or popup)
1582
+ * Use removeHints if you want to remove all elements.
1583
+ *
1584
+ * @api private
1585
+ * @method _removeHint
1586
+ */
1587
+ function _removeHint(stepId) {
1588
+ var hint = this._targetElement.querySelector('.introjs-hint[data-step="' + stepId + '"]');
1589
+
1590
+ if (hint) {
1591
+ hint.parentNode.removeChild(hint);
1592
+ }
1593
+ };
1594
+
1595
+ /**
1596
+ * Add all available hints to the page
1597
+ *
1598
+ * @api private
1599
+ * @method _addHints
1600
+ */
1601
+ function _addHints() {
1602
+ var self = this;
1603
+
1604
+ var oldHintsWrapper = document.querySelector('.introjs-hints');
1605
+
1606
+ if (oldHintsWrapper != null) {
1607
+ hintsWrapper = oldHintsWrapper;
1608
+ } else {
1609
+ var hintsWrapper = document.createElement('div');
1610
+ hintsWrapper.className = 'introjs-hints';
1611
+ }
1612
+
1613
+ for (var i = 0, l = this._introItems.length; i < l; i++) {
1614
+ var item = this._introItems[i];
1615
+
1616
+ // avoid append a hint twice
1617
+ if (document.querySelector('.introjs-hint[data-step="' + i + '"]'))
1618
+ continue;
1619
+
1620
+ var hint = document.createElement('a');
1621
+ _setAnchorAsButton(hint);
1622
+
1623
+ (function (hint, item, i) {
1624
+ // when user clicks on the hint element
1625
+ hint.onclick = function(e) {
1626
+ var evt = e ? e : window.event;
1627
+ if (evt.stopPropagation) evt.stopPropagation();
1628
+ if (evt.cancelBubble != null) evt.cancelBubble = true;
1629
+
1630
+ _hintClick.call(self, hint, item, i);
1631
+ };
1632
+ }(hint, item, i));
1633
+
1634
+ hint.className = 'introjs-hint';
1635
+
1636
+ if (!item.hintAnimation) {
1637
+ hint.className += ' introjs-hint-no-anim';
1638
+ }
1639
+
1640
+ // hint's position should be fixed if the target element's position is fixed
1641
+ if (_isFixed(item.element)) {
1642
+ hint.className += ' introjs-fixedhint';
1643
+ }
1644
+
1645
+ var hintDot = document.createElement('div');
1646
+ hintDot.className = 'introjs-hint-dot';
1647
+ var hintPulse = document.createElement('div');
1648
+ hintPulse.className = 'introjs-hint-pulse';
1649
+
1650
+ hint.appendChild(hintDot);
1651
+ hint.appendChild(hintPulse);
1652
+ hint.setAttribute('data-step', i);
1653
+
1654
+ // we swap the hint element with target element
1655
+ // because _setHelperLayerPosition uses `element` property
1656
+ item.targetElement = item.element;
1657
+ item.element = hint;
1658
+
1659
+ // align the hint position
1660
+ _alignHintPosition.call(this, item.hintPosition, hint, item.targetElement);
1661
+
1662
+ hintsWrapper.appendChild(hint);
1663
+ }
1664
+
1665
+ // adding the hints wrapper
1666
+ document.body.appendChild(hintsWrapper);
1667
+
1668
+ // call the callback function (if any)
1669
+ if (typeof (this._hintsAddedCallback) !== 'undefined') {
1670
+ this._hintsAddedCallback.call(this);
1671
+ }
1672
+ }
1673
+
1674
+ /**
1675
+ * Aligns hint position
1676
+ *
1677
+ * @api private
1678
+ * @method _alignHintPosition
1679
+ * @param {String} position
1680
+ * @param {Object} hint
1681
+ * @param {Object} element
1682
+ */
1683
+ function _alignHintPosition(position, hint, element) {
1684
+ // get/calculate offset of target element
1685
+ var offset = _getOffset.call(this, element);
1686
+ var iconWidth = 20;
1687
+ var iconHeight = 20;
1688
+
1689
+ // align the hint element
1690
+ switch (position) {
1691
+ default:
1692
+ case 'top-left':
1693
+ hint.style.left = offset.left + 'px';
1694
+ hint.style.top = offset.top + 'px';
1695
+ break;
1696
+ case 'top-right':
1697
+ hint.style.left = (offset.left + offset.width - iconWidth) + 'px';
1698
+ hint.style.top = offset.top + 'px';
1699
+ break;
1700
+ case 'bottom-left':
1701
+ hint.style.left = offset.left + 'px';
1702
+ hint.style.top = (offset.top + offset.height - iconHeight) + 'px';
1703
+ break;
1704
+ case 'bottom-right':
1705
+ hint.style.left = (offset.left + offset.width - iconWidth) + 'px';
1706
+ hint.style.top = (offset.top + offset.height - iconHeight) + 'px';
1707
+ break;
1708
+ case 'middle-left':
1709
+ hint.style.left = offset.left + 'px';
1710
+ hint.style.top = (offset.top + (offset.height - iconHeight) / 2) + 'px';
1711
+ break;
1712
+ case 'middle-right':
1713
+ hint.style.left = (offset.left + offset.width - iconWidth) + 'px';
1714
+ hint.style.top = (offset.top + (offset.height - iconHeight) / 2) + 'px';
1715
+ break;
1716
+ case 'middle-middle':
1717
+ hint.style.left = (offset.left + (offset.width - iconWidth) / 2) + 'px';
1718
+ hint.style.top = (offset.top + (offset.height - iconHeight) / 2) + 'px';
1719
+ break;
1720
+ case 'bottom-middle':
1721
+ hint.style.left = (offset.left + (offset.width - iconWidth) / 2) + 'px';
1722
+ hint.style.top = (offset.top + offset.height - iconHeight) + 'px';
1723
+ break;
1724
+ case 'top-middle':
1725
+ hint.style.left = (offset.left + (offset.width - iconWidth) / 2) + 'px';
1726
+ hint.style.top = offset.top + 'px';
1727
+ break;
1728
+ }
1729
+ }
1730
+
1731
+ /**
1732
+ * Triggers when user clicks on the hint element
1733
+ *
1734
+ * @api private
1735
+ * @method _hintClick
1736
+ * @param {Object} hintElement
1737
+ * @param {Object} item
1738
+ * @param {Number} stepId
1739
+ */
1740
+ function _hintClick(hintElement, item, stepId) {
1741
+ // call the callback function (if any)
1742
+ if (typeof (this._hintClickCallback) !== 'undefined') {
1743
+ this._hintClickCallback.call(this, hintElement, item, stepId);
1744
+ }
1745
+
1746
+ // remove all open tooltips
1747
+ var removedStep = _removeHintTooltip.call(this);
1748
+
1749
+ // to toggle the tooltip
1750
+ if (parseInt(removedStep, 10) == stepId) {
1751
+ return;
1752
+ }
1753
+
1754
+ var tooltipLayer = document.createElement('div');
1755
+ var tooltipTextLayer = document.createElement('div');
1756
+ var arrowLayer = document.createElement('div');
1757
+ var referenceLayer = document.createElement('div');
1758
+
1759
+ tooltipLayer.className = 'introjs-tooltip';
1760
+
1761
+ tooltipLayer.onclick = function (e) {
1762
+ //IE9 & Other Browsers
1763
+ if (e.stopPropagation) {
1764
+ e.stopPropagation();
1765
+ }
1766
+ //IE8 and Lower
1767
+ else {
1768
+ e.cancelBubble = true;
1769
+ }
1770
+ };
1771
+
1772
+ tooltipTextLayer.className = 'introjs-tooltiptext';
1773
+
1774
+ var tooltipWrapper = document.createElement('p');
1775
+ tooltipWrapper.innerHTML = item.hint;
1776
+
1777
+ var closeButton = document.createElement('a');
1778
+ closeButton.className = 'introjs-button';
1779
+ closeButton.innerHTML = this._options.hintButtonLabel;
1780
+ closeButton.onclick = _hideHint.bind(this, stepId);
1781
+
1782
+ tooltipTextLayer.appendChild(tooltipWrapper);
1783
+ tooltipTextLayer.appendChild(closeButton);
1784
+
1785
+ arrowLayer.className = 'introjs-arrow';
1786
+ tooltipLayer.appendChild(arrowLayer);
1787
+
1788
+ tooltipLayer.appendChild(tooltipTextLayer);
1789
+
1790
+ // set current step for _placeTooltip function
1791
+ this._currentStep = hintElement.getAttribute('data-step');
1792
+
1793
+ // align reference layer position
1794
+ referenceLayer.className = 'introjs-tooltipReferenceLayer introjs-hintReference';
1795
+ referenceLayer.setAttribute('data-step', hintElement.getAttribute('data-step'));
1796
+ _setHelperLayerPosition.call(this, referenceLayer);
1797
+
1798
+ referenceLayer.appendChild(tooltipLayer);
1799
+ document.body.appendChild(referenceLayer);
1800
+
1801
+ //set proper position
1802
+ _placeTooltip.call(this, hintElement, tooltipLayer, arrowLayer, null, true);
1803
+ }
1804
+
1805
+ /**
1806
+ * Get an element position on the page
1807
+ * Thanks to `meouw`: http://stackoverflow.com/a/442474/375966
1808
+ *
1809
+ * @api private
1810
+ * @method _getOffset
1811
+ * @param {Object} element
1812
+ * @returns Element's position info
1813
+ */
1814
+ function _getOffset(element) {
1815
+ var elementPosition = {};
1816
+
1817
+ var body = document.body;
1818
+ var docEl = document.documentElement;
1819
+
1820
+ var scrollTop = window.pageYOffset || docEl.scrollTop || body.scrollTop;
1821
+ var scrollLeft = window.pageXOffset || docEl.scrollLeft || body.scrollLeft;
1822
+
1823
+ var x = element.getBoundingClientRect()
1824
+ elementPosition.top = x.top + scrollTop;
1825
+ elementPosition.width = x.width;
1826
+ elementPosition.height = x.height;
1827
+ elementPosition.left = x.left + scrollLeft;
1828
+
1829
+ return elementPosition;
1830
+ }
1831
+
1832
+ /**
1833
+ * Gets the current progress percentage
1834
+ *
1835
+ * @api private
1836
+ * @method _getProgress
1837
+ * @returns current progress percentage
1838
+ */
1839
+ function _getProgress() {
1840
+ // Steps are 0 indexed
1841
+ var currentStep = parseInt((this._currentStep + 1), 10);
1842
+ return ((currentStep / this._introItems.length) * 100);
1843
+ }
1844
+
1845
+ /**
1846
+ * Overwrites obj1's values with obj2's and adds obj2's if non existent in obj1
1847
+ * via: http://stackoverflow.com/questions/171251/how-can-i-merge-properties-of-two-javascript-objects-dynamically
1848
+ *
1849
+ * @param obj1
1850
+ * @param obj2
1851
+ * @returns obj3 a new object based on obj1 and obj2
1852
+ */
1853
+ function _mergeOptions(obj1,obj2) {
1854
+ var obj3 = {};
1855
+ for (var attrname in obj1) { obj3[attrname] = obj1[attrname]; }
1856
+ for (var attrname in obj2) { obj3[attrname] = obj2[attrname]; }
1857
+ return obj3;
1858
+ }
1859
+
1860
+ var introJs = function (targetElm) {
1861
+ if (typeof (targetElm) === 'object') {
1862
+ //Ok, create a new instance
1863
+ return new IntroJs(targetElm);
1864
+
1865
+ } else if (typeof (targetElm) === 'string') {
1866
+ //select the target element with query selector
1867
+ var targetElement = document.querySelector(targetElm);
1868
+
1869
+ if (targetElement) {
1870
+ return new IntroJs(targetElement);
1871
+ } else {
1872
+ throw new Error('There is no element with given selector.');
1873
+ }
1874
+ } else {
1875
+ return new IntroJs(document.body);
1876
+ }
1877
+ };
1878
+
1879
+ /**
1880
+ * Current IntroJs version
1881
+ *
1882
+ * @property version
1883
+ * @type String
1884
+ */
1885
+ introJs.version = VERSION;
1886
+
1887
+ //Prototype
1888
+ introJs.fn = IntroJs.prototype = {
1889
+ clone: function () {
1890
+ return new IntroJs(this);
1891
+ },
1892
+ setOption: function(option, value) {
1893
+ this._options[option] = value;
1894
+ return this;
1895
+ },
1896
+ setOptions: function(options) {
1897
+ this._options = _mergeOptions(this._options, options);
1898
+ return this;
1899
+ },
1900
+ start: function () {
1901
+ _introForElement.call(this, this._targetElement);
1902
+ return this;
1903
+ },
1904
+ goToStep: function(step) {
1905
+ _goToStep.call(this, step);
1906
+ return this;
1907
+ },
1908
+ addStep: function(options) {
1909
+ if (!this._options.steps) {
1910
+ this._options.steps = [];
1911
+ }
1912
+
1913
+ this._options.steps.push(options);
1914
+
1915
+ return this;
1916
+ },
1917
+ addSteps: function(steps) {
1918
+ if (!steps.length) return;
1919
+
1920
+ for(var index = 0; index < steps.length; index++) {
1921
+ this.addStep(steps[index]);
1922
+ }
1923
+
1924
+ return this;
1925
+ },
1926
+ goToStepNumber: function(step) {
1927
+ _goToStepNumber.call(this, step);
1928
+
1929
+ return this;
1930
+ },
1931
+ nextStep: function() {
1932
+ _nextStep.call(this);
1933
+ return this;
1934
+ },
1935
+ previousStep: function() {
1936
+ _previousStep.call(this);
1937
+ return this;
1938
+ },
1939
+ exit: function() {
1940
+ _exitIntro.call(this, this._targetElement);
1941
+ return this;
1942
+ },
1943
+ refresh: function() {
1944
+ _refresh.call(this);
1945
+ return this;
1946
+ },
1947
+ onbeforechange: function(providedCallback) {
1948
+ if (typeof (providedCallback) === 'function') {
1949
+ this._introBeforeChangeCallback = providedCallback;
1950
+ } else {
1951
+ throw new Error('Provided callback for onbeforechange was not a function');
1952
+ }
1953
+ return this;
1954
+ },
1955
+ onchange: function(providedCallback) {
1956
+ if (typeof (providedCallback) === 'function') {
1957
+ this._introChangeCallback = providedCallback;
1958
+ } else {
1959
+ throw new Error('Provided callback for onchange was not a function.');
1960
+ }
1961
+ return this;
1962
+ },
1963
+ onafterchange: function(providedCallback) {
1964
+ if (typeof (providedCallback) === 'function') {
1965
+ this._introAfterChangeCallback = providedCallback;
1966
+ } else {
1967
+ throw new Error('Provided callback for onafterchange was not a function');
1968
+ }
1969
+ return this;
1970
+ },
1971
+ oncomplete: function(providedCallback) {
1972
+ if (typeof (providedCallback) === 'function') {
1973
+ this._introCompleteCallback = providedCallback;
1974
+ } else {
1975
+ throw new Error('Provided callback for oncomplete was not a function.');
1976
+ }
1977
+ return this;
1978
+ },
1979
+ onhintsadded: function(providedCallback) {
1980
+ if (typeof (providedCallback) === 'function') {
1981
+ this._hintsAddedCallback = providedCallback;
1982
+ } else {
1983
+ throw new Error('Provided callback for onhintsadded was not a function.');
1984
+ }
1985
+ return this;
1986
+ },
1987
+ onhintclick: function(providedCallback) {
1988
+ if (typeof (providedCallback) === 'function') {
1989
+ this._hintClickCallback = providedCallback;
1990
+ } else {
1991
+ throw new Error('Provided callback for onhintclick was not a function.');
1992
+ }
1993
+ return this;
1994
+ },
1995
+ onhintclose: function(providedCallback) {
1996
+ if (typeof (providedCallback) === 'function') {
1997
+ this._hintCloseCallback = providedCallback;
1998
+ } else {
1999
+ throw new Error('Provided callback for onhintclose was not a function.');
2000
+ }
2001
+ return this;
2002
+ },
2003
+ onexit: function(providedCallback) {
2004
+ if (typeof (providedCallback) === 'function') {
2005
+ this._introExitCallback = providedCallback;
2006
+ } else {
2007
+ throw new Error('Provided callback for onexit was not a function.');
2008
+ }
2009
+ return this;
2010
+ },
2011
+ addHints: function() {
2012
+ _populateHints.call(this, this._targetElement);
2013
+ return this;
2014
+ },
2015
+ hideHint: function (stepId) {
2016
+ _hideHint.call(this, stepId);
2017
+ return this;
2018
+ },
2019
+ hideHints: function () {
2020
+ _hideHints.call(this);
2021
+ return this;
2022
+ },
2023
+ showHint: function (stepId) {
2024
+ _showHint.call(this, stepId);
2025
+ return this;
2026
+ },
2027
+ showHints: function () {
2028
+ _showHints.call(this);
2029
+ return this;
2030
+ },
2031
+ removeHints: function () {
2032
+ _removeHints.call(this);
2033
+ return this;
2034
+ },
2035
+ removeHint: function (stepId) {
2036
+ _removeHint.call(this, stepId);
2037
+ return this;
2038
+ }
2039
+ };
2040
+
2041
+ exports.introJs = introJs;
2042
+ return introJs;
2043
+ }));