/* ==========================================================================
 * bootstrap-tab-history.js
 * Author: Michael Narayan <mnarayan01@gmail.com>
 * Repository: https://github.com/mnarayan01/bootstrap-tab-history/
 * ==========================================================================
 * Licensed under the Apache License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License. You may obtain a
 * copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 * ========================================================================== */

/* ========================================================================== */
/* JSHint directives                                                          */
/*                                                                            */
/* global BootstrapTabHistory: true                                           */
/*                                                                            */
/* global document                                                            */
/* global jQuery                                                              */
/* global history                                                             */
/* global window                                                              */
/* ========================================================================== */

/**
 * Integrate [HTML5 history state tracking](https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Manipulating_the_browser_history)
 * with [`bootstrap/tab.js`](http://getbootstrap.com/javascript/#tabs). To enable tracking on a tab element, simply set
 * the `data-tab-history` attribute to true (or a string denoting a tab grouping).
 *
 * See the README for additional information.
 *
 * Functionality based upon bootstrap/tab.js v3.1.0; reference it when making any changes.
 */

BootstrapTabHistory = {
  options: {
    /**
     * When the anchor portion of the URI is used to activate a tab, scroll down to the given offset, rather than the
     * element with the given `id` attribute. Set to null to disable. Only relevant if showTabsBasedOnAnchor is true.
     *
     * May be overriden on a per-element basis by the attribute `data-tab-history-anchor-y-offset`.
     *
     * @public
     * @type {?number}
     */
    defaultAnchorYOffset: 0,
    /**
     * Either 'push' or 'replace', for whether to use `history.pushState` or `history.replaceState`, resp.
     *
     * May be overriden on a per-element basis by the attribute `data-tab-history-changer`.
     *
     * @public
     * @type {string}
     */
    defaultChanger: 'replace',
    /**
     * If true, update the URL in onShownTab in the calls to `history.pushState` and `history.replaceState`. Otherwise,
     * `null` is passed as the third parameter to these calls.
     *
     * May be overriden on a per-element basis by the attribute `data-tab-history-update-url`.
     *
     * @public
     * @type {boolean}
     */
    defaultUpdateURL: false,
    /**
     * Should the anchor portion of the loaded URI be used to activate a single tab if no history was present on page
     * load.
     *
     * @public
     * @type {boolean}
     */
    showTabsBasedOnAnchor: true
  }
};

