showoff 0.18.2 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
+ }));