text: 'use strict';
  
  import $ from 'jquery';
  import { Plugin } from './foundation.core.plugin';
  import { onLoad } from './foundation.core.utils';
  import { Keyboard } from './foundation.util.keyboard';
  import { onImagesLoaded } from './foundation.util.imageLoader';
  /**
   * Tabs module.
   * @module foundation.tabs
   * @requires foundation.util.keyboard
   * @requires foundation.util.imageLoader if tabs contain images
   */
  
  class Tabs extends Plugin {
    /**
     * Creates a new instance of tabs.
     * @class
     * @name Tabs
     * @fires Tabs#init
     * @param {jQuery} element - jQuery object to make into tabs.
     * @param {Object} options - Overrides to the default plugin settings.
     */
    _setup(element, options) {
      this.$element = element;
      this.options = $.extend({}, Tabs.defaults, this.$element.data(), options);
      this.className = 'Tabs'; // ie9 back compat
  
      this._init();
      Keyboard.register('Tabs', {
        'ENTER': 'open',
        'SPACE': 'open',
        'ARROW_RIGHT': 'next',
        'ARROW_UP': 'previous',
        'ARROW_DOWN': 'next',
        'ARROW_LEFT': 'previous'
        // 'TAB': 'next',
        // 'SHIFT_TAB': 'previous'
      });
    }
  
    /**
     * Initializes the tabs by showing and focusing (if autoFocus=true) the preset active tab.
     * @private
     */
    _init() {
      var _this = this;
      this._isInitializing = true;
  
      this.$element.attr({'role': 'tablist'});
      this.$tabTitles = this.$element.find(`.${this.options.linkClass}`);
      this.$tabContent = $(`[data-tabs-content="${this.$element[0].id}"]`);
  
      this.$tabTitles.each(function(){
        var $elem = $(this),
            $link = $elem.find('a'),
            isActive = $elem.hasClass(`${_this.options.linkActiveClass}`),
            hash = $link.attr('data-tabs-target') || $link[0].hash.slice(1),
            linkId = $link[0].id ? $link[0].id : `${hash}-label`,
            $tabContent = $(`#${hash}`);
  
        $elem.attr({'role': 'presentation'});
  
        $link.attr({
          'role': 'tab',
          'aria-controls': hash,
          'aria-selected': isActive,
          'id': linkId,
          'tabindex': isActive ? '0' : '-1'
        });
  
        $tabContent.attr({
          'role': 'tabpanel',
          'aria-labelledby': linkId
        });
  
        // Save up the initial hash to return to it later when going back in history
        if (isActive) {
          _this._initialAnchor = `#${hash}`;
        }
  
        if(!isActive) {
          $tabContent.attr('aria-hidden', 'true');
        }
  
        if(isActive && _this.options.autoFocus){
          _this.onLoadListener = onLoad($(window), function() {
            $('html, body').animate({ scrollTop: $elem.offset().top }, _this.options.deepLinkSmudgeDelay, () => {
              $link.focus();
            });
          });
        }
      });
  
      if(this.options.matchHeight) {
        var $images = this.$tabContent.find('img');
  
        if ($images.length) {
          onImagesLoaded($images, this._setHeight.bind(this));
        } else {
          this._setHeight();
        }
      }
  
       // Current context-bound function to open tabs on page load or history hashchange
      this._checkDeepLink = () => {
        var anchor = window.location.hash;
  
        if (!anchor.length) {
          // If we are still initializing and there is no anchor, then there is nothing to do
          if (this._isInitializing) return;
          // Otherwise, move to the initial anchor
          if (this._initialAnchor) anchor = this._initialAnchor;
        }
  
        var anchorNoHash = anchor.indexOf('#') >= 0 ? anchor.slice(1) : anchor;
        var $anchor = anchorNoHash && $(`#${anchorNoHash}`);
        var $link = anchor && this.$element.find(`[href$="${anchor}"],[data-tabs-target="${anchorNoHash}"]`).first();
        // Whether the anchor element that has been found is part of this element
        var isOwnAnchor = !!($anchor.length && $link.length);
  
        if (isOwnAnchor) {
          // If there is an anchor for the hash, select it
          if ($anchor && $anchor.length && $link && $link.length) {
            this.selectTab($anchor, true);
          }
          // Otherwise, collapse everything
          else {
            this._collapse();
          }
  
          // Roll up a little to show the titles
          if (this.options.deepLinkSmudge) {
            var offset = this.$element.offset();
            $('html, body').animate({ scrollTop: offset.top }, this.options.deepLinkSmudgeDelay);
          }
  
          /**
           * Fires when the plugin has deeplinked at pageload
           * @event Tabs#deeplink
           */
          this.$element.trigger('deeplink.zf.tabs', [$link, $anchor]);
        }
      }
  
      //use browser to open a tab, if it exists in this tabset
      if (this.options.deepLink) {
        this._checkDeepLink();
      }
  
      this._events();
  
      this._isInitializing = false;
    }
  
    /**
     * Adds event handlers for items within the tabs.
     * @private
     */
    _events() {
      this._addKeyHandler();
      this._addClickHandler();
      this._setHeightMqHandler = null;
  
      if (this.options.matchHeight) {
        this._setHeightMqHandler = this._setHeight.bind(this);
  
        $(window).on('changed.zf.mediaquery', this._setHeightMqHandler);
      }
  
      if(this.options.deepLink) {
        $(window).on('hashchange', this._checkDeepLink);
      }
    }
  
    /**
     * Adds click handlers for items within the tabs.
     * @private
     */
    _addClickHandler() {
      var _this = this;
  
      this.$element
        .off('click.zf.tabs')
        .on('click.zf.tabs', `.${this.options.linkClass}`, function(e){
          e.preventDefault();
          _this._handleTabChange($(this));
        });
    }
  
    /**
     * Adds keyboard event handlers for items within the tabs.
     * @private
     */
    _addKeyHandler() {
      var _this = this;
  
      this.$tabTitles.off('keydown.zf.tabs').on('keydown.zf.tabs', function(e){
        if (e.which === 9) return;
  
  
        var $element = $(this),
          $elements = $element.parent('ul').children('li'),
          $prevElement,
          $nextElement;
  
        $elements.each(function(i) {
          if ($(this).is($element)) {
            if (_this.options.wrapOnKeys) {
              $prevElement = i === 0 ? $elements.last() : $elements.eq(i-1);
              $nextElement = i === $elements.length -1 ? $elements.first() : $elements.eq(i+1);
            } else {
              $prevElement = $elements.eq(Math.max(0, i-1));
              $nextElement = $elements.eq(Math.min(i+1, $elements.length-1));
            }
            return;
          }
        });
  
        // handle keyboard event with keyboard util
        Keyboard.handleKey(e, 'Tabs', {
          open: function() {
            $element.find('[role="tab"]').focus();
            _this._handleTabChange($element);
          },
          previous: function() {
            $prevElement.find('[role="tab"]').focus();
            _this._handleTabChange($prevElement);
          },
          next: function() {
            $nextElement.find('[role="tab"]').focus();
            _this._handleTabChange($nextElement);
          },
          handled: function() {
            e.preventDefault();
          }
        });
      });
    }
  
    /**
     * Opens the tab `$targetContent` defined by `$target`. Collapses active tab.
     * @param {jQuery} $target - Tab to open.
     * @param {boolean} historyHandled - browser has already handled a history update
     * @fires Tabs#change
     * @function
     */
    _handleTabChange($target, historyHandled) {
  
      // With `activeCollapse`, if the target is the active Tab, collapse it.
      if ($target.hasClass(`${this.options.linkActiveClass}`)) {
          if(this.options.activeCollapse) {
              this._collapse();
          }
          return;
      }
  
      var $oldTab = this.$element.
            find(`.${this.options.linkClass}.${this.options.linkActiveClass}`),
            $tabLink = $target.find('[role="tab"]'),
            target = $tabLink.attr('data-tabs-target'),
            anchor = target && target.length ? `#${target}` : $tabLink[0].hash,
            $targetContent = this.$tabContent.find(anchor);
  
      //close old tab
      this._collapseTab($oldTab);
  
      //open new tab
      this._openTab($target);
  
      //either replace or update browser history
      if (this.options.deepLink && !historyHandled) {
        if (this.options.updateHistory) {
          history.pushState({}, '', anchor);
        } else {
          history.replaceState({}, '', anchor);
        }
      }
  
      /**
       * Fires when the plugin has successfully changed tabs.
       * @event Tabs#change
       */
      this.$element.trigger('change.zf.tabs', [$target, $targetContent]);
  
      //fire to children a mutation event
      $targetContent.find("[data-mutate]").trigger("mutateme.zf.trigger");
    }
  
    /**
     * Opens the tab `$targetContent` defined by `$target`.
     * @param {jQuery} $target - Tab to open.
     * @function
     */
    _openTab($target) {
        var $tabLink = $target.find('[role="tab"]'),
            hash = $tabLink.attr('data-tabs-target') || $tabLink[0].hash.slice(1),
            $targetContent = this.$tabContent.find(`#${hash}`);
  
        $target.addClass(`${this.options.linkActiveClass}`);
  
        $tabLink.attr({
          'aria-selected': 'true',
          'tabindex': '0'
        });
  
        $targetContent
          .addClass(`${this.options.panelActiveClass}`).removeAttr('aria-hidden');
    }
  
    /**
     * Collapses `$targetContent` defined by `$target`.
     * @param {jQuery} $target - Tab to collapse.
     * @function
     */
    _collapseTab($target) {
      var $target_anchor = $target
        .removeClass(`${this.options.linkActiveClass}`)
        .find('[role="tab"]')
        .attr({
          'aria-selected': 'false',
          'tabindex': -1
        });
  
      $(`#${$target_anchor.attr('aria-controls')}`)
        .removeClass(`${this.options.panelActiveClass}`)
        .attr({ 'aria-hidden': 'true' })
    }
  
    /**
     * Collapses the active Tab.
     * @fires Tabs#collapse
     * @function
     */
    _collapse() {
      var $activeTab = this.$element.find(`.${this.options.linkClass}.${this.options.linkActiveClass}`);
  
      if ($activeTab.length) {
        this._collapseTab($activeTab);
  
        /**
        * Fires when the plugin has successfully collapsed tabs.
        * @event Tabs#collapse
        */
        this.$element.trigger('collapse.zf.tabs', [$activeTab]);
      }
    }
  
    /**
     * Public method for selecting a content pane to display.
     * @param {jQuery | String} elem - jQuery object or string of the id of the pane to display.
     * @param {boolean} historyHandled - browser has already handled a history update
     * @function
     */
    selectTab(elem, historyHandled) {
      var idStr, hashIdStr;
  
      if (typeof elem === 'object') {
        idStr = elem[0].id;
      } else {
        idStr = elem;
      }
  
      if (idStr.indexOf('#') < 0) {
        hashIdStr = `#${idStr}`;
      } else {
        hashIdStr = idStr;
        idStr = idStr.slice(1);
      }
  
      var $target = this.$tabTitles.has(`[href$="${hashIdStr}"],[data-tabs-target="${idStr}"]`).first();
  
      this._handleTabChange($target, historyHandled);
    };
  
    /**
     * Sets the height of each panel to the height of the tallest panel.
     * If enabled in options, gets called on media query change.
     * If loading content via external source, can be called directly or with _reflow.
     * If enabled with `data-match-height="true"`, tabs sets to equal height
     * @function
     * @private
     */
    _setHeight() {
      var max = 0,
          _this = this; // Lock down the `this` value for the root tabs object
  
      if (!this.$tabContent) {
        return;
      }
  
      this.$tabContent
        .find(`.${this.options.panelClass}`)
        .css('height', '')
        .each(function() {
  
          var panel = $(this),
              isActive = panel.hasClass(`${_this.options.panelActiveClass}`); // get the options from the parent instead of trying to get them from the child
  
          if (!isActive) {
            panel.css({'visibility': 'hidden', 'display': 'block'});
          }
  
          var temp = this.getBoundingClientRect().height;
  
          if (!isActive) {
            panel.css({
              'visibility': '',
              'display': ''
            });
          }
  
          max = temp > max ? temp : max;
        })
        .css('height', `${max}px`);
    }
  
    /**
     * Destroys an instance of tabs.
     * @fires Tabs#destroyed
     */
    _destroy() {
      this.$element
        .find(`.${this.options.linkClass}`)
        .off('.zf.tabs').hide().end()
        .find(`.${this.options.panelClass}`)
        .hide();
  
      if (this.options.matchHeight) {
        if (this._setHeightMqHandler != null) {
           $(window).off('changed.zf.mediaquery', this._setHeightMqHandler);
        }
      }
  
      if (this.options.deepLink) {
        $(window).off('hashchange', this._checkDeepLink);
      }
  
      if (this.onLoadListener) {
        $(window).off(this.onLoadListener);
      }
    }
  }
  
  Tabs.defaults = {
    /**
     * Link the location hash to the active pane.
     * Set the location hash when the active pane changes, and open the corresponding pane when the location changes.
     * @option
     * @type {boolean}
     * @default false
     */
    deepLink: false,
  
    /**
     * If `deepLink` is enabled, adjust the deep link scroll to make sure the top of the tab panel is visible
     * @option
     * @type {boolean}
     * @default false
     */
    deepLinkSmudge: false,
  
    /**
     * If `deepLinkSmudge` is enabled, animation time (ms) for the deep link adjustment
     * @option
     * @type {number}
     * @default 300
     */
    deepLinkSmudgeDelay: 300,
  
    /**
     * If `deepLink` is enabled, update the browser history with the open tab
     * @option
     * @type {boolean}
     * @default false
     */
    updateHistory: false,
  
    /**
     * Allows the window to scroll to content of active pane on load.
     * Not recommended if more than one tab panel per page.
     * @option
     * @type {boolean}
     * @default false
     */
    autoFocus: false,
  
    /**
     * Allows keyboard input to 'wrap' around the tab links.
     * @option
     * @type {boolean}
     * @default true
     */
    wrapOnKeys: true,
  
    /**
     * Allows the tab content panes to match heights if set to true.
     * @option
     * @type {boolean}
     * @default false
     */
    matchHeight: false,
  
    /**
     * Allows active tabs to collapse when clicked.
     * @option
     * @type {boolean}
     * @default false
     */
    activeCollapse: false,
  
    /**
     * Class applied to `li`'s in tab link list.
     * @option
     * @type {string}
     * @default 'tabs-title'
     */
    linkClass: 'tabs-title',
  
    /**
     * Class applied to the active `li` in tab link list.
     * @option
     * @type {string}
     * @default 'is-active'
     */
    linkActiveClass: 'is-active',
  
    /**
     * Class applied to the content containers.
     * @option
     * @type {string}
     * @default 'tabs-panel'
     */
    panelClass: 'tabs-panel',
  
    /**
     * Class applied to the active content container.
     * @option
     * @type {string}
     * @default 'is-active'
     */
    panelActiveClass: 'is-active'
  };
  
  export {Tabs};
  