From 8555a2ddbac422f3f0d6d83210100098b01b9797 Mon Sep 17 00:00:00 2001 From: Sourav Badami Date: Tue, 6 Jun 2017 22:53:25 +0530 Subject: [PATCH 1/5] Added introjs external libraries. --- website/static/css/introjs.css | 496 ++++++++ website/static/js/intro.js | 1986 ++++++++++++++++++++++++++++++++ 2 files changed, 2482 insertions(+) create mode 100644 website/static/css/introjs.css create mode 100644 website/static/js/intro.js diff --git a/website/static/css/introjs.css b/website/static/css/introjs.css new file mode 100644 index 0000000000..8e64898d7a --- /dev/null +++ b/website/static/css/introjs.css @@ -0,0 +1,496 @@ +.introjs-overlay { + position: absolute; + box-sizing: content-box; + z-index: 999999; + background-color: #000; + opacity: 0; + background: -moz-radial-gradient(center,ellipse cover,rgba(0,0,0,0.4) 0,rgba(0,0,0,0.9) 100%); + background: -webkit-gradient(radial,center center,0px,center center,100%,color-stop(0%,rgba(0,0,0,0.4)),color-stop(100%,rgba(0,0,0,0.9))); + background: -webkit-radial-gradient(center,ellipse cover,rgba(0,0,0,0.4) 0,rgba(0,0,0,0.9) 100%); + background: -o-radial-gradient(center,ellipse cover,rgba(0,0,0,0.4) 0,rgba(0,0,0,0.9) 100%); + background: -ms-radial-gradient(center,ellipse cover,rgba(0,0,0,0.4) 0,rgba(0,0,0,0.9) 100%); + background: radial-gradient(center,ellipse cover,rgba(0,0,0,0.4) 0,rgba(0,0,0,0.9) 100%); + filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr='#66000000',endColorstr='#e6000000',GradientType=1)"; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; + filter: alpha(opacity=50); + -webkit-transition: all 0.3s ease-out; + -moz-transition: all 0.3s ease-out; + -ms-transition: all 0.3s ease-out; + -o-transition: all 0.3s ease-out; + transition: all 0.3s ease-out; +} + +.introjs-fixParent { + z-index: auto !important; + opacity: 1.0 !important; + -webkit-transform: none !important; + -moz-transform: none !important; + -ms-transform: none !important; + -o-transform: none !important; + transform: none !important; +} + +.introjs-showElement, +tr.introjs-showElement > td, +tr.introjs-showElement > th { + z-index: 9999999 !important; +} + +.introjs-disableInteraction { + z-index: 99999999 !important; + position: absolute; + background-color: white; + opacity: 0; + filter: alpha(opacity=0); +} + +.introjs-relativePosition, +tr.introjs-showElement > td, +tr.introjs-showElement > th { + position: relative; +} + +.introjs-helperLayer { + box-sizing: content-box; + position: absolute; + z-index: 9999998; + background-color: #FFF; + background-color: rgba(255,255,255,.9); + border: 1px solid #777; + border: 1px solid rgba(0,0,0,.5); + border-radius: 4px; + box-shadow: 0 2px 15px rgba(0,0,0,.4); + -webkit-transition: all 0.3s ease-out; + -moz-transition: all 0.3s ease-out; + -ms-transition: all 0.3s ease-out; + -o-transition: all 0.3s ease-out; + transition: all 0.3s ease-out; +} + +.introjs-tooltipReferenceLayer { + box-sizing: content-box; + position: absolute; + visibility: hidden; + z-index: 10000000; + background-color: transparent; + -webkit-transition: all 0.3s ease-out; + -moz-transition: all 0.3s ease-out; + -ms-transition: all 0.3s ease-out; + -o-transition: all 0.3s ease-out; + transition: all 0.3s ease-out; +} + +.introjs-helperLayer *, +.introjs-helperLayer *:before, +.introjs-helperLayer *:after { + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + -ms-box-sizing: content-box; + -o-box-sizing: content-box; + box-sizing: content-box; +} + +.introjs-helperNumberLayer { + box-sizing: content-box; + position: absolute; + visibility: visible; + top: -16px; + left: -16px; + z-index: 9999999999 !important; + padding: 2px; + font-family: Arial, verdana, tahoma; + font-size: 13px; + font-weight: bold; + color: white; + text-align: center; + text-shadow: 1px 1px 1px rgba(0,0,0,.3); + background: #ff3019; /* Old browsers */ + background: -webkit-linear-gradient(top, #ff3019 0%, #cf0404 100%); /* Chrome10+,Safari5.1+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #ff3019), color-stop(100%, #cf0404)); /* Chrome,Safari4+ */ + background: -moz-linear-gradient(top, #ff3019 0%, #cf0404 100%); /* FF3.6+ */ + background: -ms-linear-gradient(top, #ff3019 0%, #cf0404 100%); /* IE10+ */ + background: -o-linear-gradient(top, #ff3019 0%, #cf0404 100%); /* Opera 11.10+ */ + background: linear-gradient(to bottom, #ff3019 0%, #cf0404 100%); /* W3C */ + width: 20px; + height:20px; + line-height: 20px; + border: 3px solid white; + border-radius: 50%; + filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3019', endColorstr='#cf0404', GradientType=0)"; /* IE6-9 */ + filter: "progid:DXImageTransform.Microsoft.Shadow(direction=135, strength=2, color=ff0000)"; /* IE10 text shadows */ + box-shadow: 0 2px 5px rgba(0,0,0,.4); +} + +.introjs-arrow { + border: 5px solid white; + content:''; + position: absolute; +} +.introjs-arrow.top { + top: -10px; + border-top-color:transparent; + border-right-color:transparent; + border-bottom-color:white; + border-left-color:transparent; +} +.introjs-arrow.top-right { + top: -10px; + right: 10px; + border-top-color:transparent; + border-right-color:transparent; + border-bottom-color:white; + border-left-color:transparent; +} +.introjs-arrow.top-middle { + top: -10px; + left: 50%; + margin-left: -5px; + border-top-color:transparent; + border-right-color:transparent; + border-bottom-color:white; + border-left-color:transparent; +} +.introjs-arrow.right { + right: -10px; + top: 10px; + border-top-color:transparent; + border-right-color:transparent; + border-bottom-color:transparent; + border-left-color:white; +} +.introjs-arrow.right-bottom { + bottom:10px; + right: -10px; + border-top-color:transparent; + border-right-color:transparent; + border-bottom-color:transparent; + border-left-color:white; +} +.introjs-arrow.bottom { + bottom: -10px; + border-top-color:white; + border-right-color:transparent; + border-bottom-color:transparent; + border-left-color:transparent; +} +.introjs-arrow.left { + left: -10px; + top: 10px; + border-top-color:transparent; + border-right-color:white; + border-bottom-color:transparent; + border-left-color:transparent; +} +.introjs-arrow.left-bottom { + left: -10px; + bottom:10px; + border-top-color:transparent; + border-right-color:white; + border-bottom-color:transparent; + border-left-color:transparent; +} + +.introjs-tooltip { + box-sizing: content-box; + position: absolute; + visibility: visible; + padding: 10px; + background-color: white; + min-width: 200px; + max-width: 300px; + border-radius: 3px; + box-shadow: 0 1px 10px rgba(0,0,0,.4); + -webkit-transition: opacity 0.1s ease-out; + -moz-transition: opacity 0.1s ease-out; + -ms-transition: opacity 0.1s ease-out; + -o-transition: opacity 0.1s ease-out; + transition: opacity 0.1s ease-out; +} + +.introjs-tooltipbuttons { + text-align: right; + white-space: nowrap; +} + +/* + Buttons style by http://nicolasgallagher.com/lab/css3-github-buttons/ + Changed by Afshin Mehrabani +*/ +.introjs-button { + box-sizing: content-box; + position: relative; + overflow: visible; + display: inline-block; + padding: 0.3em 0.8em; + border: 1px solid #d4d4d4; + margin: 0; + text-decoration: none; + text-shadow: 1px 1px 0 #fff; + font: 11px/normal sans-serif; + color: #333; + white-space: nowrap; + cursor: pointer; + outline: none; + background-color: #ececec; + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f4f4f4), to(#ececec)); + background-image: -moz-linear-gradient(#f4f4f4, #ececec); + background-image: -o-linear-gradient(#f4f4f4, #ececec); + background-image: linear-gradient(#f4f4f4, #ececec); + -webkit-background-clip: padding; + -moz-background-clip: padding; + -o-background-clip: padding-box; + /*background-clip: padding-box;*/ /* commented out due to Opera 11.10 bug */ + -webkit-border-radius: 0.2em; + -moz-border-radius: 0.2em; + border-radius: 0.2em; + /* IE hacks */ + zoom: 1; + *display: inline; + margin-top: 10px; +} + +.introjs-button:hover { + border-color: #bcbcbc; + text-decoration: none; + box-shadow: 0px 1px 1px #e3e3e3; +} + +.introjs-button:focus, +.introjs-button:active { + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ececec), to(#f4f4f4)); + background-image: -moz-linear-gradient(#ececec, #f4f4f4); + background-image: -o-linear-gradient(#ececec, #f4f4f4); + background-image: linear-gradient(#ececec, #f4f4f4); +} + +/* overrides extra padding on button elements in Firefox */ +.introjs-button::-moz-focus-inner { + padding: 0; + border: 0; +} + +.introjs-skipbutton { + box-sizing: content-box; + margin-right: 5px; + color: #7a7a7a; +} + +.introjs-prevbutton { + -webkit-border-radius: 0.2em 0 0 0.2em; + -moz-border-radius: 0.2em 0 0 0.2em; + border-radius: 0.2em 0 0 0.2em; + border-right: none; +} + +.introjs-prevbutton.introjs-fullbutton { + border: 1px solid #d4d4d4; + -webkit-border-radius: 0.2em; + -moz-border-radius: 0.2em; + border-radius: 0.2em; +} + +.introjs-nextbutton { + -webkit-border-radius: 0 0.2em 0.2em 0; + -moz-border-radius: 0 0.2em 0.2em 0; + border-radius: 0 0.2em 0.2em 0; +} + +.introjs-nextbutton.introjs-fullbutton { + -webkit-border-radius: 0.2em; + -moz-border-radius: 0.2em; + border-radius: 0.2em; +} + +.introjs-disabled, .introjs-disabled:hover, .introjs-disabled:focus { + color: #9a9a9a; + border-color: #d4d4d4; + box-shadow: none; + cursor: default; + background-color: #f4f4f4; + background-image: none; + text-decoration: none; +} + +.introjs-hidden { + display: none; +} + +.introjs-bullets { + text-align: center; +} +.introjs-bullets ul { + box-sizing: content-box; + clear: both; + margin: 15px auto 0; + padding: 0; + display: inline-block; +} +.introjs-bullets ul li { + box-sizing: content-box; + list-style: none; + float: left; + margin: 0 2px; +} +.introjs-bullets ul li a { + box-sizing: content-box; + display: block; + width: 6px; + height: 6px; + background: #ccc; + border-radius: 10px; + -moz-border-radius: 10px; + -webkit-border-radius: 10px; + text-decoration: none; + cursor: pointer; +} +.introjs-bullets ul li a:hover { + background: #999; +} +.introjs-bullets ul li a.active { + background: #999; +} + +.introjs-progress { + box-sizing: content-box; + overflow: hidden; + height: 10px; + margin: 10px 0 5px 0; + border-radius: 4px; + background-color: #ecf0f1 +} +.introjs-progressbar { + box-sizing: content-box; + float: left; + width: 0%; + height: 100%; + font-size: 10px; + line-height: 10px; + text-align: center; + background-color: #08c; +} + +.introjsFloatingElement { + position: absolute; + height: 0; + width: 0; + left: 50%; + top: 50%; +} + +.introjs-fixedTooltip { + position: fixed; +} + +.introjs-hint { + box-sizing: content-box; + position: absolute; + background: transparent; + width: 20px; + height: 15px; + cursor: pointer; +} +.introjs-hint:focus { + border: 0; + outline: 0; +} +.introjs-hidehint { + display: none; +} + +.introjs-fixedhint { + position: fixed; +} + +.introjs-hint:hover > .introjs-hint-pulse { + border: 5px solid rgba(60, 60, 60, 0.57); +} + +.introjs-hint-pulse { + box-sizing: content-box; + width: 10px; + height: 10px; + border: 5px solid rgba(60, 60, 60, 0.27); + -webkit-border-radius: 30px; + -moz-border-radius: 30px; + border-radius: 30px; + background-color: rgba(136, 136, 136, 0.24); + z-index: 10; + position: absolute; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + -ms-transition: all 0.2s ease-out; + -o-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.introjs-hint-no-anim .introjs-hint-dot { + -webkit-animation: none; + -moz-animation: none; + animation: none; +} +.introjs-hint-dot { + box-sizing: content-box; + border: 10px solid rgba(146, 146, 146, 0.36); + background: transparent; + -webkit-border-radius: 60px; + -moz-border-radius: 60px; + border-radius: 60px; + height: 50px; + width: 50px; + -webkit-animation: introjspulse 3s ease-out; + -moz-animation: introjspulse 3s ease-out; + animation: introjspulse 3s ease-out; + -webkit-animation-iteration-count: infinite; + -moz-animation-iteration-count: infinite; + animation-iteration-count: infinite; + position: absolute; + top: -25px; + left: -25px; + z-index: 1; + opacity: 0; +} + +@-moz-keyframes introjspulse { + 0% { + -moz-transform: scale(0); + opacity: 0.0; + } + 25% { + -moz-transform: scale(0); + opacity: 0.1; + } + 50% { + -moz-transform: scale(0.1); + opacity: 0.3; + } + 75% { + -moz-transform: scale(0.5); + opacity: 0.5; + } + 100% { + -moz-transform: scale(1); + opacity: 0.0; + } +} + +@-webkit-keyframes introjspulse { + 0% { + -webkit-transform: scale(0); + opacity: 0.0; + } + 25% { + -webkit-transform: scale(0); + opacity: 0.1; + } + 50% { + -webkit-transform: scale(0.1); + opacity: 0.3; + } + 75% { + -webkit-transform: scale(0.5); + opacity: 0.5; + } + 100% { + -webkit-transform: scale(1); + opacity: 0.0; + } +} diff --git a/website/static/js/intro.js b/website/static/js/intro.js new file mode 100644 index 0000000000..75b4940731 --- /dev/null +++ b/website/static/js/intro.js @@ -0,0 +1,1986 @@ +/** + * Intro.js v2.5.0 + * https://github.com/usablica/intro.js + * + * Copyright (C) 2016 Afshin Mehrabani (@afshinmeh) + */ + +(function (root, factory) { + if (typeof exports === 'object') { + // CommonJS + factory(exports); + } else if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define(['exports'], factory); + } else { + // Browser globals + factory(root); + } +} (this, function (exports) { + //Default config/variables + var VERSION = '2.5.0'; + + /** + * IntroJs main class + * + * @class IntroJs + */ + function IntroJs(obj) { + this._targetElement = obj; + this._introItems = []; + + this._options = { + /* Next button label in tooltip box */ + nextLabel: 'Next →', + /* Previous button label in tooltip box */ + prevLabel: '← Back', + /* Skip button label in tooltip box */ + skipLabel: 'Skip', + /* Done button label in tooltip box */ + doneLabel: 'Done', + /* Hide previous button in the first step? Otherwise, it will be disabled button. */ + hidePrev: false, + /* Hide next button in the last step? Otherwise, it will be disabled button. */ + hideNext: false, + /* Default tooltip box position */ + tooltipPosition: 'bottom', + /* Next CSS class for tooltip boxes */ + tooltipClass: '', + /* CSS class that is added to the helperLayer */ + highlightClass: '', + /* Close introduction when pressing Escape button? */ + exitOnEsc: true, + /* Close introduction when clicking on overlay layer? */ + exitOnOverlayClick: true, + /* Show step numbers in introduction? */ + showStepNumbers: true, + /* Let user use keyboard to navigate the tour? */ + keyboardNavigation: true, + /* Show tour control buttons? */ + showButtons: true, + /* Show tour bullets? */ + showBullets: true, + /* Show tour progress? */ + showProgress: false, + /* Scroll to highlighted element? */ + scrollToElement: true, + /* Set the overlay opacity */ + overlayOpacity: 0.8, + /* Padding to add after scrolling when element is not in the viewport (in pixels) */ + scrollPadding: 30, + /* Precedence of positions, when auto is enabled */ + positionPrecedence: ["bottom", "top", "right", "left"], + /* Disable an interaction with element? */ + disableInteraction: false, + /* Default hint position */ + hintPosition: 'top-middle', + /* Hint button label */ + hintButtonLabel: 'Got it', + /* Adding animation to hints? */ + hintAnimation: true + }; + } + + /** + * Initiate a new introduction/guide from an element in the page + * + * @api private + * @method _introForElement + * @param {Object} targetElm + * @returns {Boolean} Success or not? + */ + function _introForElement(targetElm) { + var introItems = [], + self = this; + + if (this._options.steps) { + //use steps passed programmatically + for (var i = 0, stepsLength = this._options.steps.length; i < stepsLength; i++) { + var currentItem = _cloneObject(this._options.steps[i]); + //set the step + currentItem.step = introItems.length + 1; + //use querySelector function only when developer used CSS selector + if (typeof(currentItem.element) === 'string') { + //grab the element with given selector from the page + currentItem.element = document.querySelector(currentItem.element); + } + + //intro without element + if (typeof(currentItem.element) === 'undefined' || currentItem.element == null) { + var floatingElementQuery = document.querySelector(".introjsFloatingElement"); + + if (floatingElementQuery == null) { + floatingElementQuery = document.createElement('div'); + floatingElementQuery.className = 'introjsFloatingElement'; + + document.body.appendChild(floatingElementQuery); + } + + currentItem.element = floatingElementQuery; + currentItem.position = 'floating'; + } + + if (currentItem.element != null) { + introItems.push(currentItem); + } + } + + } else { + //use steps from data-* annotations + var allIntroSteps = targetElm.querySelectorAll('*[data-intro]'); + //if there's no element to intro + if (allIntroSteps.length < 1) { + return false; + } + + //first add intro items with data-step + for (var i = 0, elmsLength = allIntroSteps.length; i < elmsLength; i++) { + var currentElement = allIntroSteps[i]; + + // skip hidden elements + if (currentElement.style.display == 'none') { + continue; + } + + var step = parseInt(currentElement.getAttribute('data-step'), 10); + + if (step > 0) { + introItems[step - 1] = { + element: currentElement, + intro: currentElement.getAttribute('data-intro'), + step: parseInt(currentElement.getAttribute('data-step'), 10), + tooltipClass: currentElement.getAttribute('data-tooltipClass'), + highlightClass: currentElement.getAttribute('data-highlightClass'), + position: currentElement.getAttribute('data-position') || this._options.tooltipPosition + }; + } + } + + //next add intro items without data-step + //todo: we need a cleanup here, two loops are redundant + var nextStep = 0; + for (var i = 0, elmsLength = allIntroSteps.length; i < elmsLength; i++) { + var currentElement = allIntroSteps[i]; + + if (currentElement.getAttribute('data-step') == null) { + + while (true) { + if (typeof introItems[nextStep] == 'undefined') { + break; + } else { + nextStep++; + } + } + + introItems[nextStep] = { + element: currentElement, + intro: currentElement.getAttribute('data-intro'), + step: nextStep + 1, + tooltipClass: currentElement.getAttribute('data-tooltipClass'), + highlightClass: currentElement.getAttribute('data-highlightClass'), + position: currentElement.getAttribute('data-position') || this._options.tooltipPosition + }; + } + } + } + + //removing undefined/null elements + var tempIntroItems = []; + for (var z = 0; z < introItems.length; z++) { + introItems[z] && tempIntroItems.push(introItems[z]); // copy non-empty values to the end of the array + } + + introItems = tempIntroItems; + + //Ok, sort all items with given steps + introItems.sort(function (a, b) { + return a.step - b.step; + }); + + //set it to the introJs object + self._introItems = introItems; + + //add overlay layer to the page + if(_addOverlayLayer.call(self, targetElm)) { + //then, start the show + _nextStep.call(self); + + var skipButton = targetElm.querySelector('.introjs-skipbutton'), + nextStepButton = targetElm.querySelector('.introjs-nextbutton'); + + self._onKeyDown = function(e) { + if (e.keyCode === 27 && self._options.exitOnEsc == true) { + //escape key pressed, exit the intro + //check if exit callback is defined + _exitIntro.call(self, targetElm); + } else if(e.keyCode === 37) { + //left arrow + _previousStep.call(self); + } else if (e.keyCode === 39) { + //right arrow + _nextStep.call(self); + } else if (e.keyCode === 13) { + //srcElement === ie + var target = e.target || e.srcElement; + if (target && target.className.indexOf('introjs-prevbutton') > 0) { + //user hit enter while focusing on previous button + _previousStep.call(self); + } else if (target && target.className.indexOf('introjs-skipbutton') > 0) { + //user hit enter while focusing on skip button + if (self._introItems.length - 1 == self._currentStep && typeof (self._introCompleteCallback) === 'function') { + self._introCompleteCallback.call(self); + } + + _exitIntro.call(self, targetElm); + } else { + //default behavior for responding to enter + _nextStep.call(self); + } + + //prevent default behaviour on hitting Enter, to prevent steps being skipped in some browsers + if(e.preventDefault) { + e.preventDefault(); + } else { + e.returnValue = false; + } + } + }; + + self._onResize = function(e) { + _setHelperLayerPosition.call(self, document.querySelector('.introjs-helperLayer')); + _setHelperLayerPosition.call(self, document.querySelector('.introjs-tooltipReferenceLayer')); + }; + + if (window.addEventListener) { + if (this._options.keyboardNavigation) { + window.addEventListener('keydown', self._onKeyDown, true); + } + //for window resize + window.addEventListener('resize', self._onResize, true); + } else if (document.attachEvent) { //IE + if (this._options.keyboardNavigation) { + document.attachEvent('onkeydown', self._onKeyDown); + } + //for window resize + document.attachEvent('onresize', self._onResize); + } + } + return false; + } + + /* + * makes a copy of the object + * @api private + * @method _cloneObject + */ + function _cloneObject(object) { + if (object == null || typeof (object) != 'object' || typeof (object.nodeType) != 'undefined') { + return object; + } + var temp = {}; + for (var key in object) { + if (typeof (jQuery) != 'undefined' && object[key] instanceof jQuery) { + temp[key] = object[key]; + } else { + temp[key] = _cloneObject(object[key]); + } + } + return temp; + } + /** + * Go to specific step of introduction + * + * @api private + * @method _goToStep + */ + function _goToStep(step) { + //because steps starts with zero + this._currentStep = step - 2; + if (typeof (this._introItems) !== 'undefined') { + _nextStep.call(this); + } + } + + /** + * Go to the specific step of introduction with the explicit [data-step] number + * + * @api private + * @method _goToStepNumber + */ + function _goToStepNumber(step) { + this._currentStepNumber = step; + if (typeof (this._introItems) !== 'undefined') { + _nextStep.call(this); + } + } + + /** + * Go to next step on intro + * + * @api private + * @method _nextStep + */ + function _nextStep() { + this._direction = 'forward'; + + if (typeof (this._currentStepNumber) !== 'undefined') { + for( var i = 0, len = this._introItems.length; i < len; i++ ) { + var item = this._introItems[i]; + if( item.step === this._currentStepNumber ) { + this._currentStep = i - 1; + this._currentStepNumber = undefined; + } + } + } + + if (typeof (this._currentStep) === 'undefined') { + this._currentStep = 0; + } else { + ++this._currentStep; + } + + if ((this._introItems.length) <= this._currentStep) { + //end of the intro + //check if any callback is defined + if (typeof (this._introCompleteCallback) === 'function') { + this._introCompleteCallback.call(this); + } + _exitIntro.call(this, this._targetElement); + return; + } + + var nextStep = this._introItems[this._currentStep]; + if (typeof (this._introBeforeChangeCallback) !== 'undefined') { + this._introBeforeChangeCallback.call(this, nextStep.element); + } + + _showElement.call(this, nextStep); + } + + /** + * Go to previous step on intro + * + * @api private + * @method _previousStep + */ + function _previousStep() { + this._direction = 'backward'; + + if (this._currentStep === 0) { + return false; + } + + var nextStep = this._introItems[--this._currentStep]; + if (typeof (this._introBeforeChangeCallback) !== 'undefined') { + this._introBeforeChangeCallback.call(this, nextStep.element); + } + + _showElement.call(this, nextStep); + } + + /** + * Exit from intro + * + * @api private + * @method _exitIntro + * @param {Object} targetElement + */ + function _exitIntro(targetElement) { + //remove overlay layers from the page + var overlayLayers = targetElement.querySelectorAll('.introjs-overlay'); + + if (overlayLayers && overlayLayers.length > 0) { + for (var i = overlayLayers.length - 1; i >= 0; i--) { + //for fade-out animation + var overlayLayer = overlayLayers[i]; + overlayLayer.style.opacity = 0; + setTimeout(function () { + if (this.parentNode) { + this.parentNode.removeChild(this); + } + }.bind(overlayLayer), 500); + }; + } + + //remove all helper layers + var helperLayer = targetElement.querySelector('.introjs-helperLayer'); + if (helperLayer) { + helperLayer.parentNode.removeChild(helperLayer); + } + + var referenceLayer = targetElement.querySelector('.introjs-tooltipReferenceLayer'); + if (referenceLayer) { + referenceLayer.parentNode.removeChild(referenceLayer); + } + //remove disableInteractionLayer + var disableInteractionLayer = targetElement.querySelector('.introjs-disableInteraction'); + if (disableInteractionLayer) { + disableInteractionLayer.parentNode.removeChild(disableInteractionLayer); + } + + //remove intro floating element + var floatingElement = document.querySelector('.introjsFloatingElement'); + if (floatingElement) { + floatingElement.parentNode.removeChild(floatingElement); + } + + _removeShowElement(); + + //remove `introjs-fixParent` class from the elements + var fixParents = document.querySelectorAll('.introjs-fixParent'); + if (fixParents && fixParents.length > 0) { + for (var i = fixParents.length - 1; i >= 0; i--) { + fixParents[i].className = fixParents[i].className.replace(/introjs-fixParent/g, '').replace(/^\s+|\s+$/g, ''); + } + } + + //clean listeners + if (window.removeEventListener) { + window.removeEventListener('keydown', this._onKeyDown, true); + } else if (document.detachEvent) { //IE + document.detachEvent('onkeydown', this._onKeyDown); + } + + //check if any callback is defined + if (this._introExitCallback != undefined) { + this._introExitCallback.call(self); + } + + //set the step to zero + this._currentStep = undefined; + } + + /** + * Render tooltip box in the page + * + * @api private + * @method _placeTooltip + * @param {HTMLElement} targetElement + * @param {HTMLElement} tooltipLayer + * @param {HTMLElement} arrowLayer + * @param {HTMLElement} helperNumberLayer + * @param {Boolean} hintMode + */ + function _placeTooltip(targetElement, tooltipLayer, arrowLayer, helperNumberLayer, hintMode) { + var tooltipCssClass = '', + currentStepObj, + tooltipOffset, + targetOffset, + windowSize, + currentTooltipPosition; + + hintMode = hintMode || false; + + //reset the old style + tooltipLayer.style.top = null; + tooltipLayer.style.right = null; + tooltipLayer.style.bottom = null; + tooltipLayer.style.left = null; + tooltipLayer.style.marginLeft = null; + tooltipLayer.style.marginTop = null; + + arrowLayer.style.display = 'inherit'; + + if (typeof(helperNumberLayer) != 'undefined' && helperNumberLayer != null) { + helperNumberLayer.style.top = null; + helperNumberLayer.style.left = null; + } + + //prevent error when `this._currentStep` is undefined + if (!this._introItems[this._currentStep]) return; + + //if we have a custom css class for each step + currentStepObj = this._introItems[this._currentStep]; + if (typeof (currentStepObj.tooltipClass) === 'string') { + tooltipCssClass = currentStepObj.tooltipClass; + } else { + tooltipCssClass = this._options.tooltipClass; + } + + tooltipLayer.className = ('introjs-tooltip ' + tooltipCssClass).replace(/^\s+|\s+$/g, ''); + + currentTooltipPosition = this._introItems[this._currentStep].position; + if ((currentTooltipPosition == "auto" || this._options.tooltipPosition == "auto")) { + if (currentTooltipPosition != "floating") { // Floating is always valid, no point in calculating + currentTooltipPosition = _determineAutoPosition.call(this, targetElement, tooltipLayer, currentTooltipPosition); + } + } + targetOffset = _getOffset(targetElement); + tooltipOffset = _getOffset(tooltipLayer); + windowSize = _getWinSize(); + + switch (currentTooltipPosition) { + case 'top': + arrowLayer.className = 'introjs-arrow bottom'; + + if (hintMode) { + var tooltipLayerStyleLeft = 0; + } else { + var tooltipLayerStyleLeft = 15; + } + + _checkRight(targetOffset, tooltipLayerStyleLeft, tooltipOffset, windowSize, tooltipLayer); + tooltipLayer.style.bottom = (targetOffset.height + 20) + 'px'; + break; + case 'right': + tooltipLayer.style.left = (targetOffset.width + 20) + 'px'; + if (targetOffset.top + tooltipOffset.height > windowSize.height) { + // In this case, right would have fallen below the bottom of the screen. + // Modify so that the bottom of the tooltip connects with the target + arrowLayer.className = "introjs-arrow left-bottom"; + tooltipLayer.style.top = "-" + (tooltipOffset.height - targetOffset.height - 20) + "px"; + } else { + arrowLayer.className = 'introjs-arrow left'; + } + break; + case 'left': + if (!hintMode && this._options.showStepNumbers == true) { + tooltipLayer.style.top = '15px'; + } + + if (targetOffset.top + tooltipOffset.height > windowSize.height) { + // In this case, left would have fallen below the bottom of the screen. + // Modify so that the bottom of the tooltip connects with the target + tooltipLayer.style.top = "-" + (tooltipOffset.height - targetOffset.height - 20) + "px"; + arrowLayer.className = 'introjs-arrow right-bottom'; + } else { + arrowLayer.className = 'introjs-arrow right'; + } + tooltipLayer.style.right = (targetOffset.width + 20) + 'px'; + + break; + case 'floating': + arrowLayer.style.display = 'none'; + + //we have to adjust the top and left of layer manually for intro items without element + tooltipLayer.style.left = '50%'; + tooltipLayer.style.top = '50%'; + tooltipLayer.style.marginLeft = '-' + (tooltipOffset.width / 2) + 'px'; + tooltipLayer.style.marginTop = '-' + (tooltipOffset.height / 2) + 'px'; + + if (typeof(helperNumberLayer) != 'undefined' && helperNumberLayer != null) { + helperNumberLayer.style.left = '-' + ((tooltipOffset.width / 2) + 18) + 'px'; + helperNumberLayer.style.top = '-' + ((tooltipOffset.height / 2) + 18) + 'px'; + } + + break; + case 'bottom-right-aligned': + arrowLayer.className = 'introjs-arrow top-right'; + + var tooltipLayerStyleRight = 0; + _checkLeft(targetOffset, tooltipLayerStyleRight, tooltipOffset, tooltipLayer); + tooltipLayer.style.top = (targetOffset.height + 20) + 'px'; + break; + + case 'bottom-middle-aligned': + arrowLayer.className = 'introjs-arrow top-middle'; + + var tooltipLayerStyleLeftRight = targetOffset.width / 2 - tooltipOffset.width / 2; + + // a fix for middle aligned hints + if (hintMode) { + tooltipLayerStyleLeftRight += 5; + } + + if (_checkLeft(targetOffset, tooltipLayerStyleLeftRight, tooltipOffset, tooltipLayer)) { + tooltipLayer.style.right = null; + _checkRight(targetOffset, tooltipLayerStyleLeftRight, tooltipOffset, windowSize, tooltipLayer); + } + tooltipLayer.style.top = (targetOffset.height + 20) + 'px'; + break; + + case 'bottom-left-aligned': + // Bottom-left-aligned is the same as the default bottom + case 'bottom': + // Bottom going to follow the default behavior + default: + arrowLayer.className = 'introjs-arrow top'; + + var tooltipLayerStyleLeft = 0; + _checkRight(targetOffset, tooltipLayerStyleLeft, tooltipOffset, windowSize, tooltipLayer); + tooltipLayer.style.top = (targetOffset.height + 20) + 'px'; + break; + } + } + + /** + * Set tooltip left so it doesn't go off the right side of the window + * + * @return boolean true, if tooltipLayerStyleLeft is ok. false, otherwise. + */ + function _checkRight(targetOffset, tooltipLayerStyleLeft, tooltipOffset, windowSize, tooltipLayer) { + if (targetOffset.left + tooltipLayerStyleLeft + tooltipOffset.width > windowSize.width) { + // off the right side of the window + tooltipLayer.style.left = (windowSize.width - tooltipOffset.width - targetOffset.left) + 'px'; + return false; + } + tooltipLayer.style.left = tooltipLayerStyleLeft + 'px'; + return true; + } + + /** + * Set tooltip right so it doesn't go off the left side of the window + * + * @return boolean true, if tooltipLayerStyleRight is ok. false, otherwise. + */ + function _checkLeft(targetOffset, tooltipLayerStyleRight, tooltipOffset, tooltipLayer) { + if (targetOffset.left + targetOffset.width - tooltipLayerStyleRight - tooltipOffset.width < 0) { + // off the left side of the window + tooltipLayer.style.left = (-targetOffset.left) + 'px'; + return false; + } + tooltipLayer.style.right = tooltipLayerStyleRight + 'px'; + return true; + } + + /** + * Determines the position of the tooltip based on the position precedence and availability + * of screen space. + * + * @param {Object} targetElement + * @param {Object} tooltipLayer + * @param {Object} desiredTooltipPosition + * + */ + function _determineAutoPosition(targetElement, tooltipLayer, desiredTooltipPosition) { + + // Take a clone of position precedence. These will be the available + var possiblePositions = this._options.positionPrecedence.slice(); + + var windowSize = _getWinSize(); + var tooltipHeight = _getOffset(tooltipLayer).height + 10; + var tooltipWidth = _getOffset(tooltipLayer).width + 20; + var targetOffset = _getOffset(targetElement); + + // If we check all the possible areas, and there are no valid places for the tooltip, the element + // must take up most of the screen real estate. Show the tooltip floating in the middle of the screen. + var calculatedPosition = "floating"; + + // Check if the width of the tooltip + the starting point would spill off the right side of the screen + // If no, neither bottom or top are valid + if (targetOffset.left + tooltipWidth > windowSize.width || ((targetOffset.left + (targetOffset.width / 2)) - tooltipWidth) < 0) { + _removeEntry(possiblePositions, "bottom"); + _removeEntry(possiblePositions, "top"); + } else { + // Check for space below + if ((targetOffset.height + targetOffset.top + tooltipHeight) > windowSize.height) { + _removeEntry(possiblePositions, "bottom"); + } + + // Check for space above + if (targetOffset.top - tooltipHeight < 0) { + _removeEntry(possiblePositions, "top"); + } + } + + // Check for space to the right + if (targetOffset.width + targetOffset.left + tooltipWidth > windowSize.width) { + _removeEntry(possiblePositions, "right"); + } + + // Check for space to the left + if (targetOffset.left - tooltipWidth < 0) { + _removeEntry(possiblePositions, "left"); + } + + // At this point, our array only has positions that are valid. Pick the first one, as it remains in order + if (possiblePositions.length > 0) { + calculatedPosition = possiblePositions[0]; + } + + // If the requested position is in the list, replace our calculated choice with that + if (desiredTooltipPosition && desiredTooltipPosition != "auto") { + if (possiblePositions.indexOf(desiredTooltipPosition) > -1) { + calculatedPosition = desiredTooltipPosition; + } + } + + return calculatedPosition; + } + + /** + * Remove an entry from a string array if it's there, does nothing if it isn't there. + * + * @param {Array} stringArray + * @param {String} stringToRemove + */ + function _removeEntry(stringArray, stringToRemove) { + if (stringArray.indexOf(stringToRemove) > -1) { + stringArray.splice(stringArray.indexOf(stringToRemove), 1); + } + } + + /** + * Update the position of the helper layer on the screen + * + * @api private + * @method _setHelperLayerPosition + * @param {Object} helperLayer + */ + function _setHelperLayerPosition(helperLayer) { + if (helperLayer) { + //prevent error when `this._currentStep` in undefined + if (!this._introItems[this._currentStep]) return; + + var currentElement = this._introItems[this._currentStep], + elementPosition = _getOffset(currentElement.element), + widthHeightPadding = 10; + + // If the target element is fixed, the tooltip should be fixed as well. + // Otherwise, remove a fixed class that may be left over from the previous + // step. + if (_isFixed(currentElement.element)) { + helperLayer.className += ' introjs-fixedTooltip'; + } else { + helperLayer.className = helperLayer.className.replace(' introjs-fixedTooltip', ''); + } + + if (currentElement.position == 'floating') { + widthHeightPadding = 0; + } + + //set new position to helper layer + helperLayer.setAttribute('style', 'width: ' + (elementPosition.width + widthHeightPadding) + 'px; ' + + 'height:' + (elementPosition.height + widthHeightPadding) + 'px; ' + + 'top:' + (elementPosition.top - 5) + 'px;' + + 'left: ' + (elementPosition.left - 5) + 'px;'); + + } + } + + /** + * Add disableinteraction layer and adjust the size and position of the layer + * + * @api private + * @method _disableInteraction + */ + function _disableInteraction() { + var disableInteractionLayer = document.querySelector('.introjs-disableInteraction'); + if (disableInteractionLayer === null) { + disableInteractionLayer = document.createElement('div'); + disableInteractionLayer.className = 'introjs-disableInteraction'; + this._targetElement.appendChild(disableInteractionLayer); + } + + _setHelperLayerPosition.call(this, disableInteractionLayer); + } + + /** + * Setting anchors to behave like buttons + * + * @api private + * @method _setAnchorAsButton + */ + function _setAnchorAsButton(anchor){ + anchor.setAttribute('role', 'button'); + anchor.tabIndex = 0; + } + + /** + * Show an element on the page + * + * @api private + * @method _showElement + * @param {Object} targetElement + */ + function _showElement(targetElement) { + if (typeof (this._introChangeCallback) !== 'undefined') { + this._introChangeCallback.call(this, targetElement.element); + } + + var self = this, + oldHelperLayer = document.querySelector('.introjs-helperLayer'), + oldReferenceLayer = document.querySelector('.introjs-tooltipReferenceLayer'), + highlightClass = 'introjs-helperLayer', + elementPosition = _getOffset(targetElement.element); + + //check for a current step highlight class + if (typeof (targetElement.highlightClass) === 'string') { + highlightClass += (' ' + targetElement.highlightClass); + } + //check for options highlight class + if (typeof (this._options.highlightClass) === 'string') { + highlightClass += (' ' + this._options.highlightClass); + } + + if (oldHelperLayer != null) { + var oldHelperNumberLayer = oldReferenceLayer.querySelector('.introjs-helperNumberLayer'), + oldtooltipLayer = oldReferenceLayer.querySelector('.introjs-tooltiptext'), + oldArrowLayer = oldReferenceLayer.querySelector('.introjs-arrow'), + oldtooltipContainer = oldReferenceLayer.querySelector('.introjs-tooltip'), + skipTooltipButton = oldReferenceLayer.querySelector('.introjs-skipbutton'), + prevTooltipButton = oldReferenceLayer.querySelector('.introjs-prevbutton'), + nextTooltipButton = oldReferenceLayer.querySelector('.introjs-nextbutton'); + + //update or reset the helper highlight class + oldHelperLayer.className = highlightClass; + //hide the tooltip + oldtooltipContainer.style.opacity = 0; + oldtooltipContainer.style.display = "none"; + + if (oldHelperNumberLayer != null) { + var lastIntroItem = this._introItems[(targetElement.step - 2 >= 0 ? targetElement.step - 2 : 0)]; + + if (lastIntroItem != null && (this._direction == 'forward' && lastIntroItem.position == 'floating') || (this._direction == 'backward' && targetElement.position == 'floating')) { + oldHelperNumberLayer.style.opacity = 0; + } + } + + //set new position to helper layer + _setHelperLayerPosition.call(self, oldHelperLayer); + _setHelperLayerPosition.call(self, oldReferenceLayer); + + //remove `introjs-fixParent` class from the elements + var fixParents = document.querySelectorAll('.introjs-fixParent'); + if (fixParents && fixParents.length > 0) { + for (var i = fixParents.length - 1; i >= 0; i--) { + fixParents[i].className = fixParents[i].className.replace(/introjs-fixParent/g, '').replace(/^\s+|\s+$/g, ''); + }; + } + + //remove old classes if the element still exist + _removeShowElement(); + + //we should wait until the CSS3 transition is competed (it's 0.3 sec) to prevent incorrect `height` and `width` calculation + if (self._lastShowElementTimer) { + clearTimeout(self._lastShowElementTimer); + } + self._lastShowElementTimer = setTimeout(function() { + //set current step to the label + if (oldHelperNumberLayer != null) { + oldHelperNumberLayer.innerHTML = targetElement.step; + } + //set current tooltip text + oldtooltipLayer.innerHTML = targetElement.intro; + //set the tooltip position + oldtooltipContainer.style.display = "block"; + _placeTooltip.call(self, targetElement.element, oldtooltipContainer, oldArrowLayer, oldHelperNumberLayer); + + //change active bullet + if (self._options.showBullets) { + oldReferenceLayer.querySelector('.introjs-bullets li > a.active').className = ''; + oldReferenceLayer.querySelector('.introjs-bullets li > a[data-stepnumber="' + targetElement.step + '"]').className = 'active'; + } + oldReferenceLayer.querySelector('.introjs-progress .introjs-progressbar').setAttribute('style', 'width:' + _getProgress.call(self) + '%;'); + + //show the tooltip + oldtooltipContainer.style.opacity = 1; + if (oldHelperNumberLayer) oldHelperNumberLayer.style.opacity = 1; + + //reset button focus + if (nextTooltipButton.tabIndex === -1) { + //tabindex of -1 means we are at the end of the tour - focus on skip / done + skipTooltipButton.focus(); + } else { + //still in the tour, focus on next + nextTooltipButton.focus(); + } + }, 350); + + // end of old element if-else condition + } else { + var helperLayer = document.createElement('div'), + referenceLayer = document.createElement('div'), + arrowLayer = document.createElement('div'), + tooltipLayer = document.createElement('div'), + tooltipTextLayer = document.createElement('div'), + bulletsLayer = document.createElement('div'), + progressLayer = document.createElement('div'), + buttonsLayer = document.createElement('div'); + + helperLayer.className = highlightClass; + referenceLayer.className = 'introjs-tooltipReferenceLayer'; + + //set new position to helper layer + _setHelperLayerPosition.call(self, helperLayer); + _setHelperLayerPosition.call(self, referenceLayer); + + //add helper layer to target element + this._targetElement.appendChild(helperLayer); + this._targetElement.appendChild(referenceLayer); + + arrowLayer.className = 'introjs-arrow'; + + tooltipTextLayer.className = 'introjs-tooltiptext'; + tooltipTextLayer.innerHTML = targetElement.intro; + + bulletsLayer.className = 'introjs-bullets'; + + if (this._options.showBullets === false) { + bulletsLayer.style.display = 'none'; + } + + var ulContainer = document.createElement('ul'); + + for (var i = 0, stepsLength = this._introItems.length; i < stepsLength; i++) { + var innerLi = document.createElement('li'); + var anchorLink = document.createElement('a'); + + anchorLink.onclick = function() { + self.goToStep(this.getAttribute('data-stepnumber')); + }; + + if (i === (targetElement.step-1)) anchorLink.className = 'active'; + + _setAnchorAsButton(anchorLink); + anchorLink.innerHTML = " "; + anchorLink.setAttribute('data-stepnumber', this._introItems[i].step); + + innerLi.appendChild(anchorLink); + ulContainer.appendChild(innerLi); + } + + bulletsLayer.appendChild(ulContainer); + + progressLayer.className = 'introjs-progress'; + + if (this._options.showProgress === false) { + progressLayer.style.display = 'none'; + } + var progressBar = document.createElement('div'); + progressBar.className = 'introjs-progressbar'; + progressBar.setAttribute('style', 'width:' + _getProgress.call(this) + '%;'); + + progressLayer.appendChild(progressBar); + + buttonsLayer.className = 'introjs-tooltipbuttons'; + if (this._options.showButtons === false) { + buttonsLayer.style.display = 'none'; + } + + tooltipLayer.className = 'introjs-tooltip'; + tooltipLayer.appendChild(tooltipTextLayer); + tooltipLayer.appendChild(bulletsLayer); + tooltipLayer.appendChild(progressLayer); + + //add helper layer number + if (this._options.showStepNumbers == true) { + var helperNumberLayer = document.createElement('span'); + helperNumberLayer.className = 'introjs-helperNumberLayer'; + helperNumberLayer.innerHTML = targetElement.step; + referenceLayer.appendChild(helperNumberLayer); + } + + tooltipLayer.appendChild(arrowLayer); + referenceLayer.appendChild(tooltipLayer); + + //next button + var nextTooltipButton = document.createElement('a'); + + nextTooltipButton.onclick = function() { + if (self._introItems.length - 1 != self._currentStep) { + _nextStep.call(self); + } + }; + + _setAnchorAsButton(nextTooltipButton); + nextTooltipButton.innerHTML = this._options.nextLabel; + + //previous button + var prevTooltipButton = document.createElement('a'); + + prevTooltipButton.onclick = function() { + if (self._currentStep != 0) { + _previousStep.call(self); + } + }; + + _setAnchorAsButton(prevTooltipButton); + prevTooltipButton.innerHTML = this._options.prevLabel; + + //skip button + var skipTooltipButton = document.createElement('a'); + skipTooltipButton.className = 'introjs-button introjs-skipbutton'; + _setAnchorAsButton(skipTooltipButton); + skipTooltipButton.innerHTML = this._options.skipLabel; + + skipTooltipButton.onclick = function() { + if (self._introItems.length - 1 == self._currentStep && typeof (self._introCompleteCallback) === 'function') { + self._introCompleteCallback.call(self); + } + + _exitIntro.call(self, self._targetElement); + }; + + buttonsLayer.appendChild(skipTooltipButton); + + //in order to prevent displaying next/previous button always + if (this._introItems.length > 1) { + buttonsLayer.appendChild(prevTooltipButton); + buttonsLayer.appendChild(nextTooltipButton); + } + + tooltipLayer.appendChild(buttonsLayer); + + //set proper position + _placeTooltip.call(self, targetElement.element, tooltipLayer, arrowLayer, helperNumberLayer); + + //end of new element if-else condition + } + + //disable interaction + if (this._options.disableInteraction === true) { + _disableInteraction.call(self); + } + + prevTooltipButton.removeAttribute('tabIndex'); + nextTooltipButton.removeAttribute('tabIndex'); + + // when it's the first step of tour + if (this._currentStep == 0 && this._introItems.length > 1) { + skipTooltipButton.className = 'introjs-button introjs-skipbutton'; + nextTooltipButton.className = 'introjs-button introjs-nextbutton'; + + if (this._options.hidePrev == true) { + prevTooltipButton.className = 'introjs-button introjs-prevbutton introjs-hidden'; + nextTooltipButton.className += ' introjs-fullbutton'; + } else { + prevTooltipButton.className = 'introjs-button introjs-prevbutton introjs-disabled'; + } + + prevTooltipButton.tabIndex = '-1'; + skipTooltipButton.innerHTML = this._options.skipLabel; + } else if (this._introItems.length - 1 == this._currentStep || this._introItems.length == 1) { + // last step of tour + skipTooltipButton.innerHTML = this._options.doneLabel; + // adding donebutton class in addition to skipbutton + skipTooltipButton.className += ' introjs-donebutton'; + prevTooltipButton.className = 'introjs-button introjs-prevbutton'; + + if (this._options.hideNext == true) { + nextTooltipButton.className = 'introjs-button introjs-nextbutton introjs-hidden'; + prevTooltipButton.className += ' introjs-fullbutton'; + } else { + nextTooltipButton.className = 'introjs-button introjs-nextbutton introjs-disabled'; + } + + nextTooltipButton.tabIndex = '-1'; + } else { + // steps between start and end + skipTooltipButton.className = 'introjs-button introjs-skipbutton'; + prevTooltipButton.className = 'introjs-button introjs-prevbutton'; + nextTooltipButton.className = 'introjs-button introjs-nextbutton'; + skipTooltipButton.innerHTML = this._options.skipLabel; + } + + //Set focus on "next" button, so that hitting Enter always moves you onto the next step + nextTooltipButton.focus(); + + _setShowElement(targetElement); + + if (!_elementInViewport(targetElement.element) && this._options.scrollToElement === true) { + var rect = targetElement.element.getBoundingClientRect(), + winHeight = _getWinSize().height, + top = rect.bottom - (rect.bottom - rect.top), + bottom = rect.bottom - winHeight; + + //Scroll up + if (top < 0 || targetElement.element.clientHeight > winHeight) { + window.scrollBy(0, top - this._options.scrollPadding); // 30px padding from edge to look nice + + //Scroll down + } else { + window.scrollBy(0, bottom + 70 + this._options.scrollPadding); // 70px + 30px padding from edge to look nice + } + } + + if (typeof (this._introAfterChangeCallback) !== 'undefined') { + this._introAfterChangeCallback.call(this, targetElement.element); + } + } + + /** + * To remove all show element(s) + * + * @api private + * @method _removeShowElement + */ + function _removeShowElement() { + var elms = document.querySelectorAll('.introjs-showElement'); + + for (var i = 0, l = elms.length; i < l; i++) { + var elm = elms[i]; + _removeClass(elm, /introjs-[a-zA-Z]+/g); + } + } + + /** + * To set the show element + * This function set a relative (in most cases) position and changes the z-index + * + * @api private + * @method _setShowElement + * @param {Object} targetElement + */ + function _setShowElement(targetElement) { + // we need to add this show element class to the parent of SVG elements + // because the SVG elements can't have independent z-index + if (targetElement.element instanceof SVGElement) { + var parentElm = targetElement.element.parentNode; + + while (targetElement.element.parentNode != null) { + if (!parentElm.tagName || parentElm.tagName.toLowerCase() === 'body') break; + + if (parentElm.tagName.toLowerCase() === 'svg') { + _setClass(parentElm, 'introjs-showElement introjs-relativePosition'); + } + + parentElm = parentElm.parentNode; + } + } + + _setClass(targetElement.element, 'introjs-showElement'); + + var currentElementPosition = _getPropValue(targetElement.element, 'position'); + if (currentElementPosition !== 'absolute' && + currentElementPosition !== 'relative' && + currentElementPosition !== 'fixed') { + //change to new intro item + //targetElement.element.className += ' introjs-relativePosition'; + _setClass(targetElement.element, 'introjs-relativePosition') + } + + var parentElm = targetElement.element.parentNode; + while (parentElm != null) { + if (!parentElm.tagName || parentElm.tagName.toLowerCase() === 'body') break; + + //fix The Stacking Context problem. + //More detail: https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Understanding_z_index/The_stacking_context + var zIndex = _getPropValue(parentElm, 'z-index'); + var opacity = parseFloat(_getPropValue(parentElm, 'opacity')); + var transform = _getPropValue(parentElm, 'transform') || _getPropValue(parentElm, '-webkit-transform') || _getPropValue(parentElm, '-moz-transform') || _getPropValue(parentElm, '-ms-transform') || _getPropValue(parentElm, '-o-transform'); + if (/[0-9]+/.test(zIndex) || opacity < 1 || (transform !== 'none' && transform !== undefined)) { + parentElm.className += ' introjs-fixParent'; + } + + parentElm = parentElm.parentNode; + } + } + + function _setClass(element, className) { + if (element instanceof SVGElement) { + var pre = element.getAttribute('class') || ''; + + element.setAttribute('class', pre + ' ' + className); + } else { + element.className += ' ' + className; + } + } + + function _removeClass(element, classNameRegex) { + if (element instanceof SVGElement) { + var pre = element.getAttribute('class') || ''; + + element.setAttribute('class', pre.replace(classNameRegex, '').replace(/^\s+|\s+$/g, '')); + } else { + element.className = element.className.replace(classNameRegex, '').replace(/^\s+|\s+$/g, ''); + } + } + + /** + * Get an element CSS property on the page + * Thanks to JavaScript Kit: http://www.javascriptkit.com/dhtmltutors/dhtmlcascade4.shtml + * + * @api private + * @method _getPropValue + * @param {Object} element + * @param {String} propName + * @returns Element's property value + */ + function _getPropValue (element, propName) { + var propValue = ''; + if (element.currentStyle) { //IE + propValue = element.currentStyle[propName]; + } else if (document.defaultView && document.defaultView.getComputedStyle) { //Others + propValue = document.defaultView.getComputedStyle(element, null).getPropertyValue(propName); + } + + //Prevent exception in IE + if (propValue && propValue.toLowerCase) { + return propValue.toLowerCase(); + } else { + return propValue; + } + } + + /** + * Checks to see if target element (or parents) position is fixed or not + * + * @api private + * @method _isFixed + * @param {Object} element + * @returns Boolean + */ + function _isFixed (element) { + var p = element.parentNode; + + if (!p || p.nodeName === 'HTML') { + return false; + } + + if (_getPropValue(element, 'position') == 'fixed') { + return true; + } + + return _isFixed(p); + } + + /** + * Provides a cross-browser way to get the screen dimensions + * via: http://stackoverflow.com/questions/5864467/internet-explorer-innerheight + * + * @api private + * @method _getWinSize + * @returns {Object} width and height attributes + */ + function _getWinSize() { + if (window.innerWidth != undefined) { + return { width: window.innerWidth, height: window.innerHeight }; + } else { + var D = document.documentElement; + return { width: D.clientWidth, height: D.clientHeight }; + } + } + + /** + * Check to see if the element is in the viewport or not + * http://stackoverflow.com/questions/123999/how-to-tell-if-a-dom-element-is-visible-in-the-current-viewport + * + * @api private + * @method _elementInViewport + * @param {Object} el + */ + function _elementInViewport(el) { + var rect = el.getBoundingClientRect(); + + return ( + rect.top >= 0 && + rect.left >= 0 && + (rect.bottom+80) <= window.innerHeight && // add 80 to get the text right + rect.right <= window.innerWidth + ); + } + + /** + * Add overlay layer to the page + * + * @api private + * @method _addOverlayLayer + * @param {Object} targetElm + */ + function _addOverlayLayer(targetElm) { + var overlayLayer = document.createElement('div'), + styleText = '', + self = this; + + //set css class name + overlayLayer.className = 'introjs-overlay'; + + //check if the target element is body, we should calculate the size of overlay layer in a better way + if (!targetElm.tagName || targetElm.tagName.toLowerCase() === 'body') { + styleText += 'top: 0;bottom: 0; left: 0;right: 0;position: fixed;'; + overlayLayer.setAttribute('style', styleText); + } else { + //set overlay layer position + var elementPosition = _getOffset(targetElm); + if (elementPosition) { + styleText += 'width: ' + elementPosition.width + 'px; height:' + elementPosition.height + 'px; top:' + elementPosition.top + 'px;left: ' + elementPosition.left + 'px;'; + overlayLayer.setAttribute('style', styleText); + } + } + + targetElm.appendChild(overlayLayer); + + overlayLayer.onclick = function() { + if (self._options.exitOnOverlayClick == true) { + _exitIntro.call(self, targetElm); + } + }; + + setTimeout(function() { + styleText += 'opacity: ' + self._options.overlayOpacity.toString() + ';'; + overlayLayer.setAttribute('style', styleText); + }, 10); + + return true; + } + + /** + * Removes open hint (tooltip hint) + * + * @api private + * @method _removeHintTooltip + */ + function _removeHintTooltip() { + var tooltip = this._targetElement.querySelector('.introjs-hintReference'); + + if (tooltip) { + var step = tooltip.getAttribute('data-step'); + tooltip.parentNode.removeChild(tooltip); + return step; + } + } + + /** + * Start parsing hint items + * + * @api private + * @param {Object} targetElm + * @method _startHint + */ + function _populateHints(targetElm) { + var self = this; + this._introItems = []; + + if (this._options.hints) { + for (var i = 0, l = this._options.hints.length; i < l; i++) { + var currentItem = _cloneObject(this._options.hints[i]); + + if (typeof(currentItem.element) === 'string') { + //grab the element with given selector from the page + currentItem.element = document.querySelector(currentItem.element); + } + + currentItem.hintPosition = currentItem.hintPosition || this._options.hintPosition; + currentItem.hintAnimation = currentItem.hintAnimation || this._options.hintAnimation; + + if (currentItem.element != null) { + this._introItems.push(currentItem); + } + } + } else { + var hints = targetElm.querySelectorAll('*[data-hint]'); + + if (hints.length < 1) { + return false; + } + + //first add intro items with data-step + for (var i = 0, l = hints.length; i < l; i++) { + var currentElement = hints[i]; + + // hint animation + var hintAnimation = currentElement.getAttribute('data-hintAnimation'); + + if (hintAnimation) { + hintAnimation = (hintAnimation == 'true'); + } else { + hintAnimation = this._options.hintAnimation; + } + + this._introItems.push({ + element: currentElement, + hint: currentElement.getAttribute('data-hint'), + hintPosition: currentElement.getAttribute('data-hintPosition') || this._options.hintPosition, + hintAnimation: hintAnimation, + tooltipClass: currentElement.getAttribute('data-tooltipClass'), + position: currentElement.getAttribute('data-position') || this._options.tooltipPosition + }); + } + } + + _addHints.call(this); + + if (document.addEventListener) { + document.addEventListener('click', _removeHintTooltip.bind(this), false); + //for window resize + window.addEventListener('resize', _reAlignHints.bind(this), true); + } else if (document.attachEvent) { //IE + //for window resize + document.attachEvent('onclick', _removeHintTooltip.bind(this)); + document.attachEvent('onresize', _reAlignHints.bind(this)); + } + } + + /** + * Re-aligns all hint elements + * + * @api private + * @method _reAlignHints + */ + function _reAlignHints() { + for (var i = 0, l = this._introItems.length; i < l; i++) { + var item = this._introItems[i]; + + if (typeof (item.targetElement) == 'undefined') continue; + + _alignHintPosition.call(this, item.hintPosition, item.element, item.targetElement) + } + } + + /** + * Hide a hint + * + * @api private + * @method _hideHint + */ + function _hideHint(stepId) { + _removeHintTooltip.call(this); + var hint = this._targetElement.querySelector('.introjs-hint[data-step="' + stepId + '"]'); + + if (hint) { + hint.className += ' introjs-hidehint'; + } + + // call the callback function (if any) + if (typeof (this._hintCloseCallback) !== 'undefined') { + this._hintCloseCallback.call(this, stepId); + } + } + + /** + * Hide all hints + * + * @api private + * @method _hideHints + */ + function _hideHints() { + var hints = this._targetElement.querySelectorAll('.introjs-hint'); + + if (hints && hints.length > 0) { + for (var i = 0; i < hints.length; i++) { + _hideHint.call(this, hints[i].getAttribute('data-step')); + } + } + } + + /** + * Show all hints + * + * @api private + * @method _showHints + */ + function _showHints() { + var hints = this._targetElement.querySelectorAll('.introjs-hint'); + + if (hints && hints.length > 0) { + for (var i = 0; i < hints.length; i++) { + _showHint.call(this, hints[i].getAttribute('data-step')); + } + } else { + _populateHints.call(this, this._targetElement); + } + }; + + /** + * Show a hint + * + * @api private + * @method _showHint + */ + function _showHint(stepId) { + var hint = this._targetElement.querySelector('.introjs-hint[data-step="' + stepId + '"]'); + + if (hint) { + hint.className = hint.className.replace(/introjs\-hidehint/g, ''); + } + }; + + /** + * Removes all hint elements on the page + * Useful when you want to destroy the elements and add them again (e.g. a modal or popup) + * + * @api private + * @method _removeHints + */ + function _removeHints() { + var hints = this._targetElement.querySelectorAll('.introjs-hint'); + + if (hints && hints.length > 0) { + for (var i = 0; i < hints.length; i++) { + _removeHint.call(this, hints[i].getAttribute('data-step')); + } + } + }; + + /** + * Remove one single hint element from the page + * Useful when you want to destroy the element and add them again (e.g. a modal or popup) + * Use removeHints if you want to remove all elements. + * + * @api private + * @method _removeHint + */ + function _removeHint(stepId) { + var hint = this._targetElement.querySelector('.introjs-hint[data-step="' + stepId + '"]'); + + if (hint) { + hint.parentNode.removeChild(hint); + } + }; + + /** + * Add all available hints to the page + * + * @api private + * @method _addHints + */ + function _addHints() { + var self = this; + + var oldHintsWrapper = document.querySelector('.introjs-hints'); + + if (oldHintsWrapper != null) { + hintsWrapper = oldHintsWrapper; + } else { + var hintsWrapper = document.createElement('div'); + hintsWrapper.className = 'introjs-hints'; + } + + for (var i = 0, l = this._introItems.length; i < l; i++) { + var item = this._introItems[i]; + + // avoid append a hint twice + if (document.querySelector('.introjs-hint[data-step="' + i + '"]')) + continue; + + var hint = document.createElement('a'); + _setAnchorAsButton(hint); + + (function (hint, item, i) { + // when user clicks on the hint element + hint.onclick = function(e) { + var evt = e ? e : window.event; + if (evt.stopPropagation) evt.stopPropagation(); + if (evt.cancelBubble != null) evt.cancelBubble = true; + + _hintClick.call(self, hint, item, i); + }; + }(hint, item, i)); + + hint.className = 'introjs-hint'; + + if (!item.hintAnimation) { + hint.className += ' introjs-hint-no-anim'; + } + + // hint's position should be fixed if the target element's position is fixed + if (_isFixed(item.element)) { + hint.className += ' introjs-fixedhint'; + } + + var hintDot = document.createElement('div'); + hintDot.className = 'introjs-hint-dot'; + var hintPulse = document.createElement('div'); + hintPulse.className = 'introjs-hint-pulse'; + + hint.appendChild(hintDot); + hint.appendChild(hintPulse); + hint.setAttribute('data-step', i); + + // we swap the hint element with target element + // because _setHelperLayerPosition uses `element` property + item.targetElement = item.element; + item.element = hint; + + // align the hint position + _alignHintPosition.call(this, item.hintPosition, hint, item.targetElement); + + hintsWrapper.appendChild(hint); + } + + // adding the hints wrapper + document.body.appendChild(hintsWrapper); + + // call the callback function (if any) + if (typeof (this._hintsAddedCallback) !== 'undefined') { + this._hintsAddedCallback.call(this); + } + } + + /** + * Aligns hint position + * + * @api private + * @method _alignHintPosition + * @param {String} position + * @param {Object} hint + * @param {Object} element + */ + function _alignHintPosition(position, hint, element) { + // get/calculate offset of target element + var offset = _getOffset.call(this, element); + var iconWidth = 20; + var iconHeight = 20; + + // align the hint element + switch (position) { + default: + case 'top-left': + hint.style.left = offset.left + 'px'; + hint.style.top = offset.top + 'px'; + break; + case 'top-right': + hint.style.left = (offset.left + offset.width - iconWidth) + 'px'; + hint.style.top = offset.top + 'px'; + break; + case 'bottom-left': + hint.style.left = offset.left + 'px'; + hint.style.top = (offset.top + offset.height - iconHeight) + 'px'; + break; + case 'bottom-right': + hint.style.left = (offset.left + offset.width - iconWidth) + 'px'; + hint.style.top = (offset.top + offset.height - iconHeight) + 'px'; + break; + case 'middle-left': + hint.style.left = offset.left + 'px'; + hint.style.top = (offset.top + (offset.height - iconHeight) / 2) + 'px'; + break; + case 'middle-right': + hint.style.left = (offset.left + offset.width - iconWidth) + 'px'; + hint.style.top = (offset.top + (offset.height - iconHeight) / 2) + 'px'; + break; + case 'middle-middle': + hint.style.left = (offset.left + (offset.width - iconWidth) / 2) + 'px'; + hint.style.top = (offset.top + (offset.height - iconHeight) / 2) + 'px'; + break; + case 'bottom-middle': + hint.style.left = (offset.left + (offset.width - iconWidth) / 2) + 'px'; + hint.style.top = (offset.top + offset.height - iconHeight) + 'px'; + break; + case 'top-middle': + hint.style.left = (offset.left + (offset.width - iconWidth) / 2) + 'px'; + hint.style.top = offset.top + 'px'; + break; + } + } + + /** + * Triggers when user clicks on the hint element + * + * @api private + * @method _hintClick + * @param {Object} hintElement + * @param {Object} item + * @param {Number} stepId + */ + function _hintClick(hintElement, item, stepId) { + // call the callback function (if any) + if (typeof (this._hintClickCallback) !== 'undefined') { + this._hintClickCallback.call(this, hintElement, item, stepId); + } + + // remove all open tooltips + var removedStep = _removeHintTooltip.call(this); + + // to toggle the tooltip + if (parseInt(removedStep, 10) == stepId) { + return; + } + + var tooltipLayer = document.createElement('div'); + var tooltipTextLayer = document.createElement('div'); + var arrowLayer = document.createElement('div'); + var referenceLayer = document.createElement('div'); + + tooltipLayer.className = 'introjs-tooltip'; + + tooltipLayer.onclick = function (e) { + //IE9 & Other Browsers + if (e.stopPropagation) { + e.stopPropagation(); + } + //IE8 and Lower + else { + e.cancelBubble = true; + } + }; + + tooltipTextLayer.className = 'introjs-tooltiptext'; + + var tooltipWrapper = document.createElement('p'); + tooltipWrapper.innerHTML = item.hint; + + var closeButton = document.createElement('a'); + closeButton.className = 'introjs-button'; + closeButton.innerHTML = this._options.hintButtonLabel; + closeButton.onclick = _hideHint.bind(this, stepId); + + tooltipTextLayer.appendChild(tooltipWrapper); + tooltipTextLayer.appendChild(closeButton); + + arrowLayer.className = 'introjs-arrow'; + tooltipLayer.appendChild(arrowLayer); + + tooltipLayer.appendChild(tooltipTextLayer); + + // set current step for _placeTooltip function + this._currentStep = hintElement.getAttribute('data-step'); + + // align reference layer position + referenceLayer.className = 'introjs-tooltipReferenceLayer introjs-hintReference'; + referenceLayer.setAttribute('data-step', hintElement.getAttribute('data-step')); + _setHelperLayerPosition.call(this, referenceLayer); + + referenceLayer.appendChild(tooltipLayer); + document.body.appendChild(referenceLayer); + + //set proper position + _placeTooltip.call(this, hintElement, tooltipLayer, arrowLayer, null, true); + } + + /** + * Get an element position on the page + * Thanks to `meouw`: http://stackoverflow.com/a/442474/375966 + * + * @api private + * @method _getOffset + * @param {Object} element + * @returns Element's position info + */ + function _getOffset(element) { + var elementPosition = {}; + + var body = document.body; + var docEl = document.documentElement; + + var scrollTop = window.pageYOffset || docEl.scrollTop || body.scrollTop; + var scrollLeft = window.pageXOffset || docEl.scrollLeft || body.scrollLeft; + + if (element instanceof SVGElement) { + var x = element.getBoundingClientRect() + elementPosition.top = x.top + scrollTop; + elementPosition.width = x.width; + elementPosition.height = x.height; + elementPosition.left = x.left + scrollLeft; + } else { + //set width + elementPosition.width = element.offsetWidth; + + //set height + elementPosition.height = element.offsetHeight; + + //calculate element top and left + var _x = 0; + var _y = 0; + while (element && !isNaN(element.offsetLeft) && !isNaN(element.offsetTop)) { + _x += element.offsetLeft; + _y += element.offsetTop; + element = element.offsetParent; + } + //set top + elementPosition.top = _y; + //set left + elementPosition.left = _x; + } + + return elementPosition; + } + + /** + * Gets the current progress percentage + * + * @api private + * @method _getProgress + * @returns current progress percentage + */ + function _getProgress() { + // Steps are 0 indexed + var currentStep = parseInt((this._currentStep + 1), 10); + return ((currentStep / this._introItems.length) * 100); + } + + /** + * Overwrites obj1's values with obj2's and adds obj2's if non existent in obj1 + * via: http://stackoverflow.com/questions/171251/how-can-i-merge-properties-of-two-javascript-objects-dynamically + * + * @param obj1 + * @param obj2 + * @returns obj3 a new object based on obj1 and obj2 + */ + function _mergeOptions(obj1,obj2) { + var obj3 = {}; + for (var attrname in obj1) { obj3[attrname] = obj1[attrname]; } + for (var attrname in obj2) { obj3[attrname] = obj2[attrname]; } + return obj3; + } + + var introJs = function (targetElm) { + if (typeof (targetElm) === 'object') { + //Ok, create a new instance + return new IntroJs(targetElm); + + } else if (typeof (targetElm) === 'string') { + //select the target element with query selector + var targetElement = document.querySelector(targetElm); + + if (targetElement) { + return new IntroJs(targetElement); + } else { + throw new Error('There is no element with given selector.'); + } + } else { + return new IntroJs(document.body); + } + }; + + /** + * Current IntroJs version + * + * @property version + * @type String + */ + introJs.version = VERSION; + + //Prototype + introJs.fn = IntroJs.prototype = { + clone: function () { + return new IntroJs(this); + }, + setOption: function(option, value) { + this._options[option] = value; + return this; + }, + setOptions: function(options) { + this._options = _mergeOptions(this._options, options); + return this; + }, + start: function () { + _introForElement.call(this, this._targetElement); + return this; + }, + goToStep: function(step) { + _goToStep.call(this, step); + return this; + }, + addStep: function(options) { + if (!this._options.steps) { + this._options.steps = []; + } + + this._options.steps.push(options); + + return this; + }, + addSteps: function(steps) { + if (!steps.length) return; + + for(var index = 0; index < steps.length; index++) { + this.addStep(steps[index]); + } + + return this; + }, + goToStepNumber: function(step) { + _goToStepNumber.call(this, step); + + return this; + }, + nextStep: function() { + _nextStep.call(this); + return this; + }, + previousStep: function() { + _previousStep.call(this); + return this; + }, + exit: function() { + _exitIntro.call(this, this._targetElement); + return this; + }, + refresh: function() { + // re-align intros + _setHelperLayerPosition.call(this, document.querySelector('.introjs-helperLayer')); + _setHelperLayerPosition.call(this, document.querySelector('.introjs-tooltipReferenceLayer')); + + //re-align hints + _reAlignHints.call(this); + return this; + }, + onbeforechange: function(providedCallback) { + if (typeof (providedCallback) === 'function') { + this._introBeforeChangeCallback = providedCallback; + } else { + throw new Error('Provided callback for onbeforechange was not a function'); + } + return this; + }, + onchange: function(providedCallback) { + if (typeof (providedCallback) === 'function') { + this._introChangeCallback = providedCallback; + } else { + throw new Error('Provided callback for onchange was not a function.'); + } + return this; + }, + onafterchange: function(providedCallback) { + if (typeof (providedCallback) === 'function') { + this._introAfterChangeCallback = providedCallback; + } else { + throw new Error('Provided callback for onafterchange was not a function'); + } + return this; + }, + oncomplete: function(providedCallback) { + if (typeof (providedCallback) === 'function') { + this._introCompleteCallback = providedCallback; + } else { + throw new Error('Provided callback for oncomplete was not a function.'); + } + return this; + }, + onhintsadded: function(providedCallback) { + if (typeof (providedCallback) === 'function') { + this._hintsAddedCallback = providedCallback; + } else { + throw new Error('Provided callback for onhintsadded was not a function.'); + } + return this; + }, + onhintclick: function(providedCallback) { + if (typeof (providedCallback) === 'function') { + this._hintClickCallback = providedCallback; + } else { + throw new Error('Provided callback for onhintclick was not a function.'); + } + return this; + }, + onhintclose: function(providedCallback) { + if (typeof (providedCallback) === 'function') { + this._hintCloseCallback = providedCallback; + } else { + throw new Error('Provided callback for onhintclose was not a function.'); + } + return this; + }, + onexit: function(providedCallback) { + if (typeof (providedCallback) === 'function') { + this._introExitCallback = providedCallback; + } else { + throw new Error('Provided callback for onexit was not a function.'); + } + return this; + }, + addHints: function() { + _populateHints.call(this, this._targetElement); + return this; + }, + hideHint: function (stepId) { + _hideHint.call(this, stepId); + return this; + }, + hideHints: function () { + _hideHints.call(this); + return this; + }, + showHint: function (stepId) { + _showHint.call(this, stepId); + return this; + }, + showHints: function () { + _showHints.call(this); + return this; + }, + removeHints: function () { + _removeHints.call(this); + return this; + }, + removeHint: function (stepId) { + _removeHint.call(this, stepId); + return this; + } + }; + + exports.introJs = introJs; + return introJs; +})); From 927197fae1f0a7c16e5012a3301c89fb1a9f67e0 Mon Sep 17 00:00:00 2001 From: Sourav Badami Date: Tue, 6 Jun 2017 22:53:48 +0530 Subject: [PATCH 2/5] Added style changes. --- website/static/css/style.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/website/static/css/style.css b/website/static/css/style.css index 5e001e0a3a..54d5b305c8 100644 --- a/website/static/css/style.css +++ b/website/static/css/style.css @@ -40,6 +40,10 @@ body{ color: #dd4252; } +.start-tour{ + color: #dd4252; +} + a:hover{ color: #009688; } From aa6fa3b20e0f896be5b08a002b25ef9378ed2180 Mon Sep 17 00:00:00 2001 From: Sourav Badami Date: Tue, 6 Jun 2017 22:54:20 +0530 Subject: [PATCH 3/5] Added frontend changes. --- website/static/js/ui.js | 4 ++++ website/templates/base.html | 23 ++++++++++++++--------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/website/static/js/ui.js b/website/static/js/ui.js index 1cacbad834..83cecb7b9e 100644 --- a/website/static/js/ui.js +++ b/website/static/js/ui.js @@ -25,4 +25,8 @@ $(document).ready(function(){ $('.edit-pic').hide(); }); + $("#startTour").click(function() { + introJs().start(); + }); + }); \ No newline at end of file diff --git a/website/templates/base.html b/website/templates/base.html index 906ce13aa1..17716ea205 100644 --- a/website/templates/base.html +++ b/website/templates/base.html @@ -21,8 +21,10 @@ + +