(function () {
  'use strict';

  jQuery(function () {
    if(history && history.pushState && history.replaceState) {
      var bootstrapTabHistory = history.state && history.state.bootstrapTabHistory;

      if(bootstrapTabHistory) {
        showTabsBasedOnState(bootstrapTabHistory);
      } else {
        showTabsBasedOnAnchor();
      }

      backfillHistoryState();

      jQuery(document).on('shown.bs.tab show.bs.collapse', onShownTab);
      jQuery(window).on('popstate', onPopState);
    } else {
      showTabsBasedOnAnchor();
    }
  });

  /**
   * Used to prevent onShownTab from registering shown events that we triggered via showTabsBasedOnState.
   *
   * @type {boolean}
   */
  var showingTabsBasedOnState = false;

  /**
   * Used to update `history.state` to reflect the default active tabs on initial page load. This supports proper
   * `history.back` handling when `data-tab-history-update-url` is true.
   */
  function backfillHistoryState() {
    var newState = null;

    jQuery('li.active > [data-tab-history], .panel-title.active [data-tab-history]').each(function () {//edited by Uncode
      var $activeTabElement = jQuery(this);
      var selector = getTabSelector($activeTabElement);

      if(selector) {
        var tabGroup = getTabGroup($activeTabElement);

        if(tabGroup) {
          newState = createNewHistoryState(newState || history.state, tabGroup, selector);
        }
      }
    });

    if(newState) {
      history.replaceState(newState, '', null);
    }
  }

  /**
   * Clone the existing state, ensure its bootstrapTabHistory attribute is an Object, and add the provided tabGroup to
   * said bootstrapTabHistory attribute.
   *
   * @param {?object} existingState
   * @param {!string} tabGroup
   * @param {!string} selector
   * @returns {!object}
   */
  function createNewHistoryState(existingState, tabGroup, selector) {
    // Clone history.state and ensure it has a bootstrapTabHistory entry.
    var newState = jQuery.extend(true, {}, existingState, {
      bootstrapTabHistory: {}
    });

    newState.bootstrapTabHistory[tabGroup] = selector;

    return newState;
  }

  /**
   * @param {jQuery} $tab
   * @returns {?string}
   */
  function getTabGroup($tab) {
    return parseTruthyAttributeValue($tab.data('tab-history'));
  }

  /**
   * @param {jQuery} $tab
   * @returns {?string}
   */
  function getTabSelector($tab) {
    return $tab.data('target') || $tab.attr('href');
  }

  /**
   * Receives the `shown.bs.tab` event. Updates `history.state` as appropriate.
   *
   * @param {jQuery.Event} shownEvt
   */
  function onShownTab(shownEvt) {
    if(!showingTabsBasedOnState) {
      var $activatedTab = jQuery(shownEvt.target);
      if ( $activatedTab.hasClass('panel-collapse') )
        $activatedTab = $activatedTab.closest('.panel').find('a');
      var selector = getTabSelector($activatedTab);

      if(selector) {
        var tabGroup = getTabGroup($activatedTab);

        if(tabGroup) {
          var historyChanger = $activatedTab.data('tab-history-changer') || BootstrapTabHistory.options.defaultChanger;
          var newState = createNewHistoryState(history.state, tabGroup, selector);
          var updateURL = (function ($activatedTab) {
            if(selector[0] === '#') {
              var elementUpdateURLOption = parseTruthyAttributeValue($activatedTab.data('tab-history-update-url'));

              if(elementUpdateURLOption === undefined) {
                return BootstrapTabHistory.options.defaultUpdateURL;
              } else {
                return elementUpdateURLOption;
              }
            } else {
              return false;
            }
          })($activatedTab);

          switch(historyChanger) {
            case 'push':
              history.pushState(newState, '', updateURL ? selector : null);
              break;
            case 'replace':
              history.replaceState(newState, '', updateURL ? selector : null);
              break;
            default:
              throw new Error('Unknown tab-history-changer: ' + historyChanger);
          }
        }
      }
    }
  }

  /**
   * Receives the `popstate` event. Shows tabs based on the value of `history.state` as appropriate.
   */
  function onPopState() {
    var bootstrapTabHistory = history.state && history.state.bootstrapTabHistory;

    if(bootstrapTabHistory) {
      showTabsBasedOnState(bootstrapTabHistory);
    }
  }

  /**
   * Returns the given value, _unless_ that value is an empty string, in which case `true` is returned.
   *
   * Rationale: HAML data attributes which are set to `true` are rendered as a blank string.
   *
   * @param {*} value
   * @returns {*}
   */
  function parseTruthyAttributeValue(value) {
    if(value) {
      return value;
    } else if(value === '') {
      return true;
    } else {
      return value;
    }
  }

  /**
   * Show tabs based upon the anchor component of `window.location`.
   */
  function showTabsBasedOnAnchor() {
    if(BootstrapTabHistory.options.showTabsBasedOnAnchor) {
      var anchor = window.location && window.location.hash;

      if(anchor) {
        var $tabElement = showTabForSelector(anchor);

        if($tabElement && window.addEventListener && window.removeEventListener) {
          var anchorYOffset = (function ($tabElement) {
            var elementSetting = $tabElement.data('tab-history-anchor-y-offset');

            if(elementSetting === undefined) {
              return BootstrapTabHistory.options.defaultAnchorYOffset;
            } else {
              return elementSetting;
            }
          })($tabElement);

          // HACK: This prevents scrolling to the tab on page load. This relies on the fact that we should never get
          //   here on `history.forward`, `history.back`, or `location.reload`, since in all those situations the
          //   `history.state` object should have been used (unless the browser did not support the modern History API).
          if(anchorYOffset || anchorYOffset === 0) {
            var scrollListener = function resetAnchorScroll () {
              window.removeEventListener('scroll', scrollListener);
              window.scrollTo(0, anchorYOffset);
            };

            window.addEventListener('scroll', scrollListener);
          }
        }
      }
    }
  }

  /**
   * Show a tab which corresponds to the provided selector.
   *
   * @param {string} selector - A CSS selector.
   * @returns {?jQuery} - The tab which was found to show (even if said tab was already active).
   */
  function showTabForSelector(selector) {
    var $tabElement = (function (selector) {
      var $ret = null;

      jQuery('[data-toggle="tab"], [data-toggle="pill"], [data-toggle="collapse"]').each(function () {
        var $potentialTab = jQuery(this);

        if(($potentialTab.attr('href') === selector || $potentialTab.data('target') === selector ) && getTabGroup($potentialTab)) {
          $ret = $potentialTab;
          return false;
        } else {
         return null;
        }
      });

      return $ret;
    })(selector);

    if($tabElement) {
      if ( !$tabElement.parent().hasClass('active') ) {
        $tabElement.trigger('click');
      }
      //$tabElement.tab('show');
    }

    return $tabElement;
  }

  /**
   * Iterate through the provided set of tab tab groups, showing the tabs based on the stored selectors.
   *
   * @param {object} bootstrapTabHistory - Each of the values is passed to showTabForSelector; the keys are actually irrelevant.
   */
  function showTabsBasedOnState(bootstrapTabHistory) {
    showingTabsBasedOnState = true;

    try {
      for(var k in bootstrapTabHistory) {
        if(bootstrapTabHistory.hasOwnProperty(k)) {
          showTabForSelector(bootstrapTabHistory[k]);
        }
      }
    } finally {
      showingTabsBasedOnState = false;
    }
  }
})();
