angular.module("personal-banker").directive "interactiveTutorial", (env, $timeout, $document, $q, $rootScope) ->
restrict: "E"
templateUrl: env.templates["amf-ui-library/templates/directives/interactive_tutorial.html"]
link: (scope, elem, attrs) ->
scope.currentStep = 0
scope.$on "it:startTutorial", (e, tutorialData) -> scope.startTutorial(tutorialData)
scope.$on "it:stopTutorial", (e) -> scope.stopTutorial()
scope.$on "it:nextStep", (e) -> scope.nextStep()
scope.$on "it:previousStep", (e) -> scope.previousStep()
scope.startTutorial = (tutorialData) ->
$rootScope.$broadcast "it:tutorialStarted"
scope.tutorialData = tutorialData
scope.tutorialActive = true
scope.currentStep = 0
scope.nextStep()
scope.stopTutorial = ->
scope.tutorial = undefined
scope.tutorialActive = false
scope.showSpotlight = false
scope.showTutorialBox = false
onLeaveHighlightFunc = scope.tutorialData[scope.currentStep - 1].onLeaveHighlight
onLeaveHighlightFunc() if onLeaveHighlightFunc
$rootScope.$broadcast "it:tutorialStopped"
scope.nextStep = ->
scope.currentStep++
if scope.currentStep > scope.tutorialData.length
scope.currentStep--
return
scope.showTutorialBox = false
onLeaveHighlightFunc = scope.tutorialData[scope.currentStep - 2].onLeaveHighlight unless scope.currentStep is 1
onLeaveHighlightFunc() if onLeaveHighlightFunc
data = scope.tutorialData[scope.currentStep - 1]
data = scope.tutorialData[scope.currentStep - 1]
$timeout -> scope.highlightElement data
scope.previousStep = ->
scope.currentStep--
if scope.currentStep is 0
scope.currentStep++
return
onLeaveHighlightFunc = scope.tutorialData[scope.currentStep].onLeaveHighlight
onLeaveHighlightFunc() if onLeaveHighlightFunc
scope.showTutorialBox = false
data = scope.tutorialData[scope.currentStep - 1]
$timeout -> scope.highlightElement data
scope.asyncEnsureSpotInViewport = (scrollTop, pHeight, spot, $mainContentWrapper) ->
deferred = $q.defer()
adjustedValues = {}
scrollDuration = 500
bottomOfSpot = spot.y + (spot.radius * 2)
bottomOfViewport = pHeight + scrollTop
animationOpts =
duration: scrollDuration
complete: -> deferred.resolve(adjustedValues)
# Is spot too high or low? Adjust y accordingly.
scrollTo = scrollTop
if spot.y < 0
adjustedValues.y = 0
scrollTo = scrollTop + spot.y
else if bottomOfSpot > pHeight
adjustedValues.y = pHeight - (spot.radius * 2)
scrollTo = scrollTop + (bottomOfSpot - pHeight)
else
animationOpts.duration = 0
$mainContentWrapper.animate { scrollTop: scrollTo }, animationOpts
return deferred.promise
scope.calculateSpotAdjustments = (adjustments, tMidPtCoords, spot) ->
return spot if adjustments.maxDim >= spot.dim
adjustedSpot = {}
align = adjustments.align or "center"
switch align
when "left"
adjustedSpot.dim = adjustments.maxDim
adjustedSpot.radius = adjustments.maxDim / 2
adjustedSpot.x = spot.x
adjustedSpot.y = spot.y + ((spot.dim / 2) - (adjustments.maxDim / 2))
when "center"
adjustedSpot.dim = adjustments.maxDim
adjustedSpot.radius = adjustments.maxDim / 2
adjustedSpot.x = spot.x + ((spot.dim / 2) - (adjustments.maxDim / 2))
adjustedSpot.y = spot.y + ((spot.dim / 2) - (adjustments.maxDim / 2))
when "right"
adjustedSpot.dim = adjustments.maxDim
adjustedSpot.radius = adjustments.maxDim / 2
adjustedSpot.x = spot.x + spot.dim - adjustments.maxDim
adjustedSpot.y = spot.y + ((spot.dim / 2) - (adjustments.maxDim / 2))
return adjustedSpot
scope.highlightElement = (data) ->
spotPad = 10
$target = $(data.selector).eq(0)
$page = $($document)
$mainContentWrapper = $(".main-app__header-and-content").eq(0)
tWidth = $target.outerWidth()
tHeight = $target.outerHeight()
tOffsetX = $target.offset().left
tOffsetY = $target.offset().top
scrollTop = $mainContentWrapper.scrollTop()
tMidPtCoords = { left: tOffsetX + (tWidth / 2), top: tOffsetY + (tHeight / 2) }
pWidth = $page.width()
pHeight = $page.height()
tRadius = Math.sqrt((Math.pow((tWidth / 2), 2) + Math.pow((tHeight / 2), 2)))
spot = {}
spot.radius = tRadius + spotPad
spot.dim = spot.radius * 2
spot.x = tMidPtCoords.left - spot.radius
spot.y = tMidPtCoords.top - spot.radius
if data.adjustments then spot = scope.calculateSpotAdjustments(data.adjustments, tMidPtCoords, spot)
promiseForSpotInViewport =
scope.asyncEnsureSpotInViewport(scrollTop, pHeight, spot, $mainContentWrapper)
promiseForSpotInViewport.then (adjustedValues) ->
$timeout ->
# NOTE: Falsy values are annyoing; lol@js!
if adjustedValues.y isnt undefined then spot.y = adjustedValues.y
scope.showSpotlight = true
showContent = ->
scope.showTutorialBox = true
data.onHighlight() if data.onHighlight
$timeout showContent, 700
scope.tutorialTarget =
spot: spot
title: data.title
content: data.content
boxLeftWidth = spot.x
boxMiddleWrapperWidth = spot.dim
boxTopHeight = spot.y
boxCenterHeight = spot.dim
boxBottomHeight = pHeight - boxCenterHeight - boxTopHeight
boxRightWidth = pWidth - boxMiddleWrapperWidth - boxLeftWidth
$boxLeft = elem.find(".it__box--left")
$boxMiddleWrapper = elem.find(".it__box-middle-wrapper")
$boxTop = elem.find(".it__box--top")
$boxCenter = elem.find(".it__box--center")
$boxBottom = elem.find(".it__box--bottom")
$boxRight = elem.find(".it__box--right")
$boxLeft.css "width", "#{boxLeftWidth}px"
$boxMiddleWrapper.css "width", "#{boxMiddleWrapperWidth}px"
$boxTop.css "height", "#{boxTopHeight}px"
$boxBottom.css "height", "#{boxBottomHeight}px"
$boxCenter.css "height", "#{boxCenterHeight}px"
$boxRight.css "width", "#{boxRightWidth}px"