;(function ($, window, document, undefined) {

'use strict';

Tennpipes.libs['magellan-expedition'] = {
  name : 'magellan-expedition',

  version : '5.5.1',

  settings : {
    active_class : 'active',
    threshold : 0, // pixels from the top of the expedition for it to become fixes
    destination_threshold : 20, // pixels from the top of destination for it to be considered active
    throttle_delay : 30, // calculation throttling to increase framerate
    fixed_top : 0, // top distance in pixels assigend to the fixed element on scroll
    offset_by_height : true,  // whether to offset the destination by the expedition height. Usually you want this to be true, unless your expedition is on the side.
    duration : 700, // animation duration time
    easing : 'swing' // animation easing
  },

  init : function (scope, method, options) {
    Tennpipes.inherit(this, 'throttle');
    this.bindings(method, options);
  },

  events : function () {
    var self = this,
        S = self.S,
        settings = self.settings;

    // initialize expedition offset
    self.set_expedition_position();

    S(self.scope)
      .off('.magellan')
      .on('click.fndtn.magellan', '[' + self.add_namespace('data-magellan-arrival') + '] a[href^="#"]', function (e) {
        e.preventDefault();
        var expedition = $(this).closest('[' + self.attr_name() + ']'),
            settings = expedition.data('magellan-expedition-init'),
            hash = this.hash.split('#').join(''),
            target = $('a[name="' + hash + '"]');

        if (target.length === 0) {
          target = $('#' + hash);

        }

        // Account for expedition height if fixed position
        var scroll_top = target.offset().top - settings.destination_threshold + 1;
        if (settings.offset_by_height) {
          scroll_top = scroll_top - expedition.outerHeight();
        }

        $('html, body').stop().animate({
          'scrollTop' : scroll_top
        }, settings.duration, settings.easing, function () {
          if (history.pushState) {
            history.pushState(null, null, '#' + hash);
          } else {
            location.hash = '#' + hash;
          }
        });
      })
      .on('scroll.fndtn.magellan', self.throttle(this.check_for_arrivals.bind(this), settings.throttle_delay));

    $(window)
      .on('resize.fndtn.magellan', self.throttle(this.set_expedition_position.bind(this), settings.throttle_delay));
  },

  check_for_arrivals : function () {
    var self = this;
    self.update_arrivals();
    self.update_expedition_positions();
  },

  set_expedition_position : function () {
    var self = this;
    $('[' + this.attr_name() + '=fixed]', self.scope).each(function (idx, el) {
      var expedition = $(this),
          settings = expedition.data('magellan-expedition-init'),
          styles = expedition.attr('styles'), // save styles
          top_offset, fixed_top;

      expedition.attr('style', '');
      top_offset = expedition.offset().top + settings.threshold;

      //set fixed-top by attribute
      fixed_top = parseInt(expedition.data('magellan-fixed-top'));
      if (!isNaN(fixed_top)) {
        self.settings.fixed_top = fixed_top;
      }

      expedition.data(self.data_attr('magellan-top-offset'), top_offset);
      expedition.attr('style', styles);
    });
  },

  update_expedition_positions : function () {
    var self = this,
        window_top_offset = $(window).scrollTop();

    $('[' + this.attr_name() + '=fixed]', self.scope).each(function () {
      var expedition = $(this),
          settings = expedition.data('magellan-expedition-init'),
          styles = expedition.attr('style'), // save styles
          top_offset = expedition.data('magellan-top-offset');

      //scroll to the top distance
      if (window_top_offset + self.settings.fixed_top >= top_offset) {
        // Placeholder allows height calculations to be consistent even when
        // appearing to switch between fixed/non-fixed placement
        var placeholder = expedition.prev('[' + self.add_namespace('data-magellan-expedition-clone') + ']');
        if (placeholder.length === 0) {
          placeholder = expedition.clone();
          placeholder.removeAttr(self.attr_name());
          placeholder.attr(self.add_namespace('data-magellan-expedition-clone'), '');
          expedition.before(placeholder);
        }
        expedition.css({position :'fixed', top : settings.fixed_top}).addClass('fixed');
      } else {
        expedition.prev('[' + self.add_namespace('data-magellan-expedition-clone') + ']').remove();
        expedition.attr('style', styles).css('position', '').css('top', '').removeClass('fixed');
      }
    });
  },

  update_arrivals : function () {
    var self = this,
        window_top_offset = $(window).scrollTop();

    $('[' + this.attr_name() + ']', self.scope).each(function () {
      var expedition = $(this),
          settings = expedition.data(self.attr_name(true) + '-init'),
          offsets = self.offsets(expedition, window_top_offset),
          arrivals = expedition.find('[' + self.add_namespace('data-magellan-arrival') + ']'),
          active_item = false;
      offsets.each(function (idx, item) {
        if (item.viewport_offset >= item.top_offset) {
          var arrivals = expedition.find('[' + self.add_namespace('data-magellan-arrival') + ']');
          arrivals.not(item.arrival).removeClass(settings.active_class);
          item.arrival.addClass(settings.active_class);
          active_item = true;
          return true;
        }
      });

      if (!active_item) {
        arrivals.removeClass(settings.active_class);
      }
    });
  },

  offsets : function (expedition, window_offset) {
    var self = this,
        settings = expedition.data(self.attr_name(true) + '-init'),
        viewport_offset = window_offset;

    return expedition.find('[' + self.add_namespace('data-magellan-arrival') + ']').map(function (idx, el) {
      var name = $(this).data(self.data_attr('magellan-arrival')),
          dest = $('[' + self.add_namespace('data-magellan-destination') + '=' + name + ']');
      if (dest.length > 0) {
        var top_offset = dest.offset().top - settings.destination_threshold;
        if (settings.offset_by_height) {
          top_offset = top_offset - expedition.outerHeight();
        }
        top_offset = Math.floor(top_offset);
        return {
          destination : dest,
          arrival : $(this),
          top_offset : top_offset,
          viewport_offset : viewport_offset
        }
      }
    }).sort(function (a, b) {
      if (a.top_offset < b.top_offset) {
        return -1;
      }
      if (a.top_offset > b.top_offset) {
        return 1;
      }
      return 0;
    });
  },

  data_attr : function (str) {
    if (this.namespace.length > 0) {
      return this.namespace + '-' + str;
    }

    return str;
  },

  off : function () {
    this.S(this.scope).off('.magellan');
    this.S(window).off('.magellan');
  },

  reflow : function () {
    var self = this;
    // remove placeholder expeditions used for height calculation purposes
    $('[' + self.add_namespace('data-magellan-expedition-clone') + ']', self.scope).remove();
  }
};

}(jQuery, window, window.document));