RED.tourGuide = (function() { var activeListeners = []; var shade; var focus; var popover; var stepContent; var targetElement; var fullscreen; var tourCache = {}; function run(tourPath, done) { done = done || function(err) { if (err) { console.error(err); } }; loadTour(tourPath, function(err, tour) { if (err) { console.warn("Error loading tour:",err); return; } runTour(tour, done); }) } function loadTour(tourPath, done) { if (tourCache[tourPath]) { done(null, tourCache[tourPath]); } else { /* jshint ignore:start */ // jshint<2.13 doesn't support dynamic imports. Once grunt-contrib-jshint // has been updated with the new jshint, we can stop ignoring this block import(tourPath).then(function(module) { tourCache[tourPath] = module.default; done(null, tourCache[tourPath]); }).catch(function(err) { done(err); }) /* jshint ignore:end */ } } function repositionFocus() { if (targetElement) { if (!fullscreen) { var pos = targetElement[0].getBoundingClientRect(); var dimension = Math.max(50, Math.max(pos.width,pos.height)*1.5); focus.css({ left: (pos.left+pos.width/2)+"px", top: (pos.top+pos.height/2)+"px", width: (2*dimension)+"px", height: (2*dimension)+"px" }) var flush = focus[0].offsetHeight; // Flush CSS changes focus.addClass("transition"); focus.css({ width: dimension+"px", height: dimension+"px" }) } else { focus.css({ left: ($(window).width()/2)+"px", top: ($(window).height()/2)+"px", width: "0px", height: "0px" }) } if (popover) { popover.move({ target: targetElement, }) } } } function runTour(tour, done) { shade = $('
').appendTo(document.body); focus = $('
').appendTo(shade); // var resizeTimer; // $(window).on("resize.red-ui-tourGuide", function() { repositionFocus(); }) var i = 0; var state = { index: 0, count: tour.steps.length }; function endTour(err) { $(window).off("resize.red-ui-tourGuide"); $(document).off('keydown.red-ui-tourGuide'); if (popover) { popover.close(); } stepContent = null; popover = null; shade.remove(); shade = null; done(err); } function runStep(carryOn) { if (carryOn === false) { endTour(false); return; } if (i === tour.steps.length) { endTour(); return } state.index = i; // console.log("TOUR STEP",i+1,"OF",tour.steps.length) try { runTourStep(tour.steps[i++], state, runStep) } catch(err) { endTour(err); return; } } runStep(); } function clearListeners() { activeListeners.forEach(function(listener) { if (listener.type === "dom-event") { listener.target[0].removeEventListener(listener.event,listener.listener,listener.opts); } else if (listener.type === "nr-event") { RED.events.off(listener.event, listener.listener) } }) activeListeners = []; } function prepareStep(step, state, done) { if (step.prepare) { if (step.prepare.length === 0) { step.prepare.call(state); } else { if (popover) { popover.element.hide(); if (!fullscreen) { fullscreen = true; repositionFocus() } } step.prepare.call(state, function() { if (popover) { popover.element.show(); } done(); }) return; } } done(); } function completeStep(step, state, done) { function finish() { clearListeners(); setTimeout(function() { done(); },0) } if (step.complete) { if (step.complete.length === 0) { step.complete.call(state); } else { if (popover) { popover.element.hide(); if (!fullscreen) { fullscreen = true; repositionFocus() } } step.complete.call(state, function() { if (popover) { popover.element.show(); } finish(); }) return; } } finish(); } function getLocaleText(property) { if (typeof property === 'string') { return property; } var currentLang = RED.i18n.lang() || 'en-US'; var availableLangs = Object.keys(property); return property[currentLang]||property['en-US']||property[availableLangs[0]] } function runTourStep(step, state, done) { shade.fadeIn(); prepareStep(step, state, function() { var zIndex; var direction = step.direction || "bottom"; fullscreen = false; if (typeof step.element === "string") { targetElement = $(step.element) } else if (typeof step.element === "function") { targetElement = step.element.call(state); } else if (!step.element) { targetElement = $(".red-ui-editor") fullscreen = true; direction = "inset"; } else { targetElement = step.element; } if (targetElement.length === 0) { targetElement = null; shade.hide(); throw new Error("Element not found") } if ($(window).width() < 400) { targetElement = $(".red-ui-editor"); fullscreen = true; direction = "inset"; } zIndex = targetElement.css("z-index"); if (!fullscreen && (step.interactive || step.wait)) { targetElement.css("z-index",2002); } repositionFocus(); if (!stepContent) { stepContent = $('
'); } else { stepContent.empty(); } $('').appendTo(stepContent).click(function(evt) { evt.preventDefault(); completeStep(step, state, function() { done(false); }); }) var stepDescription = $('
').appendTo(stepContent); if (step.titleIcon) { $('

').appendTo(stepDescription); } if (step.title) { $('

').text(getLocaleText(step.title)).appendTo(stepDescription); } $('
').css("text-align","left").html(getLocaleText(step.description)).appendTo(stepDescription); var stepToolbar = $('
',{class:"red-ui-tourGuide-toolbar"}).appendTo(stepContent); // var breadcrumbs = $('
',{class:"red-ui-tourGuide-breadcrumbs"}).appendTo(stepToolbar); // var bcStart = Math.max(0,state.index - 3); // var bcEnd = Math.min(state.count, bcStart + 7); // if (bcEnd === state.count) { // bcStart = Math.max(0,bcEnd - 7); // } // for (var i = bcStart; i < bcEnd; i++) { // var bullet = $('').addClass(i===state.index ? "fa-circle":"fa-circle-o").appendTo(breadcrumbs); // if (i === bcStart) { // if (i > 1) { // bullet.css("font-size", "3px"); // } else if (i === 1) { // bullet.css("font-size", "4px"); // } // } else if (i === bcStart + 1) { // if (i > 2) { // bullet.css("font-size", "4px"); // } // } // if (i === bcEnd - 1) { // if (i < state.count - 2) { // bullet.css("font-size", "3px"); // } else if (i === state.count - 2) { // bullet.css("font-size", "4px"); // } // } else if (i === bcEnd - 2) { // if (i < state.count - 3) { // bullet.css("font-size", "4px"); // } // } // // if (i === bcEnd - 1) { // // if (i < state.count - 2) { // // bullet.css("font-size", "3px"); // // } else if (i === state.count - 2) { // // bullet.css("font-size", "4px"); // // } // // } // } $('').text((state.index+1)+"/"+state.count).appendTo(stepToolbar) var nextButton; if (fullscreen || !step.wait) { nextButton = $('').appendTo(stepToolbar).one('click',function(evt) { evt.preventDefault(); stepEventListener(); }); if (state.index === state.count - 1) { $('').text(RED._("common.label.close")).appendTo(nextButton); } else if (state.index === 0) { $('start').text(RED._("tourGuide.start")).appendTo(nextButton); $('').appendTo(nextButton); } else if (state.index < state.count-1) { $('').text(RED._("tourGuide.next")).appendTo(nextButton); $('').appendTo(nextButton); } } var width = step.width; if (fullscreen) { width = 500; } var maxWidth = Math.min($(window).width()-10,Math.max(width || 0, 300)); if (!popover) { popover = RED.popover.create({ target: targetElement, width: width || "auto", maxWidth: maxWidth+"px", direction: direction, class: "red-ui-tourGuide-popover"+(fullscreen?" ":""), trigger: "manual", content: stepContent }).open(); } $(document).off('keydown.red-ui-tourGuide'); $(document).on('keydown.red-ui-tourGuide', function(evt) { if (evt.key === "Escape" || evt.key === "Esc") { evt.preventDefault(); evt.stopPropagation(); completeStep(step, state, function() { done(false); }); } }) popover.element.toggleClass("red-ui-tourGuide-popover-full",!!fullscreen); popover.move({ target: targetElement, width: width || "auto", maxWidth: maxWidth+"px", direction: direction, }) setTimeout(function() { var pos = popover.element.position() if (pos.left < 0) { popover.element.css({left: 0}); } },100); if (nextButton) { setTimeout(function() { nextButton.focus(); },100); } var isSVG = targetElement[0] instanceof SVGElement; if (step.fallback) { focus.one("mouseenter", function(evt) { setTimeout(function() { focus.css({ width: (4*dimension)+"px", height: (4*dimension)+"px" }) shade.fadeOut(); popover.move({ target: $(".red-ui-editor"), direction: step.fallback, offset: 10, transition: true }) // popover.element.addClass('red-ui-tourGuide-popover-bounce'); },isSVG?0:500); }) } var stepEventListener = function() { focus.removeClass("transition"); targetElement.css("z-index",zIndex); completeStep(step, state, done); } if (step.wait) { if (step.wait.type === "dom-event") { var eventTarget = targetElement; if (step.wait.element) { if (typeof step.wait.element === "string") { eventTarget = $(step.wait.element); } else if (typeof step.wait.element === "function") { eventTarget = step.wait.element.call(state); } } var listener = { type: step.wait.type, target: eventTarget, event: step.wait.event, listener: function() { stepEventListener(); }, opts: { once: true } } activeListeners.push(listener) eventTarget[0].addEventListener(listener.event,listener.listener,listener.opts) } else if (step.wait.type === "nr-event") { var listener = { type: step.wait.type, event: step.wait.event, listener: function() { if (step.wait.filter) { if (!step.wait.filter.apply(state,arguments)) { return; } } stepEventListener(); } } activeListeners.push(listener); RED.events.on(listener.event,listener.listener); } } }) } return { load: loadTour, run: run, reset: function() { RED.settings.set("editor.tours.welcome",''); } } })();