|
42 | 42 | tooltipPosition: 'bottom',
|
43 | 43 | /* Next CSS class for tooltip boxes */
|
44 | 44 | tooltipClass: '',
|
| 45 | + /* CSS class that is added to the helperLayer */ |
| 46 | + highlightClass: '', |
45 | 47 | /* Close introduction when pressing Escape button? */
|
46 | 48 | exitOnEsc: true,
|
47 | 49 | /* Close introduction when clicking on overlay layer? */
|
|
54 | 56 | showButtons: true,
|
55 | 57 | /* Show tour bullets? */
|
56 | 58 | showBullets: true,
|
| 59 | + /* Show tour progress? */ |
| 60 | + showProgress: false, |
57 | 61 | /* Scroll to highlighted element? */
|
58 | 62 | scrollToElement: true,
|
59 | 63 | /* Set the overlay opacity */
|
|
130 | 134 | intro: currentElement.getAttribute('data-intro'),
|
131 | 135 | step: parseInt(currentElement.getAttribute('data-step'), 10),
|
132 | 136 | tooltipClass: currentElement.getAttribute('data-tooltipClass'),
|
| 137 | + highlightClass: currentElement.getAttribute('data-highlightClass'), |
133 | 138 | position: currentElement.getAttribute('data-position') || this._options.tooltipPosition
|
134 | 139 | };
|
135 | 140 | }
|
|
156 | 161 | intro: currentElement.getAttribute('data-intro'),
|
157 | 162 | step: nextStep + 1,
|
158 | 163 | tooltipClass: currentElement.getAttribute('data-tooltipClass'),
|
| 164 | + highlightClass: currentElement.getAttribute('data-highlightClass'), |
159 | 165 | position: currentElement.getAttribute('data-position') || this._options.tooltipPosition
|
160 | 166 | };
|
161 | 167 | }
|
|
197 | 203 | } else if(e.keyCode === 37) {
|
198 | 204 | //left arrow
|
199 | 205 | _previousStep.call(self);
|
200 |
| - } else if (e.keyCode === 39 || e.keyCode === 13) { |
201 |
| - //right arrow or enter |
| 206 | + } else if (e.keyCode === 39) { |
| 207 | + //right arrow |
202 | 208 | _nextStep.call(self);
|
| 209 | + } else if (e.keyCode === 13) { |
| 210 | + //srcElement === ie |
| 211 | + var target = e.target || e.srcElement; |
| 212 | + if (target && target.className.indexOf('introjs-prevbutton') > 0) { |
| 213 | + //user hit enter while focusing on previous button |
| 214 | + _previousStep.call(self); |
| 215 | + } else if (target && target.className.indexOf('introjs-skipbutton') > 0) { |
| 216 | + //user hit enter while focusing on skip button |
| 217 | + _exitIntro.call(self, targetElm); |
| 218 | + } else { |
| 219 | + //default behavior for responding to enter |
| 220 | + _nextStep.call(self); |
| 221 | + } |
| 222 | + |
203 | 223 | //prevent default behaviour on hitting Enter, to prevent steps being skipped in some browsers
|
204 | 224 | if(e.preventDefault) {
|
205 | 225 | e.preventDefault();
|
|
515 | 535 | break;
|
516 | 536 | }
|
517 | 537 | }
|
518 |
| - |
| 538 | + |
519 | 539 | /**
|
520 | 540 | * Determines the position of the tooltip based on the position precedence and availability
|
521 | 541 | * of screen space.
|
|
655 | 675 | var self = this,
|
656 | 676 | oldHelperLayer = document.querySelector('.introjs-helperLayer'),
|
657 | 677 | oldReferenceLayer = document.querySelector('.introjs-tooltipReferenceLayer'),
|
| 678 | + highlightClass = 'introjs-helperLayer', |
658 | 679 | elementPosition = _getOffset(targetElement.element);
|
659 | 680 |
|
| 681 | + //check for a current step highlight class |
| 682 | + if (typeof (targetElement.highlightClass) === 'string') { |
| 683 | + highlightClass += (' ' + targetElement.highlightClass); |
| 684 | + } |
| 685 | + //check for options highlight class |
| 686 | + if (typeof (this._options.highlightClass) === 'string') { |
| 687 | + highlightClass += (' ' + this._options.highlightClass); |
| 688 | + } |
| 689 | + |
660 | 690 | if (oldHelperLayer != null) {
|
661 | 691 | var oldHelperNumberLayer = oldReferenceLayer.querySelector('.introjs-helperNumberLayer'),
|
662 | 692 | oldtooltipLayer = oldReferenceLayer.querySelector('.introjs-tooltiptext'),
|
|
666 | 696 | prevTooltipButton = oldReferenceLayer.querySelector('.introjs-prevbutton'),
|
667 | 697 | nextTooltipButton = oldReferenceLayer.querySelector('.introjs-nextbutton');
|
668 | 698 |
|
| 699 | + //update or reset the helper highlight class |
| 700 | + oldHelperLayer.className = highlightClass; |
669 | 701 | //hide the tooltip
|
670 | 702 | oldtooltipContainer.style.opacity = 0;
|
671 | 703 | oldtooltipContainer.style.display = "none";
|
|
713 | 745 | oldReferenceLayer.querySelector('.introjs-bullets li > a.active').className = '';
|
714 | 746 | oldReferenceLayer.querySelector('.introjs-bullets li > a[data-stepnumber="' + targetElement.step + '"]').className = 'active';
|
715 | 747 |
|
| 748 | + oldReferenceLayer.querySelector('.introjs-progress .introjs-progressbar').setAttribute('style', 'width:' + _getProgress.call(self) + '%;'); |
| 749 | + |
716 | 750 | //show the tooltip
|
717 | 751 | oldtooltipContainer.style.opacity = 1;
|
718 | 752 | if (oldHelperNumberLayer) oldHelperNumberLayer.style.opacity = 1;
|
| 753 | + |
| 754 | + //reset button focus |
| 755 | + if (nextTooltipButton.tabIndex === -1) { |
| 756 | + //tabindex of -1 means we are at the end of the tour - focus on skip / done |
| 757 | + skipTooltipButton.focus(); |
| 758 | + } else { |
| 759 | + //still in the tour, focus on next |
| 760 | + nextTooltipButton.focus(); |
| 761 | + } |
719 | 762 | }, 350);
|
720 | 763 |
|
721 | 764 | } else {
|
|
725 | 768 | tooltipLayer = document.createElement('div'),
|
726 | 769 | tooltipTextLayer = document.createElement('div'),
|
727 | 770 | bulletsLayer = document.createElement('div'),
|
| 771 | + progressLayer = document.createElement('div'), |
728 | 772 | buttonsLayer = document.createElement('div');
|
729 | 773 |
|
730 |
| - helperLayer.className = 'introjs-helperLayer'; |
| 774 | + helperLayer.className = highlightClass; |
731 | 775 | referenceLayer.className = 'introjs-tooltipReferenceLayer';
|
732 | 776 |
|
733 | 777 | //set new position to helper layer
|
|
772 | 816 |
|
773 | 817 | bulletsLayer.appendChild(ulContainer);
|
774 | 818 |
|
| 819 | + progressLayer.className = 'introjs-progress'; |
| 820 | + |
| 821 | + if (this._options.showProgress === false) { |
| 822 | + progressLayer.style.display = 'none'; |
| 823 | + } |
| 824 | + var progressBar = document.createElement('div'); |
| 825 | + progressBar.className = 'introjs-progressbar'; |
| 826 | + progressBar.setAttribute('style', 'width:' + _getProgress.call(this) + '%;'); |
| 827 | + |
| 828 | + progressLayer.appendChild(progressBar); |
| 829 | + |
775 | 830 | buttonsLayer.className = 'introjs-tooltipbuttons';
|
776 | 831 | if (this._options.showButtons === false) {
|
777 | 832 | buttonsLayer.style.display = 'none';
|
|
780 | 835 | tooltipLayer.className = 'introjs-tooltip';
|
781 | 836 | tooltipLayer.appendChild(tooltipTextLayer);
|
782 | 837 | tooltipLayer.appendChild(bulletsLayer);
|
| 838 | + tooltipLayer.appendChild(progressLayer); |
783 | 839 |
|
784 | 840 | //add helper layer number
|
785 | 841 | if (this._options.showStepNumbers == true) {
|
|
853 | 909 | _disableInteraction.call(self);
|
854 | 910 | }
|
855 | 911 |
|
| 912 | + prevTooltipButton.removeAttribute('tabIndex'); |
| 913 | + nextTooltipButton.removeAttribute('tabIndex'); |
| 914 | + |
856 | 915 | if (this._currentStep == 0 && this._introItems.length > 1) {
|
857 | 916 | prevTooltipButton.className = 'introjs-button introjs-prevbutton introjs-disabled';
|
| 917 | + prevTooltipButton.tabIndex = '-1'; |
858 | 918 | nextTooltipButton.className = 'introjs-button introjs-nextbutton';
|
859 | 919 | skipTooltipButton.innerHTML = this._options.skipLabel;
|
860 | 920 | } else if (this._introItems.length - 1 == this._currentStep || this._introItems.length == 1) {
|
861 | 921 | skipTooltipButton.innerHTML = this._options.doneLabel;
|
862 | 922 | prevTooltipButton.className = 'introjs-button introjs-prevbutton';
|
863 | 923 | nextTooltipButton.className = 'introjs-button introjs-nextbutton introjs-disabled';
|
| 924 | + nextTooltipButton.tabIndex = '-1'; |
864 | 925 | } else {
|
865 | 926 | prevTooltipButton.className = 'introjs-button introjs-prevbutton';
|
866 | 927 | nextTooltipButton.className = 'introjs-button introjs-nextbutton';
|
|
1062 | 1123 | return elementPosition;
|
1063 | 1124 | }
|
1064 | 1125 |
|
| 1126 | + /** |
| 1127 | + * Gets the current progress percentage |
| 1128 | + * |
| 1129 | + * @api private |
| 1130 | + * @method _getProgress |
| 1131 | + * @returns current progress percentage |
| 1132 | + */ |
| 1133 | + function _getProgress() { |
| 1134 | + // Steps are 0 indexed |
| 1135 | + var currentStep = parseInt((this._currentStep + 1), 10); |
| 1136 | + return ((currentStep / this._introItems.length) * 100); |
| 1137 | + } |
| 1138 | + |
1065 | 1139 | /**
|
1066 | 1140 | * Overwrites obj1's values with obj2's and adds obj2's if non existent in obj1
|
1067 | 1141 | * via: http://stackoverflow.com/questions/171251/how-can-i-merge-properties-of-two-javascript-objects-dynamically
|
|
0 commit comments