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.
- checksums.yaml +4 -4
- data/Rakefile +16 -3
- data/lib/showoff.rb +37 -25
- data/lib/showoff/version.rb +1 -1
- data/lib/showoff_utils.rb +2 -2
- data/locales/de.yml +23 -0
- data/locales/en.yml +23 -0
- data/locales/es.yml +23 -0
- data/locales/fr.yml +23 -0
- data/locales/ja.yml +23 -0
- data/locales/nl.yml +23 -0
- data/locales/pt.yml +23 -0
- data/public/css/introjs-2.5.local.css +503 -0
- data/public/css/presenter.css +1 -0
- data/public/css/showoff.css +56 -3
- data/public/js/intro-2.5.local.js +2043 -0
- data/public/js/presenter.js +2 -0
- data/public/js/showoff.js +109 -18
- data/views/header.erb +4 -0
- data/views/index.erb +24 -0
- data/views/onepage.erb +24 -4
- data/views/presenter.erb +112 -2
- metadata +5 -14
data/public/css/presenter.css
CHANGED
data/public/css/showoff.css
CHANGED
@@ -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
|
-
|
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 = " ";
|
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
|
+
}));
|