Masterchoc

Untitled

Jun 7th, 2019
136
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /**
  2.  * Intro.js v2.9.3
  3.  * https://github.com/usablica/intro.js
  4.  *
  5.  * Copyright (C) 2017 Afshin Mehrabani (@afshinmeh)
  6.  */
  7.  
  8. (function(f) {
  9.     if (typeof exports === "object" && typeof module !== "undefined") {
  10.         module.exports = f();
  11.         // deprecated function
  12.         // @since 2.8.0
  13.         module.exports.introJs = function () {
  14.           console.warn('Deprecated: please use require("intro.js") directly, instead of the introJs method of the function');
  15.           // introJs()
  16.           return f().apply(this, arguments);
  17.         };
  18.     } else if (typeof define === "function" && define.amd) {
  19.         define([], f);
  20.     } else {
  21.         var g;
  22.         if (typeof window !== "undefined") {
  23.             g = window;
  24.         } else if (typeof global !== "undefined") {
  25.             g = global;
  26.         } else if (typeof self !== "undefined") {
  27.             g = self;
  28.         } else {
  29.             g = this;
  30.         }
  31.         g.introJs = f();
  32.     }
  33. })(function () {
  34.   //Default config/variables
  35.   var VERSION = '2.9.3';
  36.  
  37.   /**
  38.    * IntroJs main class
  39.    *
  40.    * @class IntroJs
  41.    */
  42.   function IntroJs(obj) {
  43.     this._targetElement = obj;
  44.     this._introItems = [];
  45.  
  46.     this._options = {
  47.       /* Next button label in tooltip box */
  48.       nextLabel: 'Next →',
  49.       /* Previous button label in tooltip box */
  50.       prevLabel: '← Back',
  51.       /* Skip button label in tooltip box */
  52.       skipLabel: 'Skip',
  53.       /* Done button label in tooltip box */
  54.       doneLabel: 'Done',
  55.       /* Hide previous button in the first step? Otherwise, it will be disabled button. */
  56.       hidePrev: false,
  57.       /* Hide next button in the last step? Otherwise, it will be disabled button. */
  58.       hideNext: false,
  59.       /* Default tooltip box position */
  60.       tooltipPosition: 'bottom',
  61.       /* Next CSS class for tooltip boxes */
  62.       tooltipClass: '',
  63.       /* CSS class that is added to the helperLayer */
  64.       highlightClass: '',
  65.       /* Close introduction when pressing Escape button? */
  66.       exitOnEsc: true,
  67.       /* Close introduction when clicking on overlay layer? */
  68.       exitOnOverlayClick: true,
  69.       /* Show step numbers in introduction? */
  70.       showStepNumbers: true,
  71.       /* Let user use keyboard to navigate the tour? */
  72.       keyboardNavigation: true,
  73.       /* Show tour control buttons? */
  74.       showButtons: true,
  75.       /* Show tour bullets? */
  76.       showBullets: true,
  77.       /* Show tour progress? */
  78.       showProgress: false,
  79.       /* Scroll to highlighted element? */
  80.       scrollToElement: true,
  81.       /*
  82.        * Should we scroll the tooltip or target element?
  83.        *
  84.        * Options are: 'element' or 'tooltip'
  85.        */
  86.       scrollTo: 'element',
  87.       /* Padding to add after scrolling when element is not in the viewport (in pixels) */
  88.       scrollPadding: 30,
  89.       /* Set the overlay opacity */
  90.       overlayOpacity: 0.8,
  91.       /* Precedence of positions, when auto is enabled */
  92.       positionPrecedence: ["bottom", "top", "right", "left"],
  93.       /* Disable an interaction with element? */
  94.       disableInteraction: false,
  95.       /* Set how much padding to be used around helper element */
  96.       helperElementPadding: 10,
  97.       /* Default hint position */
  98.       hintPosition: 'top-middle',
  99.       /* Hint button label */
  100.       hintButtonLabel: 'Got it',
  101.       /* Adding animation to hints? */
  102.       hintAnimation: true,
  103.       /* additional classes to put on the buttons */
  104.       buttonClass: "introjs-button"
  105.     };
  106.   }
  107.  
  108.   /**
  109.    * Initiate a new introduction/guide from an element in the page
  110.    *
  111.    * @api private
  112.    * @method _introForElement
  113.    * @param {Object} targetElm
  114.    * @param {String} group
  115.    * @returns {Boolean} Success or not?
  116.    */
  117.   function _introForElement(targetElm, group) {
  118.     var allIntroSteps = targetElm.querySelectorAll("*[data-intro]"),
  119.         introItems = [];
  120.  
  121.     if (this._options.steps) {
  122.       //use steps passed programmatically
  123.       _forEach(this._options.steps, function (step) {
  124.         var currentItem = _cloneObject(step);
  125.  
  126.         //set the step
  127.         currentItem.step = introItems.length + 1;
  128.  
  129.         //use querySelector function only when developer used CSS selector
  130.         if (typeof (currentItem.element) === 'string') {
  131.           //grab the element with given selector from the page
  132.           currentItem.element = document.querySelector(currentItem.element);
  133.         }
  134.  
  135.         //intro without element
  136.         if (typeof (currentItem.element) === 'undefined' || currentItem.element === null) {
  137.           var floatingElementQuery = document.querySelector(".introjsFloatingElement");
  138.  
  139.           if (floatingElementQuery === null) {
  140.             floatingElementQuery = document.createElement('div');
  141.             floatingElementQuery.className = 'introjsFloatingElement';
  142.  
  143.             document.body.appendChild(floatingElementQuery);
  144.           }
  145.  
  146.           currentItem.element  = floatingElementQuery;
  147.           currentItem.position = 'floating';
  148.         }
  149.  
  150.         currentItem.scrollTo = currentItem.scrollTo || this._options.scrollTo;
  151.  
  152.         if (typeof (currentItem.disableInteraction) === 'undefined') {
  153.           currentItem.disableInteraction = this._options.disableInteraction;
  154.         }
  155.  
  156.         if (currentItem.element !== null) {
  157.           introItems.push(currentItem);
  158.         }        
  159.       }.bind(this));
  160.  
  161.     } else {
  162.       //use steps from data-* annotations
  163.       var elmsLength = allIntroSteps.length;
  164.       var disableInteraction;
  165.      
  166.       //if there's no element to intro
  167.       if (elmsLength < 1) {
  168.         return false;
  169.       }
  170.  
  171.       _forEach(allIntroSteps, function (currentElement) {
  172.        
  173.         // PR #80
  174.         // start intro for groups of elements
  175.         if (group && (currentElement.getAttribute("data-intro-group") !== group)) {
  176.           return;
  177.         }
  178.  
  179.         // skip hidden elements
  180.         if (currentElement.style.display === 'none') {
  181.           return;
  182.         }
  183.  
  184.         var step = parseInt(currentElement.getAttribute('data-step'), 10);
  185.  
  186.         if (typeof (currentElement.getAttribute('data-disable-interaction')) !== 'undefined') {
  187.           disableInteraction = !!currentElement.getAttribute('data-disable-interaction');
  188.         } else {
  189.           disableInteraction = this._options.disableInteraction;
  190.         }
  191.  
  192.         if (step > 0) {
  193.           introItems[step - 1] = {
  194.             element: currentElement,
  195.             intro: currentElement.getAttribute('data-intro'),
  196.             step: parseInt(currentElement.getAttribute('data-step'), 10),
  197.             tooltipClass: currentElement.getAttribute('data-tooltipclass'),
  198.             highlightClass: currentElement.getAttribute('data-highlightclass'),
  199.             position: currentElement.getAttribute('data-position') || this._options.tooltipPosition,
  200.             scrollTo: currentElement.getAttribute('data-scrollto') || this._options.scrollTo,
  201.             disableInteraction: disableInteraction
  202.           };
  203.         }
  204.       }.bind(this));
  205.  
  206.       //next add intro items without data-step
  207.       //todo: we need a cleanup here, two loops are redundant
  208.       var nextStep = 0;
  209.  
  210.       _forEach(allIntroSteps, function (currentElement) {
  211.        
  212.         // PR #80
  213.         // start intro for groups of elements
  214.         if (group && (currentElement.getAttribute("data-intro-group") !== group)) {
  215.           return;
  216.         }
  217.        
  218.         if (currentElement.getAttribute('data-step') === null) {
  219.  
  220.           while (true) {
  221.             if (typeof introItems[nextStep] === 'undefined') {
  222.               break;
  223.             } else {
  224.               nextStep++;
  225.             }
  226.           }
  227.  
  228.           if (typeof (currentElement.getAttribute('data-disable-interaction')) !== 'undefined') {
  229.             disableInteraction = !!currentElement.getAttribute('data-disable-interaction');
  230.           } else {
  231.             disableInteraction = this._options.disableInteraction;
  232.           }
  233.  
  234.           introItems[nextStep] = {
  235.             element: currentElement,
  236.             intro: currentElement.getAttribute('data-intro'),
  237.             step: nextStep + 1,
  238.             tooltipClass: currentElement.getAttribute('data-tooltipclass'),
  239.             highlightClass: currentElement.getAttribute('data-highlightclass'),
  240.             position: currentElement.getAttribute('data-position') || this._options.tooltipPosition,
  241.             scrollTo: currentElement.getAttribute('data-scrollto') || this._options.scrollTo,
  242.             disableInteraction: disableInteraction
  243.           };
  244.         }
  245.       }.bind(this));
  246.     }
  247.  
  248.     //removing undefined/null elements
  249.     var tempIntroItems = [];
  250.     for (var z = 0; z < introItems.length; z++) {
  251.       if (introItems[z]) {
  252.         // copy non-falsy values to the end of the array
  253.         tempIntroItems.push(introItems[z]);  
  254.       }
  255.     }
  256.  
  257.     introItems = tempIntroItems;
  258.  
  259.     //Ok, sort all items with given steps
  260.     introItems.sort(function (a, b) {
  261.       return a.step - b.step;
  262.     });
  263.  
  264.     //set it to the introJs object
  265.     this._introItems = introItems;
  266.  
  267.     //add overlay layer to the page
  268.     if(_addOverlayLayer.call(this, targetElm)) {
  269.       //then, start the show
  270.       _nextStep.call(this);
  271.  
  272.       if (this._options.keyboardNavigation) {
  273.         DOMEvent.on(window, 'keydown', _onKeyDown, this, true);
  274.       }
  275.       //for window resize
  276.       DOMEvent.on(window, 'resize', _onResize, this, true);
  277.     }
  278.     return false;
  279.   }
  280.  
  281.   function _onResize () {
  282.     this.refresh.call(this);
  283.   }
  284.  
  285.   /**
  286.   * on keyCode:
  287.   * https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode
  288.   * This feature has been removed from the Web standards.
  289.   * Though some browsers may still support it, it is in
  290.   * the process of being dropped.
  291.   * Instead, you should use KeyboardEvent.code,
  292.   * if it's implemented.
  293.   *
  294.   * jQuery's approach is to test for
  295.   *   (1) e.which, then
  296.   *   (2) e.charCode, then
  297.   *   (3) e.keyCode
  298.   * https://github.com/jquery/jquery/blob/a6b0705294d336ae2f63f7276de0da1195495363/src/event.js#L638
  299.   *
  300.   * @param type var
  301.   * @return type
  302.   */
  303.   function _onKeyDown (e) {
  304.     var code = (e.code === null) ? e.which : e.code;
  305.  
  306.     // if code/e.which is null
  307.     if (code === null) {
  308.       code = (e.charCode === null) ? e.keyCode : e.charCode;
  309.     }
  310.    
  311.     if ((code === 'Escape' || code === 27) && this._options.exitOnEsc === true) {
  312.       //escape key pressed, exit the intro
  313.       //check if exit callback is defined
  314.       _exitIntro.call(this, this._targetElement);
  315.     } else if (code === 'ArrowLeft' || code === 37) {
  316.       //left arrow
  317.       _previousStep.call(this);
  318.     } else if (code === 'ArrowRight' || code === 39) {
  319.       //right arrow
  320.       _nextStep.call(this);
  321.     } else if (code === 'Enter' || code === 13) {
  322.       //srcElement === ie
  323.       var target = e.target || e.srcElement;
  324.       if (target && target.className.match('introjs-prevbutton')) {
  325.         //user hit enter while focusing on previous button
  326.         _previousStep.call(this);
  327.       } else if (target && target.className.match('introjs-skipbutton')) {
  328.         //user hit enter while focusing on skip button
  329.         if (this._introItems.length - 1 === this._currentStep && typeof (this._introCompleteCallback) === 'function') {
  330.             this._introCompleteCallback.call(this);
  331.         }
  332.  
  333.         _exitIntro.call(this, this._targetElement);
  334.       } else if (target && target.getAttribute('data-stepnumber')) {
  335.         // user hit enter while focusing on step bullet
  336.         target.click();
  337.       } else {
  338.         //default behavior for responding to enter
  339.         _nextStep.call(this);
  340.       }
  341.  
  342.       //prevent default behaviour on hitting Enter, to prevent steps being skipped in some browsers
  343.       if(e.preventDefault) {
  344.         e.preventDefault();
  345.       } else {
  346.         e.returnValue = false;
  347.       }
  348.     }
  349.   }
  350.  
  351.  /*
  352.    * makes a copy of the object
  353.    * @api private
  354.    * @method _cloneObject
  355.   */
  356.   function _cloneObject(object) {
  357.       if (object === null || typeof (object) !== 'object' || typeof (object.nodeType) !== 'undefined') {
  358.         return object;
  359.       }
  360.       var temp = {};
  361.       for (var key in object) {
  362.         if (typeof(window.jQuery) !== 'undefined' && object[key] instanceof window.jQuery) {
  363.           temp[key] = object[key];
  364.         } else {
  365.           temp[key] = _cloneObject(object[key]);
  366.         }
  367.       }
  368.       return temp;
  369.   }
  370.   /**
  371.    * Go to specific step of introduction
  372.    *
  373.    * @api private
  374.    * @method _goToStep
  375.    */
  376.   function _goToStep(step) {
  377.     //because steps starts with zero
  378.     this._currentStep = step - 2;
  379.     if (typeof (this._introItems) !== 'undefined') {
  380.       _nextStep.call(this);
  381.     }
  382.   }
  383.  
  384.   /**
  385.    * Go to the specific step of introduction with the explicit [data-step] number
  386.    *
  387.    * @api private
  388.    * @method _goToStepNumber
  389.    */
  390.   function _goToStepNumber(step) {
  391.     this._currentStepNumber = step;
  392.     if (typeof (this._introItems) !== 'undefined') {
  393.       _nextStep.call(this);
  394.     }
  395.   }
  396.  
  397.   /**
  398.    * Go to next step on intro
  399.    *
  400.    * @api private
  401.    * @method _nextStep
  402.    */
  403.   function _nextStep() {
  404.     this._direction = 'forward';
  405.  
  406.     if (typeof (this._currentStepNumber) !== 'undefined') {
  407.       _forEach(this._introItems, function (item, i) {
  408.         if( item.step === this._currentStepNumber ) {
  409.           this._currentStep = i - 1;
  410.           this._currentStepNumber = undefined;
  411.         }
  412.       }.bind(this));
  413.     }
  414.  
  415.     if (typeof (this._currentStep) === 'undefined') {
  416.       this._currentStep = 0;
  417.     } else {
  418.       ++this._currentStep;
  419.     }
  420.  
  421.     var nextStep = this._introItems[this._currentStep];
  422.     var continueStep = true;
  423.  
  424.     if (typeof (this._introBeforeChangeCallback) !== 'undefined') {
  425.       continueStep = this._introBeforeChangeCallback.call(this, nextStep.element);
  426.     }
  427.  
  428.     // if `onbeforechange` returned `false`, stop displaying the element
  429.     if (continueStep === false) {
  430.       --this._currentStep;
  431.       return false;
  432.     }
  433.  
  434.     if ((this._introItems.length) <= this._currentStep) {
  435.       //end of the intro
  436.       //check if any callback is defined
  437.       if (typeof (this._introCompleteCallback) === 'function') {
  438.         this._introCompleteCallback.call(this);
  439.       }
  440.       _exitIntro.call(this, this._targetElement);
  441.       return;
  442.     }
  443.  
  444.     _showElement.call(this, nextStep);
  445.   }
  446.  
  447.   /**
  448.    * Go to previous step on intro
  449.    *
  450.    * @api private
  451.    * @method _previousStep
  452.    */
  453.   function _previousStep() {
  454.     this._direction = 'backward';
  455.  
  456.     if (this._currentStep === 0) {
  457.       return false;
  458.     }
  459.  
  460.     --this._currentStep;
  461.  
  462.     var nextStep = this._introItems[this._currentStep];
  463.     var continueStep = true;
  464.  
  465.     if (typeof (this._introBeforeChangeCallback) !== 'undefined') {
  466.       continueStep = this._introBeforeChangeCallback.call(this, nextStep.element);
  467.     }
  468.  
  469.     // if `onbeforechange` returned `false`, stop displaying the element
  470.     if (continueStep === false) {
  471.       ++this._currentStep;
  472.       return false;
  473.     }
  474.  
  475.     _showElement.call(this, nextStep);
  476.   }
  477.  
  478.   /**
  479.    * Update placement of the intro objects on the screen
  480.    * @api private
  481.    */
  482.   function _refresh() {
  483.     // re-align intros
  484.  
  485.     var helperLayer = document.querySelector('.introjs-helperLayer');
  486.  
  487.     if(helperLayer.hasAttribute("data-hidden"))
  488.     {
  489.         helperLayer.style["background-color"] = "transparent";
  490.  
  491.         var highlighted = document.querySelector(".highlited");
  492.  
  493.         if(highlighted)
  494.             highlighted.style.color = "white";
  495.     }
  496.  
  497.     _setHelperLayerPosition.call(this, document.querySelector('.introjs-helperLayer'));
  498.     _setHelperLayerPosition.call(this, document.querySelector('.introjs-tooltipReferenceLayer'));
  499.     _setHelperLayerPosition.call(this, document.querySelector('.introjs-disableInteraction'));
  500.  
  501.     // re-align tooltip
  502.     if(this._currentStep !== undefined && this._currentStep !== null) {
  503.       var oldHelperNumberLayer = document.querySelector('.introjs-helperNumberLayer'),
  504.         oldArrowLayer        = document.querySelector('.introjs-arrow'),
  505.         oldtooltipContainer  = document.querySelector('.introjs-tooltip');
  506.       _placeTooltip.call(this, this._introItems[this._currentStep].element, oldtooltipContainer, oldArrowLayer, oldHelperNumberLayer);
  507.     }
  508.  
  509.     //re-align hints
  510.     _reAlignHints.call(this);
  511.     return this;
  512.   }
  513.  
  514.   /**
  515.    * Exit from intro
  516.    *
  517.    * @api private
  518.    * @method _exitIntro
  519.    * @param {Object} targetElement
  520.    * @param {Boolean} force - Setting to `true` will skip the result of beforeExit callback
  521.    */
  522.   function _exitIntro(targetElement, force) {
  523.     var continueExit = true;
  524.  
  525.     // calling onbeforeexit callback
  526.     //
  527.     // If this callback return `false`, it would halt the process
  528.     if (this._introBeforeExitCallback !== undefined) {
  529.       continueExit = this._introBeforeExitCallback.call(this);
  530.     }
  531.  
  532.     // skip this check if `force` parameter is `true`
  533.     // otherwise, if `onbeforeexit` returned `false`, don't exit the intro
  534.     if (!force && continueExit === false) return;
  535.  
  536.     //remove overlay layers from the page
  537.     var overlayLayers = targetElement.querySelectorAll('.introjs-overlay');
  538.  
  539.     if (overlayLayers && overlayLayers.length) {
  540.       _forEach(overlayLayers, function (overlayLayer) {
  541.         overlayLayer.style.opacity = 0;
  542.         window.setTimeout(function () {
  543.           if (this.parentNode) {
  544.             this.parentNode.removeChild(this);
  545.           }
  546.         }.bind(overlayLayer), 500);
  547.       }.bind(this));
  548.     }
  549.  
  550.     //remove all helper layers
  551.     var helperLayer = targetElement.querySelector('.introjs-helperLayer');
  552.     if (helperLayer) {
  553.       helperLayer.parentNode.removeChild(helperLayer);
  554.     }
  555.  
  556.     var referenceLayer = targetElement.querySelector('.introjs-tooltipReferenceLayer');
  557.     if (referenceLayer) {
  558.       referenceLayer.parentNode.removeChild(referenceLayer);
  559.     }
  560.  
  561.     //remove disableInteractionLayer
  562.     var disableInteractionLayer = targetElement.querySelector('.introjs-disableInteraction');
  563.     if (disableInteractionLayer) {
  564.       disableInteractionLayer.parentNode.removeChild(disableInteractionLayer);
  565.     }
  566.  
  567.     //remove intro floating element
  568.     var floatingElement = document.querySelector('.introjsFloatingElement');
  569.     if (floatingElement) {
  570.       floatingElement.parentNode.removeChild(floatingElement);
  571.     }
  572.  
  573.     _removeShowElement();
  574.  
  575.     //remove `introjs-fixParent` class from the elements
  576.     var fixParents = document.querySelectorAll('.introjs-fixParent');
  577.     _forEach(fixParents, function (parent) {
  578.       _removeClass(parent, /introjs-fixParent/g);
  579.     });
  580.  
  581.     //clean listeners
  582.     DOMEvent.off(window, 'keydown', _onKeyDown, this, true);
  583.     DOMEvent.off(window, 'resize', _onResize, this, true);
  584.  
  585.     //check if any callback is defined
  586.     if (this._introExitCallback !== undefined) {
  587.       this._introExitCallback.call(this);
  588.     }
  589.  
  590.     //set the step to zero
  591.     this._currentStep = undefined;
  592.   }
  593.  
  594.   /**
  595.    * Render tooltip box in the page
  596.    *
  597.    * @api private
  598.    * @method _placeTooltip
  599.    * @param {HTMLElement} targetElement
  600.    * @param {HTMLElement} tooltipLayer
  601.    * @param {HTMLElement} arrowLayer
  602.    * @param {HTMLElement} helperNumberLayer
  603.    * @param {Boolean} hintMode
  604.    */
  605.   function _placeTooltip(targetElement, tooltipLayer, arrowLayer, helperNumberLayer, hintMode) {
  606.     var tooltipCssClass = '',
  607.         currentStepObj,
  608.         tooltipOffset,
  609.         targetOffset,
  610.         windowSize,
  611.         currentTooltipPosition;
  612.  
  613.     hintMode = hintMode || false;
  614.  
  615.     //reset the old style
  616.     tooltipLayer.style.top        = null;
  617.     tooltipLayer.style.right      = null;
  618.     tooltipLayer.style.bottom     = null;
  619.     tooltipLayer.style.left       = null;
  620.     tooltipLayer.style.marginLeft = null;
  621.     tooltipLayer.style.marginTop  = null;
  622.  
  623.     arrowLayer.style.display = 'inherit';
  624.  
  625.     if (typeof(helperNumberLayer) !== 'undefined' && helperNumberLayer !== null) {
  626.       helperNumberLayer.style.top  = null;
  627.       helperNumberLayer.style.left = null;
  628.     }
  629.  
  630.     //prevent error when `this._currentStep` is undefined
  631.     if (!this._introItems[this._currentStep]) return;
  632.  
  633.     //if we have a custom css class for each step
  634.     currentStepObj = this._introItems[this._currentStep];
  635.     if (typeof (currentStepObj.tooltipClass) === 'string') {
  636.       tooltipCssClass = currentStepObj.tooltipClass;
  637.     } else {
  638.       tooltipCssClass = this._options.tooltipClass;
  639.     }
  640.  
  641.     tooltipLayer.className = ('introjs-tooltip ' + tooltipCssClass).replace(/^\s+|\s+$/g, '');
  642.     tooltipLayer.setAttribute('role', 'dialog');
  643.  
  644.     currentTooltipPosition = this._introItems[this._currentStep].position;
  645.  
  646.     // Floating is always valid, no point in calculating
  647.     if (currentTooltipPosition !== "floating") {
  648.       currentTooltipPosition = _determineAutoPosition.call(this, targetElement, tooltipLayer, currentTooltipPosition);
  649.     }
  650.  
  651.     var tooltipLayerStyleLeft;
  652.     targetOffset  = _getOffset(targetElement);
  653.     tooltipOffset = _getOffset(tooltipLayer);
  654.     windowSize    = _getWinSize();
  655.  
  656.     _addClass(tooltipLayer, 'introjs-' + currentTooltipPosition);
  657.  
  658.     switch (currentTooltipPosition) {
  659.       case 'top-right-aligned':
  660.         arrowLayer.className      = 'introjs-arrow bottom-right';
  661.  
  662.         var tooltipLayerStyleRight = 0;
  663.         _checkLeft(targetOffset, tooltipLayerStyleRight, tooltipOffset, tooltipLayer);
  664.         tooltipLayer.style.bottom    = (targetOffset.height +  20) + 'px';
  665.         break;
  666.  
  667.       case 'top-middle-aligned':
  668.         arrowLayer.className      = 'introjs-arrow bottom-middle';
  669.  
  670.         var tooltipLayerStyleLeftRight = targetOffset.width / 2 - tooltipOffset.width / 2;
  671.  
  672.         // a fix for middle aligned hints
  673.         if (hintMode) {
  674.           tooltipLayerStyleLeftRight += 5;
  675.         }
  676.  
  677.         if (_checkLeft(targetOffset, tooltipLayerStyleLeftRight, tooltipOffset, tooltipLayer)) {
  678.           tooltipLayer.style.right = null;
  679.           _checkRight(targetOffset, tooltipLayerStyleLeftRight, tooltipOffset, windowSize, tooltipLayer);
  680.         }
  681.         tooltipLayer.style.bottom = (targetOffset.height + 20) + 'px';
  682.         break;
  683.  
  684.       case 'top-left-aligned':
  685.       // top-left-aligned is the same as the default top
  686.       case 'top':
  687.         arrowLayer.className = 'introjs-arrow bottom';
  688.  
  689.         tooltipLayerStyleLeft = (hintMode) ? 0 : 15;
  690.  
  691.         _checkRight(targetOffset, tooltipLayerStyleLeft, tooltipOffset, windowSize, tooltipLayer);
  692.         tooltipLayer.style.bottom = (targetOffset.height +  20) + 'px';
  693.         break;
  694.       case 'right':
  695.         tooltipLayer.style.left = (targetOffset.width + 20) + 'px';
  696.         if (targetOffset.top + tooltipOffset.height > windowSize.height) {
  697.           // In this case, right would have fallen below the bottom of the screen.
  698.           // Modify so that the bottom of the tooltip connects with the target
  699.           arrowLayer.className = "introjs-arrow left-bottom";
  700.           tooltipLayer.style.top = "-" + (tooltipOffset.height - targetOffset.height - 20) + "px";
  701.         } else {
  702.           arrowLayer.className = 'introjs-arrow left';
  703.         }
  704.         break;
  705.       case 'left':
  706.         if (!hintMode && this._options.showStepNumbers === true) {
  707.           tooltipLayer.style.top = '15px';
  708.         }
  709.  
  710.         if (targetOffset.top + tooltipOffset.height > windowSize.height) {
  711.           // In this case, left would have fallen below the bottom of the screen.
  712.           // Modify so that the bottom of the tooltip connects with the target
  713.           tooltipLayer.style.top = "-" + (tooltipOffset.height - targetOffset.height - 20) + "px";
  714.           arrowLayer.className = 'introjs-arrow right-bottom';
  715.         } else {
  716.           arrowLayer.className = 'introjs-arrow right';
  717.         }
  718.         tooltipLayer.style.right = (targetOffset.width + 20) + 'px';
  719.  
  720.         break;
  721.       case 'floating':
  722.         arrowLayer.style.display = 'none';
  723.  
  724.         //we have to adjust the top and left of layer manually for intro items without element
  725.         tooltipLayer.style.left   = '50%';
  726.         tooltipLayer.style.top    = '50%';
  727.         tooltipLayer.style.marginLeft = '-' + (tooltipOffset.width / 2)  + 'px';
  728.         tooltipLayer.style.marginTop  = '-' + (tooltipOffset.height / 2) + 'px';
  729.  
  730.         if (typeof(helperNumberLayer) !== 'undefined' && helperNumberLayer !== null) {
  731.           helperNumberLayer.style.left = '-' + ((tooltipOffset.width / 2) + 18) + 'px';
  732.           helperNumberLayer.style.top  = '-' + ((tooltipOffset.height / 2) + 18) + 'px';
  733.         }
  734.  
  735.         break;
  736.       case 'bottom-right-aligned':
  737.         arrowLayer.className      = 'introjs-arrow top-right';
  738.  
  739.         tooltipLayerStyleRight = 0;
  740.         _checkLeft(targetOffset, tooltipLayerStyleRight, tooltipOffset, tooltipLayer);
  741.         tooltipLayer.style.top    = (targetOffset.height +  20) + 'px';
  742.         break;
  743.  
  744.       case 'bottom-middle-aligned':
  745.         arrowLayer.className      = 'introjs-arrow top-middle';
  746.  
  747.         tooltipLayerStyleLeftRight = targetOffset.width / 2 - tooltipOffset.width / 2;
  748.  
  749.         // a fix for middle aligned hints
  750.         if (hintMode) {
  751.           tooltipLayerStyleLeftRight += 5;
  752.         }
  753.  
  754.         if (_checkLeft(targetOffset, tooltipLayerStyleLeftRight, tooltipOffset, tooltipLayer)) {
  755.           tooltipLayer.style.right = null;
  756.           _checkRight(targetOffset, tooltipLayerStyleLeftRight, tooltipOffset, windowSize, tooltipLayer);
  757.         }
  758.         tooltipLayer.style.top = (targetOffset.height + 20) + 'px';
  759.         break;
  760.  
  761.       // case 'bottom-left-aligned':
  762.       // Bottom-left-aligned is the same as the default bottom
  763.       // case 'bottom':
  764.       // Bottom going to follow the default behavior
  765.       default:
  766.         arrowLayer.className = 'introjs-arrow top';
  767.  
  768.         tooltipLayerStyleLeft = 0;
  769.         _checkRight(targetOffset, tooltipLayerStyleLeft, tooltipOffset, windowSize, tooltipLayer);
  770.         tooltipLayer.style.top    = (targetOffset.height +  20) + 'px';
  771.     }
  772.   }
  773.  
  774.   /**
  775.    * Set tooltip left so it doesn't go off the right side of the window
  776.    *
  777.    * @return boolean true, if tooltipLayerStyleLeft is ok.  false, otherwise.
  778.    */
  779.   function _checkRight(targetOffset, tooltipLayerStyleLeft, tooltipOffset, windowSize, tooltipLayer) {
  780.     if (targetOffset.left + tooltipLayerStyleLeft + tooltipOffset.width > windowSize.width) {
  781.       // off the right side of the window
  782.       tooltipLayer.style.left = (windowSize.width - tooltipOffset.width - targetOffset.left) + 'px';
  783.       return false;
  784.     }
  785.     tooltipLayer.style.left = tooltipLayerStyleLeft + 'px';
  786.     return true;
  787.   }
  788.  
  789.   /**
  790.    * Set tooltip right so it doesn't go off the left side of the window
  791.    *
  792.    * @return boolean true, if tooltipLayerStyleRight is ok.  false, otherwise.
  793.    */
  794.   function _checkLeft(targetOffset, tooltipLayerStyleRight, tooltipOffset, tooltipLayer) {
  795.     if (targetOffset.left + targetOffset.width - tooltipLayerStyleRight - tooltipOffset.width < 0) {
  796.       // off the left side of the window
  797.       tooltipLayer.style.left = (-targetOffset.left) + 'px';
  798.       return false;
  799.     }
  800.     tooltipLayer.style.right = tooltipLayerStyleRight + 'px';
  801.     return true;
  802.   }
  803.  
  804.   /**
  805.    * Determines the position of the tooltip based on the position precedence and availability
  806.    * of screen space.
  807.    *
  808.    * @param {Object}    targetElement
  809.    * @param {Object}    tooltipLayer
  810.    * @param {String}    desiredTooltipPosition
  811.    * @return {String}   calculatedPosition
  812.    */
  813.   function _determineAutoPosition(targetElement, tooltipLayer, desiredTooltipPosition) {
  814.  
  815.     // Take a clone of position precedence. These will be the available
  816.     var possiblePositions = this._options.positionPrecedence.slice();
  817.  
  818.     var windowSize = _getWinSize();
  819.     var tooltipHeight = _getOffset(tooltipLayer).height + 10;
  820.     var tooltipWidth = _getOffset(tooltipLayer).width + 20;
  821.     var targetElementRect = targetElement.getBoundingClientRect();
  822.  
  823.     // If we check all the possible areas, and there are no valid places for the tooltip, the element
  824.     // must take up most of the screen real estate. Show the tooltip floating in the middle of the screen.
  825.     var calculatedPosition = "floating";
  826.  
  827.     /*
  828.     * auto determine position
  829.     */
  830.  
  831.     // Check for space below
  832.     if (targetElementRect.bottom + tooltipHeight > windowSize.height) {
  833.       _removeEntry(possiblePositions, "bottom");
  834.     }
  835.  
  836.     // Check for space above
  837.     if (targetElementRect.top - tooltipHeight < 0) {
  838.       _removeEntry(possiblePositions, "top");
  839.     }
  840.  
  841.     // Check for space to the right
  842.     if (targetElementRect.right + tooltipWidth > windowSize.width) {
  843.       _removeEntry(possiblePositions, "right");
  844.     }
  845.  
  846.     // Check for space to the left
  847.     if (targetElementRect.left - tooltipWidth < 0) {
  848.       _removeEntry(possiblePositions, "left");
  849.     }
  850.  
  851.     // @var {String}  ex: 'right-aligned'
  852.     var desiredAlignment = (function (pos) {
  853.       var hyphenIndex = pos.indexOf('-');
  854.       if (hyphenIndex !== -1) {
  855.         // has alignment
  856.         return pos.substr(hyphenIndex);
  857.       }
  858.       return '';
  859.     })(desiredTooltipPosition || '');
  860.  
  861.     // strip alignment from position
  862.     if (desiredTooltipPosition) {
  863.       // ex: "bottom-right-aligned"
  864.       // should return 'bottom'
  865.       desiredTooltipPosition = desiredTooltipPosition.split('-')[0];
  866.     }
  867.  
  868.     if (possiblePositions.length) {
  869.       if (desiredTooltipPosition !== "auto" &&
  870.           possiblePositions.indexOf(desiredTooltipPosition) > -1) {
  871.         // If the requested position is in the list, choose that
  872.         calculatedPosition = desiredTooltipPosition;
  873.       } else {
  874.         // Pick the first valid position, in order
  875.         calculatedPosition = possiblePositions[0];
  876.       }
  877.     }
  878.  
  879.     // only top and bottom positions have optional alignments
  880.     if (['top', 'bottom'].indexOf(calculatedPosition) !== -1) {
  881.       calculatedPosition += _determineAutoAlignment(targetElementRect.left, tooltipWidth, windowSize, desiredAlignment);
  882.     }
  883.  
  884.     return calculatedPosition;
  885.   }
  886.  
  887.   /**
  888.   * auto-determine alignment
  889.   * @param {Integer}  offsetLeft
  890.   * @param {Integer}  tooltipWidth
  891.   * @param {Object}   windowSize
  892.   * @param {String}   desiredAlignment
  893.   * @return {String}  calculatedAlignment
  894.   */
  895.   function _determineAutoAlignment (offsetLeft, tooltipWidth, windowSize, desiredAlignment) {
  896.     var halfTooltipWidth = tooltipWidth / 2,
  897.       winWidth = Math.min(windowSize.width, window.screen.width),
  898.       possibleAlignments = ['-left-aligned', '-middle-aligned', '-right-aligned'],
  899.       calculatedAlignment = '';
  900.    
  901.     // valid left must be at least a tooltipWidth
  902.     // away from right side
  903.     if (winWidth - offsetLeft < tooltipWidth) {
  904.       _removeEntry(possibleAlignments, '-left-aligned');
  905.     }
  906.  
  907.     // valid middle must be at least half
  908.     // width away from both sides
  909.     if (offsetLeft < halfTooltipWidth ||
  910.       winWidth - offsetLeft < halfTooltipWidth) {
  911.       _removeEntry(possibleAlignments, '-middle-aligned');
  912.     }
  913.  
  914.     // valid right must be at least a tooltipWidth
  915.     // width away from left side
  916.     if (offsetLeft < tooltipWidth) {
  917.       _removeEntry(possibleAlignments, '-right-aligned');
  918.     }
  919.  
  920.     if (possibleAlignments.length) {
  921.       if (possibleAlignments.indexOf(desiredAlignment) !== -1) {
  922.         // the desired alignment is valid
  923.         calculatedAlignment = desiredAlignment;
  924.       } else {
  925.         // pick the first valid position, in order
  926.         calculatedAlignment = possibleAlignments[0];
  927.       }
  928.     } else {
  929.       // if screen width is too small
  930.       // for ANY alignment, middle is
  931.       // probably the best for visibility
  932.       calculatedAlignment = '-middle-aligned';
  933.     }
  934.  
  935.     return calculatedAlignment;
  936.   }
  937.  
  938.   /**
  939.    * Remove an entry from a string array if it's there, does nothing if it isn't there.
  940.    *
  941.    * @param {Array} stringArray
  942.    * @param {String} stringToRemove
  943.    */
  944.   function _removeEntry(stringArray, stringToRemove) {
  945.     if (stringArray.indexOf(stringToRemove) > -1) {
  946.       stringArray.splice(stringArray.indexOf(stringToRemove), 1);
  947.     }
  948.   }
  949.  
  950.   /**
  951.    * Update the position of the helper layer on the screen
  952.    *
  953.    * @api private
  954.    * @method _setHelperLayerPosition
  955.    * @param {Object} helperLayer
  956.    */
  957.   function _setHelperLayerPosition(helperLayer) {
  958.     if (helperLayer) {
  959.       //prevent error when `this._currentStep` in undefined
  960.       if (!this._introItems[this._currentStep]) return;
  961.  
  962.       var currentElement  = this._introItems[this._currentStep],
  963.           elementPosition = _getOffset(currentElement.element),
  964.           widthHeightPadding = this._options.helperElementPadding;
  965.  
  966.       // If the target element is fixed, the tooltip should be fixed as well.
  967.       // Otherwise, remove a fixed class that may be left over from the previous
  968.       // step.
  969.       if (_isFixed(currentElement.element)) {
  970.         _addClass(helperLayer, 'introjs-fixedTooltip');
  971.       } else {
  972.         _removeClass(helperLayer, 'introjs-fixedTooltip');
  973.       }
  974.  
  975.       if (currentElement.position === 'floating') {
  976.         widthHeightPadding = 0;
  977.       }
  978.  
  979.       //set new position to helper layer
  980.       helperLayer.style.cssText = 'width: ' + (elementPosition.width  + widthHeightPadding)  + 'px; ' +
  981.                                         'height:' + (elementPosition.height + widthHeightPadding)  + 'px; ' +
  982.                                         'top:'    + (elementPosition.top    - widthHeightPadding / 2)   + 'px;' +
  983.                                         'left: '  + (elementPosition.left   - widthHeightPadding / 2)   + 'px;';
  984.  
  985.     }
  986.   }
  987.  
  988.   /**
  989.    * Add disableinteraction layer and adjust the size and position of the layer
  990.    *
  991.    * @api private
  992.    * @method _disableInteraction
  993.    */
  994.   function _disableInteraction() {
  995.     var disableInteractionLayer = document.querySelector('.introjs-disableInteraction');
  996.  
  997.     if (disableInteractionLayer === null) {
  998.       disableInteractionLayer = document.createElement('div');
  999.       disableInteractionLayer.className = 'introjs-disableInteraction';
  1000.       this._targetElement.appendChild(disableInteractionLayer);
  1001.     }
  1002.  
  1003.     _setHelperLayerPosition.call(this, disableInteractionLayer);
  1004.   }
  1005.  
  1006.   /**
  1007.    * Setting anchors to behave like buttons
  1008.    *
  1009.    * @api private
  1010.    * @method _setAnchorAsButton
  1011.    */
  1012.   function _setAnchorAsButton(anchor){
  1013.     anchor.setAttribute('role', 'button');
  1014.     anchor.tabIndex = 0;
  1015.   }
  1016.  
  1017.   /**
  1018.    * Show an element on the page
  1019.    *
  1020.    * @api private
  1021.    * @method _showElement
  1022.    * @param {Object} targetElement
  1023.    */
  1024.   function _showElement(targetElement) {
  1025.     if (typeof (this._introChangeCallback) !== 'undefined') {
  1026.       this._introChangeCallback.call(this, targetElement.element);
  1027.     }
  1028.  
  1029.     var self = this,
  1030.         oldHelperLayer = document.querySelector('.introjs-helperLayer'),
  1031.         oldReferenceLayer = document.querySelector('.introjs-tooltipReferenceLayer'),
  1032.         highlightClass = 'introjs-helperLayer',
  1033.         nextTooltipButton,
  1034.         prevTooltipButton,
  1035.         skipTooltipButton,
  1036.         scrollParent;
  1037.  
  1038.     //check for a current step highlight class
  1039.     if (typeof (targetElement.highlightClass) === 'string') {
  1040.       highlightClass += (' ' + targetElement.highlightClass);
  1041.     }
  1042.     //check for options highlight class
  1043.     if (typeof (this._options.highlightClass) === 'string') {
  1044.       highlightClass += (' ' + this._options.highlightClass);
  1045.     }
  1046.  
  1047.     if (oldHelperLayer !== null) {
  1048.       var oldHelperNumberLayer = oldReferenceLayer.querySelector('.introjs-helperNumberLayer'),
  1049.           oldtooltipLayer      = oldReferenceLayer.querySelector('.introjs-tooltiptext'),
  1050.           oldArrowLayer        = oldReferenceLayer.querySelector('.introjs-arrow'),
  1051.           oldtooltipContainer  = oldReferenceLayer.querySelector('.introjs-tooltip');
  1052.          
  1053.       skipTooltipButton    = oldReferenceLayer.querySelector('.introjs-skipbutton');
  1054.       prevTooltipButton    = oldReferenceLayer.querySelector('.introjs-prevbutton');
  1055.       nextTooltipButton    = oldReferenceLayer.querySelector('.introjs-nextbutton');
  1056.  
  1057.       //update or reset the helper highlight class
  1058.       oldHelperLayer.className = highlightClass;
  1059.       //hide the tooltip
  1060.       oldtooltipContainer.style.opacity = 0;
  1061.       oldtooltipContainer.style.display = "none";
  1062.  
  1063.       if (oldHelperNumberLayer !== null) {
  1064.         var lastIntroItem = this._introItems[(targetElement.step - 2 >= 0 ? targetElement.step - 2 : 0)];
  1065.  
  1066.         if (lastIntroItem !== null && (this._direction === 'forward' && lastIntroItem.position === 'floating') || (this._direction === 'backward' && targetElement.position === 'floating')) {
  1067.           oldHelperNumberLayer.style.opacity = 0;
  1068.         }
  1069.       }
  1070.  
  1071.       // scroll to element
  1072.       scrollParent = _getScrollParent( targetElement.element );
  1073.  
  1074.       if (scrollParent !== document.body) {
  1075.         // target is within a scrollable element
  1076.         _scrollParentToElement(scrollParent, targetElement.element);
  1077.       }
  1078.  
  1079.       // set new position to helper layer
  1080.       _setHelperLayerPosition.call(self, oldHelperLayer);
  1081.       _setHelperLayerPosition.call(self, oldReferenceLayer);
  1082.  
  1083.       //remove `introjs-fixParent` class from the elements
  1084.       var fixParents = document.querySelectorAll('.introjs-fixParent');
  1085.       _forEach(fixParents, function (parent) {
  1086.         _removeClass(parent, /introjs-fixParent/g);
  1087.       });
  1088.      
  1089.       //remove old classes if the element still exist
  1090.       _removeShowElement();
  1091.  
  1092.       //we should wait until the CSS3 transition is competed (it's 0.3 sec) to prevent incorrect `height` and `width` calculation
  1093.       if (self._lastShowElementTimer) {
  1094.         window.clearTimeout(self._lastShowElementTimer);
  1095.       }
  1096.  
  1097.       self._lastShowElementTimer = window.setTimeout(function() {
  1098.         //set current step to the label
  1099.         if (oldHelperNumberLayer !== null) {
  1100.           oldHelperNumberLayer.innerHTML = targetElement.step;
  1101.         }
  1102.         //set current tooltip text
  1103.         oldtooltipLayer.innerHTML = targetElement.intro;
  1104.         //set the tooltip position
  1105.         oldtooltipContainer.style.display = "block";
  1106.         _placeTooltip.call(self, targetElement.element, oldtooltipContainer, oldArrowLayer, oldHelperNumberLayer);
  1107.  
  1108.         //change active bullet
  1109.         if (self._options.showBullets) {
  1110.             oldReferenceLayer.querySelector('.introjs-bullets li > a.active').className = '';
  1111.             oldReferenceLayer.querySelector('.introjs-bullets li > a[data-stepnumber="' + targetElement.step + '"]').className = 'active';
  1112.         }
  1113.         oldReferenceLayer.querySelector('.introjs-progress .introjs-progressbar').style.cssText = 'width:' + _getProgress.call(self) + '%;';
  1114.         oldReferenceLayer.querySelector('.introjs-progress .introjs-progressbar').setAttribute('aria-valuenow', _getProgress.call(self));
  1115.  
  1116.         //show the tooltip
  1117.         oldtooltipContainer.style.opacity = 1;
  1118.         if (oldHelperNumberLayer) oldHelperNumberLayer.style.opacity = 1;
  1119.  
  1120.         //reset button focus
  1121.         if (typeof skipTooltipButton !== "undefined" && skipTooltipButton !== null && /introjs-donebutton/gi.test(skipTooltipButton.className)) {
  1122.           // skip button is now "done" button
  1123.           skipTooltipButton.focus();
  1124.         } else if (typeof nextTooltipButton !== "undefined" && nextTooltipButton !== null) {
  1125.           //still in the tour, focus on next
  1126.           nextTooltipButton.focus();
  1127.         }
  1128.  
  1129.         // change the scroll of the window, if needed
  1130.         _scrollTo.call(self, targetElement.scrollTo, targetElement, oldtooltipLayer);
  1131.       }, 350);
  1132.  
  1133.       // end of old element if-else condition
  1134.     } else {
  1135.       var helperLayer       = document.createElement('div'),
  1136.           referenceLayer    = document.createElement('div'),
  1137.           arrowLayer        = document.createElement('div'),
  1138.           tooltipLayer      = document.createElement('div'),
  1139.           tooltipTextLayer  = document.createElement('div'),
  1140.           bulletsLayer      = document.createElement('div'),
  1141.           progressLayer     = document.createElement('div'),
  1142.           buttonsLayer      = document.createElement('div');
  1143.  
  1144.       helperLayer.className = highlightClass;
  1145.       referenceLayer.className = 'introjs-tooltipReferenceLayer';
  1146.  
  1147.       // scroll to element
  1148.       scrollParent = _getScrollParent( targetElement.element );
  1149.  
  1150.       if (scrollParent !== document.body) {
  1151.         // target is within a scrollable element
  1152.         _scrollParentToElement(scrollParent, targetElement.element);
  1153.       }
  1154.  
  1155.       //set new position to helper layer
  1156.       _setHelperLayerPosition.call(self, helperLayer);
  1157.       _setHelperLayerPosition.call(self, referenceLayer);
  1158.  
  1159.       //add helper layer to target element
  1160.       this._targetElement.appendChild(helperLayer);
  1161.       this._targetElement.appendChild(referenceLayer);
  1162.  
  1163.       arrowLayer.className = 'introjs-arrow';
  1164.  
  1165.       tooltipTextLayer.className = 'introjs-tooltiptext';
  1166.       tooltipTextLayer.innerHTML = targetElement.intro;
  1167.  
  1168.       bulletsLayer.className = 'introjs-bullets';
  1169.  
  1170.       if (this._options.showBullets === false) {
  1171.         bulletsLayer.style.display = 'none';
  1172.       }
  1173.  
  1174.       var ulContainer = document.createElement('ul');
  1175.       ulContainer.setAttribute('role', 'tablist');
  1176.  
  1177.       var anchorClick = function () {
  1178.           self.goToStep(this.getAttribute('data-stepnumber'));
  1179.       };
  1180.  
  1181.       _forEach(this._introItems, function (item, i) {
  1182.         var innerLi    = document.createElement('li');
  1183.         var anchorLink = document.createElement('a');
  1184.        
  1185.         innerLi.setAttribute('role', 'presentation');
  1186.         anchorLink.setAttribute('role', 'tab');
  1187.  
  1188.         anchorLink.onclick = anchorClick;
  1189.  
  1190.         if (i === (targetElement.step-1)) {
  1191.           anchorLink.className = 'active';
  1192.         }
  1193.  
  1194.         _setAnchorAsButton(anchorLink);
  1195.         anchorLink.innerHTML = "&nbsp;";
  1196.         anchorLink.setAttribute('data-stepnumber', item.step);
  1197.  
  1198.         innerLi.appendChild(anchorLink);
  1199.         ulContainer.appendChild(innerLi);
  1200.       });
  1201.  
  1202.       bulletsLayer.appendChild(ulContainer);
  1203.  
  1204.       progressLayer.className = 'introjs-progress';
  1205.  
  1206.       if (this._options.showProgress === false) {
  1207.         progressLayer.style.display = 'none';
  1208.       }
  1209.       var progressBar = document.createElement('div');
  1210.       progressBar.className = 'introjs-progressbar';
  1211.       progressBar.setAttribute('role', 'progress');
  1212.       progressBar.setAttribute('aria-valuemin', 0);
  1213.       progressBar.setAttribute('aria-valuemax', 100);
  1214.       progressBar.setAttribute('aria-valuenow', _getProgress.call(this));
  1215.       progressBar.style.cssText = 'width:' + _getProgress.call(this) + '%;';
  1216.  
  1217.       progressLayer.appendChild(progressBar);
  1218.  
  1219.       buttonsLayer.className = 'introjs-tooltipbuttons';
  1220.       if (this._options.showButtons === false) {
  1221.         buttonsLayer.style.display = 'none';
  1222.       }
  1223.  
  1224.       tooltipLayer.className = 'introjs-tooltip';
  1225.       tooltipLayer.appendChild(tooltipTextLayer);
  1226.       tooltipLayer.appendChild(bulletsLayer);
  1227.       tooltipLayer.appendChild(progressLayer);
  1228.  
  1229.       //add helper layer number
  1230.       var helperNumberLayer = document.createElement('span');
  1231.       if (this._options.showStepNumbers === true) {
  1232.         helperNumberLayer.className = 'introjs-helperNumberLayer';
  1233.         helperNumberLayer.innerHTML = targetElement.step;
  1234.         referenceLayer.appendChild(helperNumberLayer);
  1235.       }
  1236.  
  1237.       tooltipLayer.appendChild(arrowLayer);
  1238.       referenceLayer.appendChild(tooltipLayer);
  1239.  
  1240.       //next button
  1241.       nextTooltipButton = document.createElement('a');
  1242.  
  1243.       nextTooltipButton.onclick = function() {
  1244.         if (self._introItems.length - 1 !== self._currentStep) {
  1245.           _nextStep.call(self);
  1246.         }
  1247.       };
  1248.  
  1249.       _setAnchorAsButton(nextTooltipButton);
  1250.       nextTooltipButton.innerHTML = this._options.nextLabel;
  1251.  
  1252.       //previous button
  1253.       prevTooltipButton = document.createElement('a');
  1254.  
  1255.       prevTooltipButton.onclick = function() {
  1256.         if (self._currentStep !== 0) {
  1257.           _previousStep.call(self);
  1258.         }
  1259.       };
  1260.  
  1261.       _setAnchorAsButton(prevTooltipButton);
  1262.       prevTooltipButton.innerHTML = this._options.prevLabel;
  1263.  
  1264.       //skip button
  1265.       skipTooltipButton = document.createElement('a');
  1266.       skipTooltipButton.className = this._options.buttonClass + ' introjs-skipbutton ';
  1267.       _setAnchorAsButton(skipTooltipButton);
  1268.       skipTooltipButton.innerHTML = this._options.skipLabel;
  1269.  
  1270.       skipTooltipButton.onclick = function() {
  1271.         if (self._introItems.length - 1 === self._currentStep && typeof (self._introCompleteCallback) === 'function') {
  1272.           self._introCompleteCallback.call(self);
  1273.         }
  1274.  
  1275.         if (self._introItems.length - 1 !== self._currentStep && typeof (self._introExitCallback) === 'function') {
  1276.           self._introExitCallback.call(self);
  1277.         }
  1278.  
  1279.         if (typeof(self._introSkipCallback) === 'function') {
  1280.           self._introSkipCallback.call(self);
  1281.         }
  1282.  
  1283.         _exitIntro.call(self, self._targetElement);
  1284.       };
  1285.  
  1286.       buttonsLayer.appendChild(skipTooltipButton);
  1287.  
  1288.       //in order to prevent displaying next/previous button always
  1289.       if (this._introItems.length > 1) {
  1290.         buttonsLayer.appendChild(prevTooltipButton);
  1291.         buttonsLayer.appendChild(nextTooltipButton);
  1292.       }
  1293.  
  1294.       tooltipLayer.appendChild(buttonsLayer);
  1295.  
  1296.       //set proper position
  1297.       _placeTooltip.call(self, targetElement.element, tooltipLayer, arrowLayer, helperNumberLayer);
  1298.  
  1299.       // change the scroll of the window, if needed
  1300.       _scrollTo.call(this, targetElement.scrollTo, targetElement, tooltipLayer);
  1301.  
  1302.       //end of new element if-else condition
  1303.     }
  1304.  
  1305.     // removing previous disable interaction layer
  1306.     var disableInteractionLayer = self._targetElement.querySelector('.introjs-disableInteraction');
  1307.     if (disableInteractionLayer) {
  1308.       disableInteractionLayer.parentNode.removeChild(disableInteractionLayer);
  1309.     }
  1310.  
  1311.     //disable interaction
  1312.     if (targetElement.disableInteraction) {
  1313.       _disableInteraction.call(self);
  1314.     }
  1315.  
  1316.     // when it's the first step of tour
  1317.     if (this._currentStep === 0 && this._introItems.length > 1) {
  1318.       if (typeof skipTooltipButton !== "undefined" && skipTooltipButton !== null) {
  1319.         skipTooltipButton.className = this._options.buttonClass + ' introjs-skipbutton';
  1320.       }
  1321.       if (typeof nextTooltipButton !== "undefined" && nextTooltipButton !== null) {
  1322.         nextTooltipButton.className = this._options.buttonClass + ' introjs-nextbutton';
  1323.       }
  1324.  
  1325.       if (this._options.hidePrev === true) {
  1326.         if (typeof prevTooltipButton !== "undefined" && prevTooltipButton !== null) {
  1327.           prevTooltipButton.className = this._options.buttonClass + ' introjs-prevbutton introjs-hidden';
  1328.         }
  1329.         if (typeof nextTooltipButton !== "undefined" && nextTooltipButton !== null) {
  1330.           _addClass(nextTooltipButton, 'introjs-fullbutton');
  1331.         }
  1332.       } else {
  1333.         if (typeof prevTooltipButton !== "undefined" && prevTooltipButton !== null) {
  1334.           prevTooltipButton.className = this._options.buttonClass + ' introjs-prevbutton introjs-disabled';
  1335.         }
  1336.       }
  1337.  
  1338.       if (typeof skipTooltipButton !== "undefined" && skipTooltipButton !== null) {
  1339.         skipTooltipButton.innerHTML = this._options.skipLabel;
  1340.       }
  1341.     } else if (this._introItems.length - 1 === this._currentStep || this._introItems.length === 1) {
  1342.       // last step of tour
  1343.       if (typeof skipTooltipButton !== "undefined" && skipTooltipButton !== null) {
  1344.         skipTooltipButton.innerHTML = this._options.doneLabel;
  1345.         // adding donebutton class in addition to skipbutton
  1346.         _addClass(skipTooltipButton, 'introjs-donebutton');
  1347.       }
  1348.       if (typeof prevTooltipButton !== "undefined" && prevTooltipButton !== null) {
  1349.         prevTooltipButton.className = this._options.buttonClass + ' introjs-prevbutton';
  1350.       }
  1351.  
  1352.       if (this._options.hideNext === true) {
  1353.         if (typeof nextTooltipButton !== "undefined" && nextTooltipButton !== null) {
  1354.           nextTooltipButton.className = this._options.buttonClass + ' introjs-nextbutton introjs-hidden';
  1355.         }
  1356.         if (typeof prevTooltipButton !== "undefined" && prevTooltipButton !== null) {
  1357.           _addClass(prevTooltipButton, 'introjs-fullbutton');
  1358.         }
  1359.       } else {
  1360.         if (typeof nextTooltipButton !== "undefined" && nextTooltipButton !== null) {
  1361.           nextTooltipButton.className = this._options.buttonClass + ' introjs-nextbutton introjs-disabled';
  1362.         }
  1363.       }
  1364.     } else {
  1365.       // steps between start and end
  1366.       if (typeof skipTooltipButton !== "undefined" && skipTooltipButton !== null) {
  1367.         skipTooltipButton.className = this._options.buttonClass + ' introjs-skipbutton';
  1368.       }
  1369.       if (typeof prevTooltipButton !== "undefined" && prevTooltipButton !== null) {
  1370.         prevTooltipButton.className = this._options.buttonClass + ' introjs-prevbutton';
  1371.       }
  1372.       if (typeof nextTooltipButton !== "undefined" && nextTooltipButton !== null) {
  1373.         nextTooltipButton.className = this._options.buttonClass + ' introjs-nextbutton';
  1374.       }
  1375.       if (typeof skipTooltipButton !== "undefined" && skipTooltipButton !== null) {
  1376.         skipTooltipButton.innerHTML = this._options.skipLabel;
  1377.       }
  1378.     }
  1379.  
  1380.     prevTooltipButton.setAttribute('role', 'button');
  1381.     nextTooltipButton.setAttribute('role', 'button');
  1382.     skipTooltipButton.setAttribute('role', 'button');
  1383.  
  1384.     //Set focus on "next" button, so that hitting Enter always moves you onto the next step
  1385.     if (typeof nextTooltipButton !== "undefined" && nextTooltipButton !== null) {
  1386.       nextTooltipButton.focus();
  1387.     }
  1388.  
  1389.     _setShowElement(targetElement);
  1390.  
  1391.     if (typeof (this._introAfterChangeCallback) !== 'undefined') {
  1392.       this._introAfterChangeCallback.call(this, targetElement.element);
  1393.     }
  1394.   }
  1395.  
  1396.   /**
  1397.    * To change the scroll of `window` after highlighting an element
  1398.    *
  1399.    * @api private
  1400.    * @method _scrollTo
  1401.    * @param {String} scrollTo
  1402.    * @param {Object} targetElement
  1403.    * @param {Object} tooltipLayer
  1404.    */
  1405.   function _scrollTo(scrollTo, targetElement, tooltipLayer) {
  1406.     if (scrollTo === 'off') return;  
  1407.     var rect;
  1408.  
  1409.     if (!this._options.scrollToElement) return;
  1410.  
  1411.     if (scrollTo === 'tooltip') {
  1412.       rect = tooltipLayer.getBoundingClientRect();
  1413.     } else {
  1414.       rect = targetElement.element.getBoundingClientRect();
  1415.     }
  1416.  
  1417.     if (!_elementInViewport(targetElement.element)) {
  1418.       var winHeight = _getWinSize().height;
  1419.       var top = rect.bottom - (rect.bottom - rect.top);
  1420.  
  1421.       // TODO (afshinm): do we need scroll padding now?
  1422.       // I have changed the scroll option and now it scrolls the window to
  1423.       // the center of the target element or tooltip.
  1424.  
  1425.       if (top < 0 || targetElement.element.clientHeight > winHeight) {
  1426.         window.scrollBy(0, rect.top - ((winHeight / 2) -  (rect.height / 2)) - this._options.scrollPadding); // 30px padding from edge to look nice
  1427.  
  1428.       //Scroll down
  1429.       } else {
  1430.         window.scrollBy(0, rect.top - ((winHeight / 2) -  (rect.height / 2)) + this._options.scrollPadding); // 30px padding from edge to look nice
  1431.       }
  1432.     }
  1433.   }
  1434.  
  1435.   /**
  1436.    * To remove all show element(s)
  1437.    *
  1438.    * @api private
  1439.    * @method _removeShowElement
  1440.    */
  1441.   function _removeShowElement() {
  1442.     var elms = document.querySelectorAll('.introjs-showElement');
  1443.  
  1444.     _forEach(elms, function (elm) {
  1445.       _removeClass(elm, /introjs-[a-zA-Z]+/g);
  1446.     });
  1447.   }
  1448.  
  1449.   /**
  1450.    * To set the show element
  1451.    * This function set a relative (in most cases) position and changes the z-index
  1452.    *
  1453.    * @api private
  1454.    * @method _setShowElement
  1455.    * @param {Object} targetElement
  1456.    */
  1457.   function _setShowElement(targetElement) {
  1458.     var parentElm;
  1459.     // we need to add this show element class to the parent of SVG elements
  1460.     // because the SVG elements can't have independent z-index
  1461.     if (targetElement.element instanceof SVGElement) {
  1462.       parentElm = targetElement.element.parentNode;
  1463.  
  1464.       while (targetElement.element.parentNode !== null) {
  1465.         if (!parentElm.tagName || parentElm.tagName.toLowerCase() === 'body') break;
  1466.  
  1467.         if (parentElm.tagName.toLowerCase() === 'svg') {
  1468.           _addClass(parentElm, 'introjs-showElement introjs-relativePosition');
  1469.         }
  1470.  
  1471.         parentElm = parentElm.parentNode;
  1472.       }
  1473.     }
  1474.  
  1475.     _addClass(targetElement.element, 'introjs-showElement');
  1476.  
  1477.     var currentElementPosition = _getPropValue(targetElement.element, 'position');
  1478.     if (currentElementPosition !== 'absolute' &&
  1479.         currentElementPosition !== 'relative' &&
  1480.         currentElementPosition !== 'fixed') {
  1481.       //change to new intro item
  1482.       _addClass(targetElement.element, 'introjs-relativePosition');
  1483.     }
  1484.  
  1485.     parentElm = targetElement.element.parentNode;
  1486.     while (parentElm !== null) {
  1487.       if (!parentElm.tagName || parentElm.tagName.toLowerCase() === 'body') break;
  1488.  
  1489.       //fix The Stacking Context problem.
  1490.       //More detail: https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Understanding_z_index/The_stacking_context
  1491.       var zIndex = _getPropValue(parentElm, 'z-index');
  1492.       var opacity = parseFloat(_getPropValue(parentElm, 'opacity'));
  1493.       var transform = _getPropValue(parentElm, 'transform') || _getPropValue(parentElm, '-webkit-transform') || _getPropValue(parentElm, '-moz-transform') || _getPropValue(parentElm, '-ms-transform') || _getPropValue(parentElm, '-o-transform');
  1494.       if (/[0-9]+/.test(zIndex) || opacity < 1 || (transform !== 'none' && transform !== undefined)) {
  1495.         _addClass(parentElm, 'introjs-fixParent');
  1496.       }
  1497.  
  1498.       parentElm = parentElm.parentNode;
  1499.     }
  1500.   }
  1501.  
  1502.   /**
  1503.   * Iterates arrays
  1504.   *
  1505.   * @param {Array} arr
  1506.   * @param {Function} forEachFnc
  1507.   * @param {Function} completeFnc
  1508.   * @return {Null}
  1509.   */
  1510.   function _forEach(arr, forEachFnc, completeFnc) {
  1511.     // in case arr is an empty query selector node list
  1512.     if (arr) {
  1513.       for (var i = 0, len = arr.length; i < len; i++) {
  1514.         forEachFnc(arr[i], i);
  1515.       }
  1516.     }
  1517.  
  1518.     if (typeof(completeFnc) === 'function') {
  1519.       completeFnc();
  1520.     }
  1521.   }
  1522.  
  1523.   /**
  1524.   * Mark any object with an incrementing number
  1525.   * used for keeping track of objects
  1526.   *
  1527.   * @param Object obj   Any object or DOM Element
  1528.   * @param String key
  1529.   * @return Object
  1530.   */
  1531.   var _stamp = (function () {
  1532.     var keys = {};
  1533.     return function stamp (obj, key) {
  1534.      
  1535.       // get group key
  1536.       key = key || 'introjs-stamp';
  1537.  
  1538.       // each group increments from 0
  1539.       keys[key] = keys[key] || 0;
  1540.  
  1541.       // stamp only once per object
  1542.       if (obj[key] === undefined) {
  1543.         // increment key for each new object
  1544.         obj[key] = keys[key]++;
  1545.       }
  1546.  
  1547.       return obj[key];
  1548.     };
  1549.   })();
  1550.  
  1551.   /**
  1552.   * DOMEvent Handles all DOM events
  1553.   *
  1554.   * methods:
  1555.   *
  1556.   * on - add event handler
  1557.   * off - remove event
  1558.   */
  1559.   var DOMEvent = (function () {
  1560.     function DOMEvent () {
  1561.       var events_key = 'introjs_event';
  1562.      
  1563.       /**
  1564.       * Gets a unique ID for an event listener
  1565.       *
  1566.       * @param Object obj
  1567.       * @param String type        event type
  1568.       * @param Function listener
  1569.       * @param Object context
  1570.       * @return String
  1571.       */
  1572.       this._id = function (obj, type, listener, context) {
  1573.         return type + _stamp(listener) + (context ? '_' + _stamp(context) : '');
  1574.       };
  1575.  
  1576.       /**
  1577.       * Adds event listener
  1578.       *
  1579.       * @param Object obj
  1580.       * @param String type        event type
  1581.       * @param Function listener
  1582.       * @param Object context
  1583.       * @param Boolean useCapture
  1584.       * @return null
  1585.       */
  1586.       this.on = function (obj, type, listener, context, useCapture) {
  1587.         var id = this._id.apply(this, arguments),
  1588.             handler = function (e) {
  1589.               return listener.call(context || obj, e || window.event);
  1590.             };
  1591.  
  1592.         if ('addEventListener' in obj) {
  1593.           obj.addEventListener(type, handler, useCapture);
  1594.         } else if ('attachEvent' in obj) {
  1595.           obj.attachEvent('on' + type, handler);
  1596.         }
  1597.  
  1598.         obj[events_key] = obj[events_key] || {};
  1599.         obj[events_key][id] = handler;
  1600.       };
  1601.  
  1602.       /**
  1603.       * Removes event listener
  1604.       *
  1605.       * @param Object obj
  1606.       * @param String type        event type
  1607.       * @param Function listener
  1608.       * @param Object context
  1609.       * @param Boolean useCapture
  1610.       * @return null
  1611.       */
  1612.       this.off = function (obj, type, listener, context, useCapture) {
  1613.         var id = this._id.apply(this, arguments),
  1614.             handler = obj[events_key] && obj[events_key][id];
  1615.  
  1616.         if (!handler) {
  1617.           return;
  1618.         }
  1619.  
  1620.         if ('removeEventListener' in obj) {
  1621.           obj.removeEventListener(type, handler, useCapture);
  1622.         } else if ('detachEvent' in obj) {
  1623.           obj.detachEvent('on' + type, handler);
  1624.         }
  1625.  
  1626.         obj[events_key][id] = null;
  1627.       };
  1628.     }
  1629.  
  1630.     return new DOMEvent();
  1631.   })();
  1632.  
  1633.   /**
  1634.    * Append a class to an element
  1635.    *
  1636.    * @api private
  1637.    * @method _addClass
  1638.    * @param {Object} element
  1639.    * @param {String} className
  1640.    * @returns null
  1641.    */
  1642.   function _addClass(element, className) {
  1643.     if (element instanceof SVGElement) {
  1644.       // svg
  1645.       var pre = element.getAttribute('class') || '';
  1646.  
  1647.       element.setAttribute('class', pre + ' ' + className);
  1648.     } else {
  1649.       if (element.classList !== undefined) {
  1650.         // check for modern classList property
  1651.         var classes = className.split(' ');
  1652.         _forEach(classes, function (cls) {
  1653.           element.classList.add( cls );
  1654.         });
  1655.       } else if (!element.className.match( className )) {
  1656.         // check if element doesn't already have className
  1657.         element.className += ' ' + className;
  1658.       }
  1659.     }
  1660.   }
  1661.  
  1662.   /**
  1663.    * Remove a class from an element
  1664.    *
  1665.    * @api private
  1666.    * @method _removeClass
  1667.    * @param {Object} element
  1668.    * @param {RegExp|String} classNameRegex can be regex or string
  1669.    * @returns null
  1670.    */
  1671.   function _removeClass(element, classNameRegex) {
  1672.     if (element instanceof SVGElement) {
  1673.       var pre = element.getAttribute('class') || '';
  1674.  
  1675.       element.setAttribute('class', pre.replace(classNameRegex, '').replace(/^\s+|\s+$/g, ''));
  1676.     } else {
  1677.       element.className = element.className.replace(classNameRegex, '').replace(/^\s+|\s+$/g, '');
  1678.     }
  1679.   }
  1680.  
  1681.   /**
  1682.    * Get an element CSS property on the page
  1683.    * Thanks to JavaScript Kit: http://www.javascriptkit.com/dhtmltutors/dhtmlcascade4.shtml
  1684.    *
  1685.    * @api private
  1686.    * @method _getPropValue
  1687.    * @param {Object} element
  1688.    * @param {String} propName
  1689.    * @returns Element's property value
  1690.    */
  1691.   function _getPropValue (element, propName) {
  1692.     var propValue = '';
  1693.     if (element.currentStyle) { //IE
  1694.       propValue = element.currentStyle[propName];
  1695.     } else if (document.defaultView && document.defaultView.getComputedStyle) { //Others
  1696.       propValue = document.defaultView.getComputedStyle(element, null).getPropertyValue(propName);
  1697.     }
  1698.  
  1699.     //Prevent exception in IE
  1700.     if (propValue && propValue.toLowerCase) {
  1701.       return propValue.toLowerCase();
  1702.     } else {
  1703.       return propValue;
  1704.     }
  1705.   }
  1706.  
  1707.   /**
  1708.    * Checks to see if target element (or parents) position is fixed or not
  1709.    *
  1710.    * @api private
  1711.    * @method _isFixed
  1712.    * @param {Object} element
  1713.    * @returns Boolean
  1714.    */
  1715.   function _isFixed (element) {
  1716.     var p = element.parentNode;
  1717.  
  1718.     if (!p || p.nodeName === 'HTML') {
  1719.       return false;
  1720.     }
  1721.  
  1722.     if (_getPropValue(element, 'position') === 'fixed') {
  1723.       return true;
  1724.     }
  1725.  
  1726.     return _isFixed(p);
  1727.   }
  1728.  
  1729.   /**
  1730.    * Provides a cross-browser way to get the screen dimensions
  1731.    * via: http://stackoverflow.com/questions/5864467/internet-explorer-innerheight
  1732.    *
  1733.    * @api private
  1734.    * @method _getWinSize
  1735.    * @returns {Object} width and height attributes
  1736.    */
  1737.   function _getWinSize() {
  1738.     if (window.innerWidth !== undefined) {
  1739.       return { width: window.innerWidth, height: window.innerHeight };
  1740.     } else {
  1741.       var D = document.documentElement;
  1742.       return { width: D.clientWidth, height: D.clientHeight };
  1743.     }
  1744.   }
  1745.  
  1746.   /**
  1747.    * Check to see if the element is in the viewport or not
  1748.    * http://stackoverflow.com/questions/123999/how-to-tell-if-a-dom-element-is-visible-in-the-current-viewport
  1749.    *
  1750.    * @api private
  1751.    * @method _elementInViewport
  1752.    * @param {Object} el
  1753.    */
  1754.   function _elementInViewport(el) {
  1755.     var rect = el.getBoundingClientRect();
  1756.  
  1757.     return (
  1758.       rect.top >= 0 &&
  1759.       rect.left >= 0 &&
  1760.       (rect.bottom+80) <= window.innerHeight && // add 80 to get the text right
  1761.       rect.right <= window.innerWidth
  1762.     );
  1763.   }
  1764.  
  1765.   /**
  1766.    * Add overlay layer to the page
  1767.    *
  1768.    * @api private
  1769.    * @method _addOverlayLayer
  1770.    * @param {Object} targetElm
  1771.    */
  1772.   function _addOverlayLayer(targetElm) {
  1773.     var overlayLayer = document.createElement('div'),
  1774.         styleText = '',
  1775.         self = this;
  1776.  
  1777.     //set css class name
  1778.     overlayLayer.className = 'introjs-overlay';
  1779.  
  1780.     //check if the target element is body, we should calculate the size of overlay layer in a better way
  1781.     if (!targetElm.tagName || targetElm.tagName.toLowerCase() === 'body') {
  1782.       styleText += 'top: 0;bottom: 0; left: 0;right: 0;position: fixed;';
  1783.       overlayLayer.style.cssText = styleText;
  1784.     } else {
  1785.       //set overlay layer position
  1786.       var elementPosition = _getOffset(targetElm);
  1787.       if (elementPosition) {
  1788.         styleText += 'width: ' + elementPosition.width + 'px; height:' + elementPosition.height + 'px; top:' + elementPosition.top + 'px;left: ' + elementPosition.left + 'px;';
  1789.         overlayLayer.style.cssText = styleText;
  1790.       }
  1791.     }
  1792.  
  1793.     targetElm.appendChild(overlayLayer);
  1794.  
  1795.     overlayLayer.onclick = function() {
  1796.       if (self._options.exitOnOverlayClick === true) {
  1797.         _exitIntro.call(self, targetElm);
  1798.       }
  1799.     };
  1800.  
  1801.     window.setTimeout(function() {
  1802.       styleText += 'opacity: ' + self._options.overlayOpacity.toString() + ';';
  1803.       overlayLayer.style.cssText = styleText;
  1804.     }, 10);
  1805.  
  1806.     return true;
  1807.   }
  1808.  
  1809.   /**
  1810.    * Removes open hint (tooltip hint)
  1811.    *
  1812.    * @api private
  1813.    * @method _removeHintTooltip
  1814.    */
  1815.   function _removeHintTooltip() {
  1816.     var tooltip = document.querySelector('.introjs-hintReference');
  1817.  
  1818.     if (tooltip) {
  1819.       var step = tooltip.getAttribute('data-step');
  1820.       tooltip.parentNode.removeChild(tooltip);
  1821.       return step;
  1822.     }
  1823.   }
  1824.  
  1825.   /**
  1826.    * Start parsing hint items
  1827.    *
  1828.    * @api private
  1829.    * @param {Object} targetElm
  1830.    * @method _startHint
  1831.    */
  1832.   function _populateHints(targetElm) {
  1833.  
  1834.     this._introItems = [];
  1835.  
  1836.     if (this._options.hints) {
  1837.       _forEach(this._options.hints, function (hint) {
  1838.         var currentItem = _cloneObject(hint);
  1839.  
  1840.         if (typeof(currentItem.element) === 'string') {
  1841.           //grab the element with given selector from the page
  1842.           currentItem.element = document.querySelector(currentItem.element);
  1843.         }
  1844.  
  1845.         currentItem.hintPosition = currentItem.hintPosition || this._options.hintPosition;
  1846.         currentItem.hintAnimation = currentItem.hintAnimation || this._options.hintAnimation;
  1847.  
  1848.         if (currentItem.element !== null) {
  1849.           this._introItems.push(currentItem);
  1850.         }
  1851.       }.bind(this));
  1852.     } else {
  1853.       var hints = targetElm.querySelectorAll('*[data-hint]');
  1854.  
  1855.       if (!hints || !hints.length) {
  1856.         return false;
  1857.       }
  1858.  
  1859.       //first add intro items with data-step
  1860.       _forEach(hints, function (currentElement) {
  1861.         // hint animation
  1862.         var hintAnimation = currentElement.getAttribute('data-hintanimation');
  1863.  
  1864.         if (hintAnimation) {
  1865.           hintAnimation = (hintAnimation === 'true');
  1866.         } else {
  1867.           hintAnimation = this._options.hintAnimation;
  1868.         }
  1869.  
  1870.         this._introItems.push({
  1871.           element: currentElement,
  1872.           hint: currentElement.getAttribute('data-hint'),
  1873.           hintPosition: currentElement.getAttribute('data-hintposition') || this._options.hintPosition,
  1874.           hintAnimation: hintAnimation,
  1875.           tooltipClass: currentElement.getAttribute('data-tooltipclass'),
  1876.           position: currentElement.getAttribute('data-position') || this._options.tooltipPosition
  1877.         });
  1878.       }.bind(this));
  1879.     }
  1880.  
  1881.     _addHints.call(this);
  1882.  
  1883.     /*
  1884.     todo:
  1885.     these events should be removed at some point
  1886.     */
  1887.     DOMEvent.on(document, 'click', _removeHintTooltip, this, false);
  1888.     DOMEvent.on(window, 'resize', _reAlignHints, this, true);
  1889.   }
  1890.  
  1891.   /**
  1892.    * Re-aligns all hint elements
  1893.    *
  1894.    * @api private
  1895.    * @method _reAlignHints
  1896.    */
  1897.   function _reAlignHints() {
  1898.     _forEach(this._introItems, function (item) {
  1899.       if (typeof(item.targetElement) === 'undefined') {
  1900.         return;
  1901.       }
  1902.  
  1903.       _alignHintPosition.call(this, item.hintPosition, item.element, item.targetElement);
  1904.     }.bind(this));
  1905.   }
  1906.  
  1907.   /**
  1908.   * Get a queryselector within the hint wrapper
  1909.   *
  1910.   * @param {String} selector
  1911.   * @return {NodeList|Array}
  1912.   */
  1913.   function _hintQuerySelectorAll(selector) {
  1914.     var hintsWrapper = document.querySelector('.introjs-hints');
  1915.     return (hintsWrapper) ? hintsWrapper.querySelectorAll(selector) : [];
  1916.   }
  1917.  
  1918.   /**
  1919.    * Hide a hint
  1920.    *
  1921.    * @api private
  1922.    * @method _hideHint
  1923.    */
  1924.   function _hideHint(stepId) {
  1925.     var hint = _hintQuerySelectorAll('.introjs-hint[data-step="' + stepId + '"]')[0];
  1926.    
  1927.     _removeHintTooltip.call(this);
  1928.  
  1929.     if (hint) {
  1930.       _addClass(hint, 'introjs-hidehint');
  1931.     }
  1932.  
  1933.     // call the callback function (if any)
  1934.     if (typeof (this._hintCloseCallback) !== 'undefined') {
  1935.       this._hintCloseCallback.call(this, stepId);
  1936.     }
  1937.   }
  1938.  
  1939.   /**
  1940.    * Hide all hints
  1941.    *
  1942.    * @api private
  1943.    * @method _hideHints
  1944.    */
  1945.   function _hideHints() {
  1946.     var hints = _hintQuerySelectorAll('.introjs-hint');
  1947.  
  1948.     _forEach(hints, function (hint) {
  1949.       _hideHint.call(this, hint.getAttribute('data-step'));
  1950.     }.bind(this));
  1951.   }
  1952.  
  1953.   /**
  1954.    * Show all hints
  1955.    *
  1956.    * @api private
  1957.    * @method _showHints
  1958.    */
  1959.   function _showHints() {
  1960.     var hints = _hintQuerySelectorAll('.introjs-hint');
  1961.  
  1962.     if (hints && hints.length) {
  1963.       _forEach(hints, function (hint) {
  1964.         _showHint.call(this, hint.getAttribute('data-step'));
  1965.       }.bind(this));
  1966.     } else {
  1967.       _populateHints.call(this, this._targetElement);
  1968.     }
  1969.   }
  1970.  
  1971.   /**
  1972.    * Show a hint
  1973.    *
  1974.    * @api private
  1975.    * @method _showHint
  1976.    */
  1977.   function _showHint(stepId) {
  1978.     var hint = _hintQuerySelectorAll('.introjs-hint[data-step="' + stepId + '"]')[0];
  1979.  
  1980.     if (hint) {
  1981.       _removeClass(hint, /introjs-hidehint/g);
  1982.     }
  1983.   }
  1984.  
  1985.   /**
  1986.    * Removes all hint elements on the page
  1987.    * Useful when you want to destroy the elements and add them again (e.g. a modal or popup)
  1988.    *
  1989.    * @api private
  1990.    * @method _removeHints
  1991.    */
  1992.   function _removeHints() {
  1993.     var hints = _hintQuerySelectorAll('.introjs-hint');
  1994.  
  1995.     _forEach(hints, function (hint) {
  1996.       _removeHint.call(this, hint.getAttribute('data-step'));
  1997.     }.bind(this));
  1998.   }
  1999.  
  2000.   /**
  2001.    * Remove one single hint element from the page
  2002.    * Useful when you want to destroy the element and add them again (e.g. a modal or popup)
  2003.    * Use removeHints if you want to remove all elements.
  2004.    *
  2005.    * @api private
  2006.    * @method _removeHint
  2007.    */
  2008.   function _removeHint(stepId) {
  2009.     var hint = _hintQuerySelectorAll('.introjs-hint[data-step="' + stepId + '"]')[0];
  2010.  
  2011.     if (hint) {
  2012.       hint.parentNode.removeChild(hint);
  2013.     }
  2014.   }
  2015.  
  2016.   /**
  2017.    * Add all available hints to the page
  2018.    *
  2019.    * @api private
  2020.    * @method _addHints
  2021.    */
  2022.   function _addHints() {
  2023.     var self = this;
  2024.  
  2025.     var hintsWrapper = document.querySelector('.introjs-hints');
  2026.  
  2027.     if (hintsWrapper === null) {
  2028.       hintsWrapper = document.createElement('div');
  2029.       hintsWrapper.className = 'introjs-hints';
  2030.     }
  2031.  
  2032.     /**
  2033.     * Returns an event handler unique to the hint iteration
  2034.     *
  2035.     * @param {Integer} i
  2036.     * @return {Function}
  2037.     */
  2038.     var getHintClick = function (i) {
  2039.       return function(e) {
  2040.         var evt = e ? e : window.event;
  2041.        
  2042.         if (evt.stopPropagation) {
  2043.           evt.stopPropagation();
  2044.         }
  2045.  
  2046.         if (evt.cancelBubble !== null) {
  2047.           evt.cancelBubble = true;
  2048.         }
  2049.  
  2050.         _showHintDialog.call(self, i);
  2051.       };
  2052.     };
  2053.  
  2054.     _forEach(this._introItems, function(item, i) {
  2055.       // avoid append a hint twice
  2056.       if (document.querySelector('.introjs-hint[data-step="' + i + '"]')) {
  2057.         return;
  2058.       }
  2059.  
  2060.       var hint = document.createElement('a');
  2061.       _setAnchorAsButton(hint);
  2062.  
  2063.       hint.onclick = getHintClick(i);
  2064.  
  2065.       hint.className = 'introjs-hint';
  2066.  
  2067.       if (!item.hintAnimation) {
  2068.         _addClass(hint, 'introjs-hint-no-anim');
  2069.       }
  2070.  
  2071.       // hint's position should be fixed if the target element's position is fixed
  2072.       if (_isFixed(item.element)) {
  2073.         _addClass(hint, 'introjs-fixedhint');
  2074.       }
  2075.  
  2076.       var hintDot = document.createElement('div');
  2077.       hintDot.className = 'introjs-hint-dot';
  2078.       var hintPulse = document.createElement('div');
  2079.       hintPulse.className = 'introjs-hint-pulse';
  2080.  
  2081.       hint.appendChild(hintDot);
  2082.       hint.appendChild(hintPulse);
  2083.       hint.setAttribute('data-step', i);
  2084.  
  2085.       // we swap the hint element with target element
  2086.       // because _setHelperLayerPosition uses `element` property
  2087.       item.targetElement = item.element;
  2088.       item.element = hint;
  2089.  
  2090.       // align the hint position
  2091.       _alignHintPosition.call(this, item.hintPosition, hint, item.targetElement);
  2092.  
  2093.       hintsWrapper.appendChild(hint);
  2094.     }.bind(this));
  2095.  
  2096.     // adding the hints wrapper
  2097.     document.body.appendChild(hintsWrapper);
  2098.  
  2099.     // call the callback function (if any)
  2100.     if (typeof (this._hintsAddedCallback) !== 'undefined') {
  2101.       this._hintsAddedCallback.call(this);
  2102.     }
  2103.   }
  2104.  
  2105.   /**
  2106.    * Aligns hint position
  2107.    *
  2108.    * @api private
  2109.    * @method _alignHintPosition
  2110.    * @param {String} position
  2111.    * @param {Object} hint
  2112.    * @param {Object} element
  2113.    */
  2114.   function _alignHintPosition(position, hint, element) {
  2115.     // get/calculate offset of target element
  2116.     var offset = _getOffset.call(this, element);
  2117.     var iconWidth = 20;
  2118.     var iconHeight = 20;
  2119.  
  2120.     // align the hint element
  2121.     switch (position) {
  2122.       default:
  2123.       case 'top-left':
  2124.         hint.style.left = offset.left + 'px';
  2125.         hint.style.top = offset.top + 'px';
  2126.         break;
  2127.       case 'top-right':
  2128.         hint.style.left = (offset.left + offset.width - iconWidth) + 'px';
  2129.         hint.style.top = offset.top + 'px';
  2130.         break;
  2131.       case 'bottom-left':
  2132.         hint.style.left = offset.left + 'px';
  2133.         hint.style.top = (offset.top + offset.height - iconHeight) + 'px';
  2134.         break;
  2135.       case 'bottom-right':
  2136.         hint.style.left = (offset.left + offset.width - iconWidth) + 'px';
  2137.         hint.style.top = (offset.top + offset.height - iconHeight) + 'px';
  2138.         break;
  2139.       case 'middle-left':
  2140.         hint.style.left = offset.left + 'px';
  2141.         hint.style.top = (offset.top + (offset.height - iconHeight) / 2) + 'px';
  2142.         break;
  2143.       case 'middle-right':
  2144.         hint.style.left = (offset.left + offset.width - iconWidth) + 'px';
  2145.         hint.style.top = (offset.top + (offset.height - iconHeight) / 2) + 'px';
  2146.         break;
  2147.       case 'middle-middle':
  2148.         hint.style.left = (offset.left + (offset.width - iconWidth) / 2) + 'px';
  2149.         hint.style.top = (offset.top + (offset.height - iconHeight) / 2) + 'px';
  2150.         break;
  2151.       case 'bottom-middle':
  2152.         hint.style.left = (offset.left + (offset.width - iconWidth) / 2) + 'px';
  2153.         hint.style.top = (offset.top + offset.height - iconHeight) + 'px';
  2154.         break;
  2155.       case 'top-middle':
  2156.         hint.style.left = (offset.left + (offset.width - iconWidth) / 2) + 'px';
  2157.         hint.style.top = offset.top + 'px';
  2158.         break;
  2159.     }
  2160.   }
  2161.  
  2162.   /**
  2163.    * Triggers when user clicks on the hint element
  2164.    *
  2165.    * @api private
  2166.    * @method _showHintDialog
  2167.    * @param {Number} stepId
  2168.    */
  2169.   function _showHintDialog(stepId) {
  2170.     var hintElement = document.querySelector('.introjs-hint[data-step="' + stepId + '"]');
  2171.     var item = this._introItems[stepId];
  2172.  
  2173.     // call the callback function (if any)
  2174.     if (typeof (this._hintClickCallback) !== 'undefined') {
  2175.       this._hintClickCallback.call(this, hintElement, item, stepId);
  2176.     }
  2177.  
  2178.     // remove all open tooltips
  2179.     var removedStep = _removeHintTooltip.call(this);
  2180.  
  2181.     // to toggle the tooltip
  2182.     if (parseInt(removedStep, 10) === stepId) {
  2183.       return;
  2184.     }
  2185.  
  2186.     var tooltipLayer = document.createElement('div');
  2187.     var tooltipTextLayer = document.createElement('div');
  2188.     var arrowLayer = document.createElement('div');
  2189.     var referenceLayer = document.createElement('div');
  2190.  
  2191.     tooltipLayer.className = 'introjs-tooltip';
  2192.  
  2193.     tooltipLayer.onclick = function (e) {
  2194.       //IE9 & Other Browsers
  2195.       if (e.stopPropagation) {
  2196.         e.stopPropagation();
  2197.       }
  2198.       //IE8 and Lower
  2199.       else {
  2200.         e.cancelBubble = true;
  2201.       }
  2202.     };
  2203.  
  2204.     tooltipTextLayer.className = 'introjs-tooltiptext';
  2205.  
  2206.     var tooltipWrapper = document.createElement('p');
  2207.     tooltipWrapper.innerHTML = item.hint;
  2208.  
  2209.     var closeButton = document.createElement('a');
  2210.     closeButton.className = this._options.buttonClass;
  2211.     closeButton.setAttribute('role', 'button');
  2212.     closeButton.innerHTML = this._options.hintButtonLabel;
  2213.     closeButton.onclick = _hideHint.bind(this, stepId);
  2214.  
  2215.     tooltipTextLayer.appendChild(tooltipWrapper);
  2216.     tooltipTextLayer.appendChild(closeButton);
  2217.  
  2218.     arrowLayer.className = 'introjs-arrow';
  2219.     tooltipLayer.appendChild(arrowLayer);
  2220.  
  2221.     tooltipLayer.appendChild(tooltipTextLayer);
  2222.  
  2223.     // set current step for _placeTooltip function
  2224.     this._currentStep = hintElement.getAttribute('data-step');
  2225.  
  2226.     // align reference layer position
  2227.     referenceLayer.className = 'introjs-tooltipReferenceLayer introjs-hintReference';
  2228.     referenceLayer.setAttribute('data-step', hintElement.getAttribute('data-step'));
  2229.     _setHelperLayerPosition.call(this, referenceLayer);
  2230.  
  2231.     referenceLayer.appendChild(tooltipLayer);
  2232.     document.body.appendChild(referenceLayer);
  2233.  
  2234.     //set proper position
  2235.     _placeTooltip.call(this, hintElement, tooltipLayer, arrowLayer, null, true);
  2236.   }
  2237.  
  2238.   /**
  2239.    * Get an element position on the page
  2240.    * Thanks to `meouw`: http://stackoverflow.com/a/442474/375966
  2241.    *
  2242.    * @api private
  2243.    * @method _getOffset
  2244.    * @param {Object} element
  2245.    * @returns Element's position info
  2246.    */
  2247.   function _getOffset(element) {
  2248.     var body = document.body;
  2249.     var docEl = document.documentElement;
  2250.     var scrollTop = window.pageYOffset || docEl.scrollTop || body.scrollTop;
  2251.     var scrollLeft = window.pageXOffset || docEl.scrollLeft || body.scrollLeft;
  2252.     var x = element.getBoundingClientRect();
  2253.     return {
  2254.       top: x.top + scrollTop,
  2255.       width: x.width,
  2256.       height: x.height,
  2257.       left: x.left + scrollLeft
  2258.     };
  2259.   }
  2260.  
  2261.   /**
  2262.   * Find the nearest scrollable parent
  2263.   * copied from https://stackoverflow.com/questions/35939886/find-first-scrollable-parent
  2264.   *
  2265.   * @param Element element
  2266.   * @return Element
  2267.   */
  2268.   function _getScrollParent(element) {
  2269.     var style = window.getComputedStyle(element);
  2270.     var excludeStaticParent = (style.position === "absolute");
  2271.     var overflowRegex = /(auto|scroll)/;
  2272.  
  2273.     if (style.position === "fixed") return document.body;
  2274.    
  2275.     for (var parent = element; (parent = parent.parentElement);) {
  2276.       style = window.getComputedStyle(parent);
  2277.       if (excludeStaticParent && style.position === "static") {
  2278.         continue;
  2279.       }
  2280.       if (overflowRegex.test(style.overflow + style.overflowY + style.overflowX)) return parent;
  2281.     }
  2282.  
  2283.     return document.body;
  2284.   }
  2285.  
  2286.   /**
  2287.   * scroll a scrollable element to a child element
  2288.   *
  2289.   * @param Element parent
  2290.   * @param Element element
  2291.   * @return Null
  2292.   */
  2293.   function _scrollParentToElement (parent, element) {
  2294.     parent.scrollTop = element.offsetTop - parent.offsetTop;
  2295.   }
  2296.  
  2297.   /**
  2298.    * Gets the current progress percentage
  2299.    *
  2300.    * @api private
  2301.    * @method _getProgress
  2302.    * @returns current progress percentage
  2303.    */
  2304.   function _getProgress() {
  2305.     // Steps are 0 indexed
  2306.     var currentStep = parseInt((this._currentStep + 1), 10);
  2307.     return ((currentStep / this._introItems.length) * 100);
  2308.   }
  2309.  
  2310.   /**
  2311.    * Overwrites obj1's values with obj2's and adds obj2's if non existent in obj1
  2312.    * via: http://stackoverflow.com/questions/171251/how-can-i-merge-properties-of-two-javascript-objects-dynamically
  2313.    *
  2314.    * @param obj1
  2315.    * @param obj2
  2316.    * @returns obj3 a new object based on obj1 and obj2
  2317.    */
  2318.   function _mergeOptions(obj1,obj2) {
  2319.     var obj3 = {},
  2320.       attrname;
  2321.     for (attrname in obj1) { obj3[attrname] = obj1[attrname]; }
  2322.     for (attrname in obj2) { obj3[attrname] = obj2[attrname]; }
  2323.     return obj3;
  2324.   }
  2325.  
  2326.   var introJs = function (targetElm) {
  2327.     var instance;
  2328.  
  2329.     if (typeof (targetElm) === 'object') {
  2330.       //Ok, create a new instance
  2331.       instance = new IntroJs(targetElm);
  2332.  
  2333.     } else if (typeof (targetElm) === 'string') {
  2334.       //select the target element with query selector
  2335.       var targetElement = document.querySelector(targetElm);
  2336.  
  2337.       if (targetElement) {
  2338.         instance = new IntroJs(targetElement);
  2339.       } else {
  2340.         throw new Error('There is no element with given selector.');
  2341.       }
  2342.     } else {
  2343.       instance = new IntroJs(document.body);
  2344.     }
  2345.     // add instance to list of _instances
  2346.     // passing group to _stamp to increment
  2347.     // from 0 onward somewhat reliably
  2348.     introJs.instances[ _stamp(instance, 'introjs-instance') ] = instance;
  2349.  
  2350.     return instance;
  2351.   };
  2352.  
  2353.   /**
  2354.    * Current IntroJs version
  2355.    *
  2356.    * @property version
  2357.    * @type String
  2358.    */
  2359.   introJs.version = VERSION;
  2360.  
  2361.   /**
  2362.   * key-val object helper for introJs instances
  2363.   *
  2364.   * @property instances
  2365.   * @type Object
  2366.   */
  2367.   introJs.instances = {};
  2368.  
  2369.   //Prototype
  2370.   introJs.fn = IntroJs.prototype = {
  2371.     clone: function () {
  2372.       return new IntroJs(this);
  2373.     },
  2374.     setOption: function(option, value) {
  2375.       this._options[option] = value;
  2376.       return this;
  2377.     },
  2378.     setOptions: function(options) {
  2379.       this._options = _mergeOptions(this._options, options);
  2380.       return this;
  2381.     },
  2382.     start: function (group) {
  2383.       _introForElement.call(this, this._targetElement, group);
  2384.       return this;
  2385.     },
  2386.     goToStep: function(step) {
  2387.       _goToStep.call(this, step);
  2388.       return this;
  2389.     },
  2390.     addStep: function(options) {
  2391.       if (!this._options.steps) {
  2392.         this._options.steps = [];
  2393.       }
  2394.  
  2395.       this._options.steps.push(options);
  2396.  
  2397.       return this;
  2398.     },
  2399.     addSteps: function(steps) {
  2400.       if (!steps.length) return;
  2401.  
  2402.       for(var index = 0; index < steps.length; index++) {
  2403.         this.addStep(steps[index]);
  2404.       }
  2405.  
  2406.       return this;
  2407.     },
  2408.     goToStepNumber: function(step) {
  2409.       _goToStepNumber.call(this, step);
  2410.  
  2411.       return this;
  2412.     },
  2413.     nextStep: function() {
  2414.       _nextStep.call(this);
  2415.       return this;
  2416.     },
  2417.     previousStep: function() {
  2418.       _previousStep.call(this);
  2419.       return this;
  2420.     },
  2421.     exit: function(force) {
  2422.       _exitIntro.call(this, this._targetElement, force);
  2423.       return this;
  2424.     },
  2425.     refresh: function() {
  2426.       _refresh.call(this);
  2427.       return this;
  2428.     },
  2429.     onbeforechange: function(providedCallback) {
  2430.       if (typeof (providedCallback) === 'function') {
  2431.         this._introBeforeChangeCallback = providedCallback;
  2432.       } else {
  2433.         throw new Error('Provided callback for onbeforechange was not a function');
  2434.       }
  2435.       return this;
  2436.     },
  2437.     onchange: function(providedCallback) {
  2438.       if (typeof (providedCallback) === 'function') {
  2439.         this._introChangeCallback = providedCallback;
  2440.       } else {
  2441.         throw new Error('Provided callback for onchange was not a function.');
  2442.       }
  2443.       return this;
  2444.     },
  2445.     onafterchange: function(providedCallback) {
  2446.       if (typeof (providedCallback) === 'function') {
  2447.         this._introAfterChangeCallback = providedCallback;
  2448.       } else {
  2449.         throw new Error('Provided callback for onafterchange was not a function');
  2450.       }
  2451.       return this;
  2452.     },
  2453.     oncomplete: function(providedCallback) {
  2454.       if (typeof (providedCallback) === 'function') {
  2455.         this._introCompleteCallback = providedCallback;
  2456.       } else {
  2457.         throw new Error('Provided callback for oncomplete was not a function.');
  2458.       }
  2459.       return this;
  2460.     },
  2461.     onhintsadded: function(providedCallback) {
  2462.       if (typeof (providedCallback) === 'function') {
  2463.         this._hintsAddedCallback = providedCallback;
  2464.       } else {
  2465.         throw new Error('Provided callback for onhintsadded was not a function.');
  2466.       }
  2467.       return this;
  2468.     },
  2469.     onhintclick: function(providedCallback) {
  2470.       if (typeof (providedCallback) === 'function') {
  2471.         this._hintClickCallback = providedCallback;
  2472.       } else {
  2473.         throw new Error('Provided callback for onhintclick was not a function.');
  2474.       }
  2475.       return this;
  2476.     },
  2477.     onhintclose: function(providedCallback) {
  2478.       if (typeof (providedCallback) === 'function') {
  2479.         this._hintCloseCallback = providedCallback;
  2480.       } else {
  2481.         throw new Error('Provided callback for onhintclose was not a function.');
  2482.       }
  2483.       return this;
  2484.     },
  2485.     onexit: function(providedCallback) {
  2486.       if (typeof (providedCallback) === 'function') {
  2487.         this._introExitCallback = providedCallback;
  2488.       } else {
  2489.         throw new Error('Provided callback for onexit was not a function.');
  2490.       }
  2491.       return this;
  2492.     },
  2493.     onskip: function(providedCallback) {
  2494.       if (typeof (providedCallback) === 'function') {
  2495.         this._introSkipCallback = providedCallback;
  2496.       } else {
  2497.         throw new Error('Provided callback for onskip was not a function.');
  2498.       }
  2499.       return this;
  2500.     },
  2501.     onbeforeexit: function(providedCallback) {
  2502.       if (typeof (providedCallback) === 'function') {
  2503.         this._introBeforeExitCallback = providedCallback;
  2504.       } else {
  2505.         throw new Error('Provided callback for onbeforeexit was not a function.');
  2506.       }
  2507.       return this;
  2508.     },
  2509.     addHints: function() {
  2510.       _populateHints.call(this, this._targetElement);
  2511.       return this;
  2512.     },
  2513.     hideHint: function (stepId) {
  2514.       _hideHint.call(this, stepId);
  2515.       return this;
  2516.     },
  2517.     hideHints: function () {
  2518.       _hideHints.call(this);
  2519.       return this;
  2520.     },
  2521.     showHint: function (stepId) {
  2522.       _showHint.call(this, stepId);
  2523.       return this;
  2524.     },
  2525.     showHints: function () {
  2526.       _showHints.call(this);
  2527.       return this;
  2528.     },
  2529.     removeHints: function () {
  2530.       _removeHints.call(this);
  2531.       return this;
  2532.     },
  2533.     removeHint: function (stepId) {
  2534.       _removeHint.call(this, stepId);
  2535.       return this;
  2536.     },
  2537.     showHintDialog: function (stepId) {
  2538.       _showHintDialog.call(this, stepId);
  2539.       return this;
  2540.     }
  2541.   };
  2542.  
  2543.   return introJs;
  2544. });
RAW Paste Data