Skip to content
This repository was archived by the owner on Feb 17, 2021. It is now read-only.

Commit 32dd422

Browse files
author
Gordon Koo
committed
added substep functionality
1 parent 3cc4ff8 commit 32dd422

File tree

6 files changed

+151
-66
lines changed

6 files changed

+151
-66
lines changed

css/hopscotch.css

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

css/hopscotch.less

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ div#hopscotch-bubble {
4040
}
4141

4242
#hopscotch-bubble-container {
43-
padding: 20px;
43+
padding: 15px;
4444
position: relative;
4545
-webkit-font-smoothing: antialiased; /* to fix text flickering */
4646
}

index.html

-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@ <h2 id="subheading" class="one two three">Content</h2>
5555
<h2>Debug controls</h2>
5656
<input id="startBtn" type="button" value="Start">
5757
<input id="endBtn" type="button" value="End">
58-
<input id="clearCookieBtn" type="button" value="Clear Cookie">
5958
<h3>Tour Options</h3>
6059
<label>
6160
<input type="checkbox" id="animateCheck"> animate

js/debug.js

-7
Original file line numberDiff line numberDiff line change
@@ -90,11 +90,4 @@ for (i = 0, len = booleanControls.length; i < len; i++) {
9090
hopscotch.endTour();
9191
});
9292

93-
// ============
94-
// CLEAR COOKIE
95-
// ============
96-
addClickListener(document.getElementById('clearCookieBtn'), function() {
97-
hopscotch.clearCookie();
98-
});
99-
10093
}());

js/hopscotch-min.js

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

js/hopscotch.js

+148-55
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
*
2121
* in addition to targetId, do we want to support specifying targetEl directly?
2222
*
23+
* flag to see if user has already taken a tour?
24+
*
2325
* http://daneden.me/animate/ for bounce animation
2426
*
2527
*/
@@ -261,9 +263,7 @@
261263
};
262264

