KamaKAzii
10/11/2016 - 12:45 AM

yo.coffee


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"