‘use strict’;

!function($) {

/**

* Magellan module.
* @module foundation.magellan
*/

class Magellan {

/**
 * Creates a new instance of Magellan.
 * @class
 * @fires Magellan#init
 * @param {Object} element - jQuery object to add the trigger to.
 * @param {Object} options - Overrides to the default plugin settings.
 */
constructor(element, options) {
  this.$element = element;
  this.options  = $.extend({}, Magellan.defaults, this.$element.data(), options);

  this._init();
  this.calcPoints();

  Foundation.registerPlugin(this, 'Magellan');
}

/**
 * Initializes the Magellan plugin and calls functions to get equalizer functioning on load.
 * @private
 */
_init() {
  var id = this.$element[0].id || Foundation.GetYoDigits(6, 'magellan');
  var _this = this;
  this.$targets = $('[data-magellan-target]');
  this.$links = this.$element.find('a');
  this.$element.attr({
    'data-resize': id,
    'data-scroll': id,
    'id': id
  });
  this.$active = $();
  this.scrollPos = parseInt(window.pageYOffset, 10);

  this._events();
}

/**
 * Calculates an array of pixel values that are the demarcation lines between locations on the page.
 * Can be invoked if new elements are added or the size of a location changes.
 * @function
 */
calcPoints() {
  var _this = this,
      body = document.body,
      html = document.documentElement;

  this.points = [];
  this.winHeight = Math.round(Math.max(window.innerHeight, html.clientHeight));
  this.docHeight = Math.round(Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight));

  this.$targets.each(function(){
    var $tar = $(this),
        pt = Math.round($tar.offset().top - _this.options.threshold);
    $tar.targetPoint = pt;
    _this.points.push(pt);
  });
}

/**
 * Initializes events for Magellan.
 * @private
 */
_events() {
  var _this = this,
      $body = $('html, body'),
      opts = {
        duration: _this.options.animationDuration,
        easing:   _this.options.animationEasing
      };
  $(window).one('load', function(){
    if(_this.options.deepLinking){
      if(location.hash){
        _this.scrollToLoc(location.hash);
      }
    }
    _this.calcPoints();
    _this._updateActive();
  });

  this.$element.on({
    'resizeme.zf.trigger': this.reflow.bind(this),
    'scrollme.zf.trigger': this._updateActive.bind(this)
  }).on('click.zf.magellan', 'a[href^="#"]', function(e) {
      e.preventDefault();
      var arrival   = this.getAttribute('href');
      _this.scrollToLoc(arrival);
    });
  $(window).on('popstate', function(e) {
    if(_this.options.deepLinking) {
      _this.scrollToLoc(window.location.hash);
    }
  });
}

/**
 * Function to scroll to a given location on the page.
 * @param {String} loc - a properly formatted jQuery id selector. Example: '#foo'
 * @function
 */
scrollToLoc(loc) {
  // Do nothing if target does not exist to prevent errors
  if (!$(loc).length) {return false;}
  this._inTransition = true;
  var _this = this,
      scrollPos = Math.round($(loc).offset().top - this.options.threshold / 2 - this.options.barOffset);

  $('html, body').stop(true).animate(
    { scrollTop: scrollPos },
    this.options.animationDuration,
    this.options.animationEasing,
    function() {_this._inTransition = false; _this._updateActive()}
  );
}

/**
 * Calls necessary functions to update Magellan upon DOM change
 * @function
 */
reflow() {
  this.calcPoints();
  this._updateActive();
}

/**
 * Updates the visibility of an active location link, and updates the url hash for the page, if deepLinking enabled.
 * @private
 * @function
 * @fires Magellan#update
 */
_updateActive(/*evt, elem, scrollPos*/) {
  if(this._inTransition) {return;}
  var winPos = /*scrollPos ||*/ parseInt(window.pageYOffset, 10),
      curIdx;

  if(winPos + this.winHeight === this.docHeight){ curIdx = this.points.length - 1; }
  else if(winPos < this.points[0]){ curIdx = undefined; }
  else{
    var isDown = this.scrollPos < winPos,
        _this = this,
        curVisible = this.points.filter(function(p, i){
          return isDown ? p - _this.options.barOffset <= winPos : p - _this.options.barOffset - _this.options.threshold <= winPos;
        });
    curIdx = curVisible.length ? curVisible.length - 1 : 0;
  }

  this.$active.removeClass(this.options.activeClass);
  this.$active = this.$links.filter('[href="#' + this.$targets.eq(curIdx).data('magellan-target') + '"]').addClass(this.options.activeClass);

  if(this.options.deepLinking){
    var hash = "";
    if(curIdx != undefined){
      hash = this.$active[0].getAttribute('href');
    }
    if(hash !== window.location.hash) {
      if(window.history.pushState){
        window.history.pushState(null, null, hash);
      }else{
        window.location.hash = hash;
      }
    }
  }

  this.scrollPos = winPos;
  /**
   * Fires when magellan is finished updating to the new active element.
   * @event Magellan#update
   */
  this.$element.trigger('update.zf.magellan', [this.$active]);
}

/**
 * Destroys an instance of Magellan and resets the url of the window.
 * @function
 */
destroy() {
  this.$element.off('.zf.trigger .zf.magellan')
      .find(`.${this.options.activeClass}`).removeClass(this.options.activeClass);

  if(this.options.deepLinking){
    var hash = this.$active[0].getAttribute('href');
    window.location.hash.replace(hash, '');
  }

  Foundation.unregisterPlugin(this);
}

}

/**

* Default settings for plugin
*/

Magellan.defaults = {

/**
 * Amount of time, in ms, the animated scrolling should take between locations.
 * @option
 * @type {number}
 * @default 500
 */
animationDuration: 500,
/**
 * Animation style to use when scrolling between locations. Can be `'swing'` or `'linear'`.
 * @option
 * @type {string}
 * @default 'linear'
 * @see {@link https://api.jquery.com/animate|Jquery animate}
 */
animationEasing: 'linear',
/**
 * Number of pixels to use as a marker for location changes.
 * @option
 * @type {number}
 * @default 50
 */
threshold: 50,
/**
 * Class applied to the active locations link on the magellan container.
 * @option
 * @type {string}
 * @default 'active'
 */
activeClass: 'active',
/**
 * Allows the script to manipulate the url of the current page, and if supported, alter the history.
 * @option
 * @type {boolean}
 * @default false
 */
deepLinking: false,
/**
 * Number of pixels to offset the scroll of the page on item click if using a sticky nav bar.
 * @option
 * @type {number}
 * @default 0
 */
barOffset: 0

}

// Window exports Foundation.plugin(Magellan, ‘Magellan’);

}(jQuery);