263265
HopscotchBubble = function(opt) {
264-
var prevBtnCallback,
265-
nextBtnCallback,
266-
currStep,
266+
var currStep,
267267
isShowing = false,
268268

269269
createButton = function(id, text) {
@@ -456,15 +456,9 @@
456456

457457
// Attach click listeners
458458
utils.addClickListener(this.prevBtnEl, function(evt) {
459-
if (prevBtnCallback) {
460-
prevBtnCallback();
461-
}
462459
window.hopscotch.prevStep();
463460
});
464461
utils.addClickListener(this.nextBtnEl, function(evt) {
465-
if (nextBtnCallback) {
466-
nextBtnCallback();
467-
}
468462
window.hopscotch.nextStep();
469463
});
470464
utils.addClickListener(this.doneBtnEl, window.hopscotch.endTour);
@@ -512,20 +506,18 @@
512506
this.element.appendChild(this.arrowEl);
513507
};
514508

515-
this.renderStep = function(step, idx, isLast, callback) {
509+
this.renderStep = function(step, idx, subIdx, isLast, callback) {
516510
var self = this,
517511
showNext = utils.valOrDefault(step.showNextButton, opt.showNextButton),
518512
showPrev = utils.valOrDefault(step.showPrevButton, opt.showPrevButton),
519513
bubbleWidth,
520514
bubblePadding;
521515

522-
currStep = step;
523-
524516
this.setTitle(step.title ? step.title : '');
525517
this.setContent(step.content ? step.content : '');
526518
this.setNum(idx);
527519

528-
this.showPrevButton(this.prevBtnEl && showPrev && idx > 0);
520+
this.showPrevButton(this.prevBtnEl && showPrev && (idx > 0 || subIdx > 0));
529521
this.showNextButton(this.nextBtnEl && showNext && !isLast);
530522
if (isLast) {
531523
utils.removeClass(this.doneBtnEl, 'hide');
@@ -555,10 +547,6 @@
555547
if (callback) { callback(); }
556548
}
557549

558-
// Set or clear new nav callbacks
559-
prevBtnCallback = step.onPrev;
560-
nextBtnCallback = step.onNext;
561-
562550
return this;
563551
};
564552

@@ -680,8 +668,10 @@
680668
opt,
681669
currTour,
682670
currStepNum,
671+
currSubstepNum,
683672
cookieTourId,
684673
cookieTourStep,
674+
cookieTourSubstep,
685675

686676
/**
687677
* getBubble
@@ -697,7 +687,58 @@
697687
},
698688

699689
getCurrStep = function() {
700-
return currTour.steps[currStepNum];
690+
var step = currTour.steps[currStepNum];
691+
692+
return (step.length > 0) ? step[currSubstepNum] : step;
693+
},
694+
695+
/**
696+
* incrementStep
697+
* =============
698+
* Sets current step num and substep num to the next step in the tour.
699+
* Returns true if successful, false if not.
700+
*/
701+
incrementStep = function() {
702+
var numSubsteps = currTour.steps[currStepNum].length;
703+
if (currSubstepNum < numSubsteps-1) {
704+
++currSubstepNum;
705+
return true;
706+
}
707+
else if (currStepNum < currTour.steps.length-1) {
708+
++currStepNum;
709+
currSubstepNum = (currStepNum.length > 0) ? 0 : undefined;
710+
return true;
711+
}
712+
return false;
713+
},
714+
715+
/**
716+
* decrementStep
717+
* =============
718+
* Sets current step num and substep num to the previous step in the tour.
719+
* Returns true if successful, false if not.
720+
*/
721+
decrementStep = function() {
722+
var numPrevSubsteps;
723+
if (currSubstepNum > 0) {
724+
--currSubstepNum;
725+
return true;
726+
}
727+
else if (currStepNum > 0) {
728+
numPrevSubsteps = currTour.steps[--currStepNum].length;
729+
if (numPrevSubsteps) {
730+
currSubstepNum = numPrevSubsteps-1;
731+
}
732+
else {
733+
currSubstepNum = undefined;
734+
}
735+
return true;
736+
}
737+
return false;
738+
},
739+
740+
isInMultiPartStep = function() {
741+
return currTour.steps[currStepNum].length > 0;
701742
},
702743

703744
/**
@@ -807,7 +848,8 @@
807848
this.loadTour = function(tour) {
808849
var tmpOpt = {},
809850
bubble,
810-
prop;
851+
prop,
852+
stepPair;
811853
currTour = tour;
812854

813855
// Set tour-specific configurations
@@ -823,12 +865,35 @@
823865
// Get existing tour state, if it exists.
824866
tourState = utils.getState(opt.cookieName);
825867
if (tourState) {
826-
tourPair = tourState.split(':');
827-
cookieTourId = tourPair[0]; // selecting tour is not supported by this framework.
828-
cookieTourStep = parseInt(tourPair[1], 10);
868+
tourPair = tourState.split(':');
869+
cookieTourId = tourPair[0]; // selecting tour is not supported by this framework.
870+
cookieTourStep = tourPair[1];
871+
cookieTourSubstep = undefined;
872+
stepPair = cookieTourStep.split('-');
873+
874+
if (stepPair.length > 1) {
875+
cookieTourStep = parseInt(stepPair[0], 10);
876+
cookieTourSubstep = parseInt(stepPair[1], 10);
877+
}
878+
else {
879+
cookieTourStep = parseInt(cookieTourStep, 10);
880+
}
881+
882+
// Check for multipage flag
829883
if (tourPair.length > 2 && tourPair[2] === 'mp') {
830-
// multipage... increment tour step by 1
831-
++cookieTourStep;
884+
// Increment cookie step
885+
if (cookieTourSubstep && cookieTourSubstep < currTour.steps[cookieTourStep].length-1) {
886+
++cookieTourSubstep;
887+
}
888+
else if (cookieTourStep < currTour.steps.length-1) {
889+
++cookieTourStep;
890+
if (currTour.steps[cookieTourStep].length > 0) {
891+
cookieTourSubstep = 0;
892+
}
893+
else {
894+
cookieTourSubstep = undefined;
895+
}
896+
}
832897
}
833898
}
834899

@@ -840,7 +905,8 @@
840905
};
841906

842907
this.startTour = function() {
843-
var bubble;
908+
var bubble,
909+
step;
844910

845911
if (!currTour) {
846912
throw "Need to load a tour before you start it!";
@@ -853,10 +919,14 @@
853919

854920
// Check if we are resuming state.
855921
if (currTour.id === cookieTourId && typeof cookieTourStep !== undefinedStr) {
856-
currStepNum = cookieTourStep;
857-
if (!document.getElementById(currTour.steps[currStepNum].targetId)) {
922+
currStepNum = cookieTourStep;
923+
currSubstepNum = cookieTourSubstep;
924+
step = getCurrStep();
925+
if (!document.getElementById(step.targetId)) {
926+
decrementStep();
927+
step = getCurrStep();
858928
// May have just refreshed the page. Previous step should work. (but don't change cookie)
859-
if (currStepNum <= 0 || !document.getElementById(currTour.steps[--currStepNum].targetId)) {
929+
if (!document.getElementById(step.targetId)) {
860930
// Previous target doesn't exist either. The user may have just
861931
// clicked on a link that wasn't part of the tour. Let's just "end"
862932
// the tour and depend on the cookie to pick the user back up where
@@ -870,7 +940,12 @@
870940
currStepNum = 0;
871941
}
872942

873-
this.showStep(currStepNum);
943+
if (!currSubstepNum && isInMultiPartStep()) {
944+
// Multi-part step
945+
currSubstepNum = 0;
946+
}
947+
948+
this.showStep(currStepNum, currSubstepNum);
874949
bubble = getBubble().show();
875950

876951
if (opt.animate) {
@@ -880,41 +955,63 @@
880955
return this;
881956
};
882957

883-
this.showStep = function(stepIdx) {
884-
var step = currTour.steps[stepIdx],
885-
numTourSteps = currTour.steps.length,
958+
this.showStep = function(stepIdx, substepIdx) {
959+
var tourSteps = currTour.steps,
960+
step = tourSteps[stepIdx],
961+
numTourSteps = tourSteps.length,
886962
cookieVal = currTour.id + ':' + stepIdx,
887-
bubble = getBubble();
963+
bubble = getBubble(),
964+
isLast;
888965

889-
if (!currTour) {
890-
throw "No tour currently selected!";
966+
// Update bubble for current step
967+
currStepNum = stepIdx;
968+
currSubstepNum = substepIdx;
969+
970+
if (typeof substepIdx !== undefinedStr) {
971+
step = step[substepIdx];
972+
cookieVal += '-' + substepIdx;
891973
}
892974

893-
// Update bubble for current step
894-
currStepNum = stepIdx;
895-
bubble.renderStep(step, stepIdx, (stepIdx === numTourSteps - 1), adjustWindowScroll);
975+
currStep = step;
976+
isLast = (stepIdx === numTourSteps - 1) || (substepIdx >= step.length - 1);
977+
bubble.renderStep(step, stepIdx, substepIdx, isLast, adjustWindowScroll);
896978

897979
if (step.multiPage) {
898980
cookieVal += ':mp';
899981
}
982+
900983
utils.setState(opt.cookieName, cookieVal, 1);
901984
return this;
902985
};
903986

904987
this.prevStep = function() {
905-
if (currStepNum > 0) {
906-
this.showStep(--currStepNum);
988+
var step = getCurrStep();
989+
990+
if (step.onPrev) {
991+
step.onPrev();
992+
}
993+
994+
if (decrementStep()) {
995+
this.showStep(currStepNum, currSubstepNum);
907996
}
908997
return this;
909998
};
910999

9111000
this.nextStep = function() {
1001+
var step = getCurrStep();
1002+
1003+
// invoke Next button callbacks
9121004
if (opt.onNext) {
913-
opt.onNext(getCurrStep(), currStepNum);
1005+
opt.onNext(step, currStepNum);
9141006
}
915-
if (currStepNum < currTour.steps.length-1) {
916-
this.showStep(++currStepNum);
1007+
if (step.onNext) {
1008+
step.onNext();
9171009
}
1010+
1011+
if (incrementStep()) {
1012+
this.showStep(currStepNum, currSubstepNum);
1013+
}
1014+
9181015
return this;
9191016
};
9201017

@@ -924,10 +1021,13 @@
9241021
* Cancels out of an active tour. No state is preserved.
9251022
*/
9261023
this.endTour = function(clearCookie) {
927-
var bubble = getBubble()
928-
clearCookie = utils.valOrDefault(clearCookie, true);
1024+
var bubble = getBubble();
1025+
clearCookie = utils.valOrDefault(clearCookie, true);
1026+
currStepNum = 0;
1027+
currSubstepNum = 0;
1028+
cookieTourStep = undefined;
1029+
9291030
bubble.hide();
930-
currStepNum = cookieTourStep = 0;
9311031
if (clearCookie) {
9321032
utils.clearState(opt.cookieName);
9331033
}
@@ -940,7 +1040,7 @@
9401040
* =========
9411041
* VALID OPTIONS INCLUDE...
9421042
* bubbleWidth: Number - Default bubble width. Defaults to 280.
943-
* bubblePadding: Number - Default bubble padding. Defaults to 10.
1043+
* bubblePadding: Number - Default bubble padding. Defaults to 15.
9441044
* bubbleBorder: Number - Default bubble border width. Defaults to 6.
9451045
* animate: Boolean - should the tour bubble animate between steps?
9461046
* Defaults to FALSE.
@@ -990,7 +1090,7 @@
9901090
opt.showPrevButton = utils.valOrDefault(opt.showPrevButton, false);
9911091
opt.showNextButton = utils.valOrDefault(opt.showNextButton, true);
9921092
opt.bubbleWidth = utils.valOrDefault(opt.bubbleWidth, 280);
993-
opt.bubblePadding = utils.valOrDefault(opt.bubblePadding, 10);
1093+
opt.bubblePadding = utils.valOrDefault(opt.bubblePadding, 15);
9941094
opt.bubbleBorder = utils.valOrDefault(opt.bubbleBorder, 6);
9951095
opt.arrowWidth = utils.valOrDefault(opt.arrowWidth, 20);
9961096
opt.onNext = utils.valOrDefault(opt.onNext, null);
@@ -1014,13 +1114,6 @@
10141114
};
10151115

10161116
this.init(initOptions);
1017-
1018-
// DEBUG
1019-
// =====
1020-
// REMOVE THIS LATER!!!
1021-
this.clearCookie = function() {
1022-
utils.clearState(opt.cookieName);
1023-
};
10241117
};
10251118

10261119
window.hopscotch = new Hopscotch();

0 commit comments

Comments
 (0)