/**

* HSHeaderSide Component.
*
* @author Htmlstream
* @version 1.0
* @requires HSScrollBar component (hs.scrollbar.js v1.0.0), jQuery(v2.0.0)
*
*/

;(function ($) {

'use strict';

$.HSCore.components.HSHeaderSide = {

  /**
   * Base configuration.
   *
   * @private
   */
  _baseConfig: {
    headerBreakpoint: null,
    breakpointsMap: {
      'md': 768,
      'sm': 576,
      'lg': 992,
      'xl': 1200
    },
    afterOpen: function(){},
    afterClose: function(){}
  },

  /**
   * Contains collection of all initialized items on the page.
   * 
   * @private
   */
  _pageCollection: $(),

  /**
   * Initializtion of the component.
   * 
   * @param {jQuery} collection
   * @param {Object} config
   *
   * @public
   * @returns {jQuery}
   */
  init: function(collection, config) {

    var _self = this;

    if(!collection || !collection.length) return $();

    this.$w = $(window);

    config = config && $.isPlainObject(config) ? config : {};

    this._bindGlobalEvents();

    return collection.each(function(i, el){

      var $this = $(el),
          itemConfig = $.extend(true, {}, _self._baseConfig, config, $this.data());

      if( $this.data('HSHeaderSide') ) return;

      $this.data('HSHeaderSide', _self._factoryMethod( $this, itemConfig ) );

      _self._pageCollection = _self._pageCollection.add($this);

    });

  },

  /**
   * Binds necessary global events.
   * 
   * @private 
   */
  _bindGlobalEvents: function() {

    var _self = this;

    this.$w.on('resize.HSHeaderSide', function(e){

      if(_self.resizeTimeoutId) clearTimeout(_self.resizeTimeoutId);

      _self.resizeTimeoutId = setTimeout(function(){

        _self._pageCollection.each(function(i, el){

          var HSHeaderSide = $(el).data('HSHeaderSide');

          if(!HSHeaderSide.config.headerBreakpoint) return;

          if(_self.$w.width() < HSHeaderSide.config.breakpointsMap[HSHeaderSide.config.headerBreakpoint] && HSHeaderSide.isInit()) {
            HSHeaderSide.destroy();
          }
          else if(_self.$w.width() >= HSHeaderSide.config.breakpointsMap[HSHeaderSide.config.headerBreakpoint] && !HSHeaderSide.isInit()) {
            HSHeaderSide.init();
          }

        });

      }, 10);

    });

    $(document).on('keyup.HSHeaderSide', function(e){

      if(e.keyCode && e.keyCode === 27) {

        _self._pageCollection.each(function(i,el){

          var HSHeaderSide = $(el).data('HSHeaderSide'),
              hamburgers = HSHeaderSide.invoker;

          if(!HSHeaderSide) return;
          if(hamburgers.length && hamburgers.find('.is-active').length) hamburgers.find('.is-active').removeClass('is-active');
          HSHeaderSide.hide();

        });

      }

    });

  },

  /**
   * Returns an object which would be describe the Header behavior.
   * 
   * @private 
   * @returns {HSHeaderSide*} 
   */
  _factoryMethod: function(element, config) {

    // static
    if( !config.headerBehavior ) {
      return new (config['headerPosition'] == "left" ? HSHeaderSideStaticLeft : HSHeaderSideStaticRight)(element, config);
    }

    // overlay
    if( config.headerBehavior && config.headerBehavior == 'overlay' ) {
      return new (config['headerPosition'] == "left" ? HSHeaderSideOverlayLeft : HSHeaderSideOverlayRight)(element, config); 
    }

    // push
    if( config.headerBehavior && config.headerBehavior == 'push' ) {
      return new (config['headerPosition'] == "left" ? HSHeaderSidePushLeft : HSHeaderSidePushRight)(element, config); 
    }

  }

}

/**
 * Provides an abstract interface for the side header.
 * 
 * @param {jQuery} element
 * @param {Object} config
 * 
 */
function _HSHeaderSideAbstract(element, config) {

  /**
   * Contains link to the current element.
   * 
   * @public
   */
  this.element = element;

  /**
   * Contains configuration object.
   * 
   * @public
   */
  this.config = config;

  /**
   * Contains link to the window object.
   * 
   * @public
   */
  this.$w = $(window);

  /**
   * Contains name of methods which should be implemented in derived class. 
   * Each of these methods except 'isInit' must return link to the current object.
   * 
   * @private
   */
  this._abstractMethods = ['init', 'destroy', 'show', 'hide', 'isInit'];

  /**
   * Runs initialization of the object.
   * 
   * @private 
   */
  this._build = function() {

    if( !this.config.headerBreakpoint ) return this.init();

    if( this.config.breakpointsMap[ this.config.headerBreakpoint ] <= this.$w.width() ) {
      return this.init();
    }
    else {
      return this.destroy();
    }
  };

  /**
   * Checks whether derived class implements necessary abstract events.
   * 
   * @private 
   */
  this._isCorrectDerrivedClass = function() {

    var _self = this;

    this._abstractMethods.forEach(function(method){

      if(!(method in _self) || !$.isFunction(_self[method])) {

        throw new Error("HSHeaderSide: Derived class must implement " + method + " method.");

      }

    });

    this._build();

  };

  setTimeout(this._isCorrectDerrivedClass.bind(this), 10);

};

/**
 * HSHeaderSide constructor function.
 * 
 * @extends _HSHeaderSideAbstract
 *
 * @param {jQuery} element
 * @param {Object} config
 *
 * @constructor
 */
function HSHeaderSideStaticLeft( element, config ) {

  _HSHeaderSideAbstract.call(this, element, config);

  Object.defineProperty(this, 'scrollContainer', {
    get: function() {
      return this.element.find('.u-header__sections-container');
    }
  });

  this.body = $('body');

};

/**
 * Initialization of the HSHeaderSideStaticLeft instance.
 * 
 * @public
 * @returns {HSHeaderSideStaticLeft}
 */
HSHeaderSideStaticLeft.prototype.init = function() {

  this.body.addClass('u-body--header-side-static-left');

  if( $.HSCore.components.HSScrollBar && this.scrollContainer.length ) {
    $.HSCore.components.HSScrollBar.init( this.scrollContainer );
  }

  return this;

};

/**
 * Destroys the HSHeaderSideStaticLeft instance.
 * 
 * @public
 * @returns {HSHeaderSideStaticLeft}
 */
HSHeaderSideStaticLeft.prototype.destroy = function() {

  this.body.removeClass('u-body--header-side-static-left');

  if( $.HSCore.components.HSScrollBar && this.scrollContainer.length ) {
    $.HSCore.components.HSScrollBar.destroy( this.scrollContainer );
  }

  return this;

};

/**
 * Checks whether instance has been initialized.
 * 
 * @public
 * @returns {Boolean}
 */
HSHeaderSideStaticLeft.prototype.isInit = function() {

  return this.body.hasClass('u-body--header-side-static-left');

};

/**
 * Shows the Header.
 * 
 * @public
 * @returns {HSHeaderSideStaticLeft}
 */
HSHeaderSideStaticLeft.prototype.show = function() {
  return this;
};

/**
 * Hides the Header.
 * 
 * @public
 * @returns {HSHeaderSideStaticLeft}
 */
HSHeaderSideStaticLeft.prototype.hide = function() {
  return this;
};

/**
 * HSHeaderSide constructor function.
 * 
 * @extends _HSHeaderSideAbstract
 *
 * @param {jQuery} element
 * @param {Object} config
 *
 * @constructor
 */
function HSHeaderSideStaticRight( element, config ) {

  _HSHeaderSideAbstract.call(this, element, config);

  Object.defineProperty(this, 'scrollContainer', {
    get: function() {
      return this.element.find('.u-header__sections-container');
    }
  });

  this.body = $('body');

};

/**
 * Initialization of the HSHeaderSideStaticRight instance.
 * 
 * @public
 * @returns {HSHeaderSideStaticRight}
 */
HSHeaderSideStaticRight.prototype.init = function() {

  this.body.addClass('u-body--header-side-static-right');

  if( $.HSCore.components.HSScrollBar && this.scrollContainer.length ) {
    $.HSCore.components.HSScrollBar.init( this.scrollContainer );
  }

  return this;

};

/**
 * Destroys the HSHeaderSideStaticRight instance.
 * 
 * @public
 * @returns {HSHeaderSideStaticRight}
 */
HSHeaderSideStaticRight.prototype.destroy = function() {

  this.body.removeClass('u-body--header-side-static-right');

  if( $.HSCore.components.HSScrollBar && this.scrollContainer.length ) {
    $.HSCore.components.HSScrollBar.destroy( this.scrollContainer );
  }

  return this;

};

/**
 * Checks whether instance has been initialized.
 * 
 * @public
 * @returns {Boolean}
 */
HSHeaderSideStaticRight.prototype.isInit = function() {

  return this.body.hasClass('u-body--header-side-static-right');

};

/**
 * Shows the Header.
 * 
 * @public
 * @returns {HSHeaderSideStaticRight}
 */
HSHeaderSideStaticRight.prototype.show = function() {
  return this;
};

/**
 * Hides the Header.
 * 
 * @public
 * @returns {HSHeaderSideStaticRight}
 */
HSHeaderSideStaticRight.prototype.hide = function() {
  return this;
};

/**
 * HSHeaderSide constructor function.
 * 
 * @extends _HSHeaderSideAbstract
 *
 * @param {jQuery} element
 * @param {Object} config
 *
 * @constructor
 */
function HSHeaderSideOverlayLeft( element, config ) {

  _HSHeaderSideAbstract.call(this, element, config);

  Object.defineProperty(this, 'scrollContainer', {
    get: function() {
      return this.element.find('.u-header__sections-container');
    }
  });

  Object.defineProperty(this, 'isShown', {
    get: function() {
      return this.body.hasClass('u-body--header-side-opened');
    }
  });

  Object.defineProperty(this, 'overlayClasses', {
    get: function() {
      return this.element.data('header-overlay-classes') ? this.element.data('header-overlay-classes') : '';
    }
  });

  Object.defineProperty(this, 'headerClasses', {
    get: function() {
      return this.element.data('header-classes') ? this.element.data('header-classes') : '';
    }
  });

  this.body = $('body');
  this.invoker = $('[data-target="#'+this.element.attr('id')+'"]');

};

/**
 * Initialization of the HSHeaderSideOverlayLeft instance.
 * 
 * @public
 * @returns {HSHeaderSideOverlayLeft}
 */
HSHeaderSideOverlayLeft.prototype.init = function() {

  var _self = this;

  this.body.addClass('u-body--header-side-overlay-left');

  if( $.HSCore.components.HSScrollBar && this.scrollContainer.length ) {
    $.HSCore.components.HSScrollBar.init( this.scrollContainer );
  }

  if(this.invoker.length) {
    this.invoker.on('click.HSHeaderSide', function(e){

      if(_self.isShown) {
        _self.hide();
      }
      else {
        _self.show();
      }

      e.preventDefault();
    }).css('display', 'block');
  }

  if(!this.overlay) {

    this.overlay = $('<div></div>', {
      class: 'u-header__overlay ' + _self.overlayClasses
    });

  }

  this.overlay.on('click.HSHeaderSide', function(e){
    var hamburgers = _self.invoker.length ? _self.invoker.find('.is-active') : $();
    if(hamburgers.length) hamburgers.removeClass('is-active');
    _self.hide();
  });

  this.element.addClass(this.headerClasses).append(this.overlay);

  return this;

};

/**
 * Destroys the HSHeaderSideOverlayLeft instance.
 * 
 * @public
 * @returns {HSHeaderSideOverlayLeft}
 */
HSHeaderSideOverlayLeft.prototype.destroy = function() {

  this.body.removeClass('u-body--header-side-overlay-left');
  this.hide();

  if( $.HSCore.components.HSScrollBar && this.scrollContainer.length ) {
    $.HSCore.components.HSScrollBar.destroy( this.scrollContainer );
  }

  this.element.removeClass(this.headerClasses);
  if(this.invoker.length) {
    this.invoker.off('click.HSHeaderSide').css('display', 'none');
  }
  if(this.overlay) {
    this.overlay.off('click.HSHeaderSide');
    this.overlay.remove();
    this.overlay = null;
  }

  return this;

};

/**
 * Checks whether instance has been initialized.
 * 
 * @public
 * @returns {Boolean}
 */
HSHeaderSideOverlayLeft.prototype.isInit = function() {

  return this.body.hasClass('u-body--header-side-overlay-left');

};

/**
 * Shows the Header.
 * 
 * @public
 * @returns {HSHeaderSideOverlayLeft}
 */
HSHeaderSideOverlayLeft.prototype.show = function() {

  this.body.addClass('u-body--header-side-opened');

  return this;
};

/**
 * Hides the Header.
 * 
 * @public
 * @returns {HSHeaderSideOverlayLeft}
 */
HSHeaderSideOverlayLeft.prototype.hide = function() {

  // var hamburgers = this.invoker.length ? this.invoker.find('.is-active') : $();
  // if(hamburgers.length) hamburgers.removeClass('is-active');

  this.body.removeClass('u-body--header-side-opened');

  return this;
};

/**
 * HSHeaderSide constructor function.
 * 
 * @extends _HSHeaderSideAbstract
 *
 * @param {jQuery} element
 * @param {Object} config
 *
 * @constructor
 */
function HSHeaderSidePushLeft( element, config ) {

  _HSHeaderSideAbstract.call(this, element, config);

  Object.defineProperty(this, 'scrollContainer', {
    get: function() {
      return this.element.find('.u-header__sections-container');
    }
  });

  Object.defineProperty(this, 'isShown', {
    get: function() {
      return this.body.hasClass('u-body--header-side-opened');
    }
  });

  Object.defineProperty(this, 'overlayClasses', {
    get: function() {
      return this.element.data('header-overlay-classes') ? this.element.data('header-overlay-classes') : '';
    }
  });

  Object.defineProperty(this, 'headerClasses', {
    get: function() {
      return this.element.data('header-classes') ? this.element.data('header-classes') : '';
    }
  });

  Object.defineProperty(this, 'bodyClasses', {
    get: function() {
      return this.element.data('header-body-classes') ? this.element.data('header-body-classes') : '';
    }
  });

  this.body = $('body');
  this.invoker = $('[data-target="#'+this.element.attr('id')+'"]');

};

/**
 * Initialization of the HSHeaderSidePushLeft instance.
 * 
 * @public
 * @returns {HSHeaderSidePushLeft}
 */
HSHeaderSidePushLeft.prototype.init = function() {

  var _self = this;

  this.body.addClass('u-body--header-side-push-left');

  if( $.HSCore.components.HSScrollBar && this.scrollContainer.length ) {
    $.HSCore.components.HSScrollBar.init( this.scrollContainer );
  }

  if(this.invoker.length) {
    this.invoker.on('click.HSHeaderSide', function(e){

      if(_self.isShown) {
        _self.hide();
      }
      else {
        _self.show();
      }

      e.preventDefault();
    }).css('display', 'block');
  }

  if(!this.overlay) {

    this.overlay = $('<div></div>', {
      class: 'u-header__overlay ' + _self.overlayClasses
    });

  }

  this.overlay.on('click.HSHeaderSide', function(e){
    var hamburgers = _self.invoker.length ? _self.invoker.find('.is-active') : $();
    if(hamburgers.length) hamburgers.removeClass('is-active');
    _self.hide();
  });

  this.element.addClass(this.headerClasses).append(this.overlay);
  this.body.addClass(this.bodyClasses);

  return this;

};

/**
 * Destroys the HSHeaderSidePushLeft instance.
 * 
 * @public
 * @returns {HSHeaderSidePushLeft}
 */
HSHeaderSidePushLeft.prototype.destroy = function() {

  this.body.removeClass('u-body--header-side-push-left');
  this.hide();

  if( $.HSCore.components.HSScrollBar && this.scrollContainer.length ) {
    $.HSCore.components.HSScrollBar.destroy( this.scrollContainer );
  }

  this.element.removeClass(this.headerClasses);
  this.body.removeClass(this.bodyClasses);
  if(this.invoker.length){
    this.invoker.off('click.HSHeaderSide').css('display', 'none');
  }
  if(this.overlay) {
    this.overlay.off('click.HSHeaderSide');
    this.overlay.remove();
    this.overlay = null;
  }

  return this;

};

/**
 * Checks whether instance has been initialized.
 * 
 * @public
 * @returns {Boolean}
 */
HSHeaderSidePushLeft.prototype.isInit = function() {

  return this.body.hasClass('u-body--header-side-push-left');

};

/**
 * Shows the Header.
 * 
 * @public
 * @returns {HSHeaderSidePushLeft}
 */
HSHeaderSidePushLeft.prototype.show = function() {

  this.body.addClass('u-body--header-side-opened');

  return this;
};

/**
 * Hides the Header.
 * 
 * @public
 * @returns {HSHeaderSidePushLeft}
 */
HSHeaderSidePushLeft.prototype.hide = function() {

  this.body.removeClass('u-body--header-side-opened');

  return this;
};

/**
 * HSHeaderSide constructor function.
 * 
 * @extends _HSHeaderSideAbstract
 *
 * @param {jQuery} element
 * @param {Object} config
 *
 * @constructor
 */
function HSHeaderSideOverlayRight( element, config ) {

  _HSHeaderSideAbstract.call(this, element, config);

  Object.defineProperty(this, 'scrollContainer', {
    get: function() {
      return this.element.find('.u-header__sections-container');
    }
  });

  Object.defineProperty(this, 'isShown', {
    get: function() {
      return this.body.hasClass('u-body--header-side-opened');
    }
  });

  Object.defineProperty(this, 'overlayClasses', {
    get: function() {
      return this.element.data('header-overlay-classes') ? this.element.data('header-overlay-classes') : '';
    }
  });

  Object.defineProperty(this, 'headerClasses', {
    get: function() {
      return this.element.data('header-classes') ? this.element.data('header-classes') : '';
    }
  });

  this.body = $('body');
  this.invoker = $('[data-target="#'+this.element.attr('id')+'"]');

};

/**
 * Initialization of the HSHeaderSideOverlayRight instance.
 * 
 * @public
 * @returns {HSHeaderSideOverlayRight}
 */
HSHeaderSideOverlayRight.prototype.init = function() {

  var _self = this;

  this.body.addClass('u-body--header-side-overlay-right');

  if( $.HSCore.components.HSScrollBar && this.scrollContainer.length ) {
    $.HSCore.components.HSScrollBar.init( this.scrollContainer );
  }

  if(this.invoker.length) {
    this.invoker.on('click.HSHeaderSide', function(e){

      if(_self.isShown) {
        _self.hide();
      }
      else {
        _self.show();
      }

      e.preventDefault();
    }).css('display', 'block');
  }

  if(!this.overlay) {

    this.overlay = $('<div></div>', {
      class: 'u-header__overlay ' + _self.overlayClasses
    });

  }

  this.overlay.on('click.HSHeaderSide', function(e){
    var hamburgers = _self.invoker.length ? _self.invoker.find('.is-active') : $();
    if(hamburgers.length) hamburgers.removeClass('is-active');
    _self.hide();
  });

  this.element.addClass(this.headerClasses).append(this.overlay);

  return this;

};

/**
 * Destroys the HSHeaderSideOverlayRight instance.
 * 
 * @public
 * @returns {HSHeaderSideOverlayRight}
 */
HSHeaderSideOverlayRight.prototype.destroy = function() {

  this.body.removeClass('u-body--header-side-overlay-right');
  this.hide();

  if( $.HSCore.components.HSScrollBar && this.scrollContainer.length ) {
    $.HSCore.components.HSScrollBar.destroy( this.scrollContainer );
  }

  this.element.removeClass(this.headerClasses);
  if(this.invoker.length) {
    this.invoker.off('click.HSHeaderSide').css('display', 'none');
  }
  if(this.overlay) {
    this.overlay.off('click.HSHeaderSide');
    this.overlay.remove();
    this.overlay = null;
  }

  return this;

};

/**
 * Checks whether instance has been initialized.
 * 
 * @public
 * @returns {Boolean}
 */
HSHeaderSideOverlayRight.prototype.isInit = function() {

  return this.body.hasClass('u-body--header-side-overlay-right');

};

/**
 * Shows the Header.
 * 
 * @public
 * @returns {HSHeaderSideOverlayRight}
 */
HSHeaderSideOverlayRight.prototype.show = function() {

  this.body.addClass('u-body--header-side-opened');

  return this;
};

/**
 * Hides the Header.
 * 
 * @public
 * @returns {HSHeaderSideOverlayRight}
 */
HSHeaderSideOverlayRight.prototype.hide = function() {

  // var hamburgers = this.invoker.length ? this.invoker.find('.is-active') : $();
  // if(hamburgers.length) hamburgers.removeClass('is-active');

  this.body.removeClass('u-body--header-side-opened');

  return this;
};

/**
 * HSHeaderSide constructor function.
 * 
 * @extends _HSHeaderSideAbstract
 *
 * @param {jQuery} element
 * @param {Object} config
 *
 * @constructor
 */
function HSHeaderSidePushRight( element, config ) {

  _HSHeaderSideAbstract.call(this, element, config);

  Object.defineProperty(this, 'scrollContainer', {
    get: function() {
      return this.element.find('.u-header__sections-container');
    }
  });

  Object.defineProperty(this, 'isShown', {
    get: function() {
      return this.body.hasClass('u-body--header-side-opened');
    }
  });

  Object.defineProperty(this, 'overlayClasses', {
    get: function() {
      return this.element.data('header-overlay-classes') ? this.element.data('header-overlay-classes') : '';
    }
  });

  Object.defineProperty(this, 'headerClasses', {
    get: function() {
      return this.element.data('header-classes') ? this.element.data('header-classes') : '';
    }
  });

  Object.defineProperty(this, 'bodyClasses', {
    get: function() {
      return this.element.data('header-body-classes') ? this.element.data('header-body-classes') : '';
    }
  });

  this.body = $('body');
  this.invoker = $('[data-target="#'+this.element.attr('id')+'"]');

};

/**
 * Initialization of the HSHeaderSidePushRight instance.
 * 
 * @public
 * @returns {HSHeaderSidePushRight}
 */
HSHeaderSidePushRight.prototype.init = function() {

  var _self = this;

  this.body.addClass('u-body--header-side-push-right');

  if( $.HSCore.components.HSScrollBar && this.scrollContainer.length ) {
    $.HSCore.components.HSScrollBar.init( this.scrollContainer );
  }

  if(this.invoker.length) {
    this.invoker.on('click.HSHeaderSide', function(e){

      if(_self.isShown) {
        _self.hide();
      }
      else {
        _self.show();
      }

      e.preventDefault();
    }).css('display', 'block');
  }

  if(!this.overlay) {

    this.overlay = $('<div></div>', {
      class: 'u-header__overlay ' + _self.overlayClasses
    });

  }

  this.overlay.on('click.HSHeaderSide', function(e){
    var hamburgers = _self.invoker.length ? _self.invoker.find('.is-active') : $();
    if(hamburgers.length) hamburgers.removeClass('is-active');
    _self.hide();
  });

  this.element.addClass(this.headerClasses).append(this.overlay);
  this.body.addClass(this.bodyClasses);

  return this;

};

/**
 * Destroys the HSHeaderSidePushRight instance.
 * 
 * @public
 * @returns {HSHeaderSidePushRight}
 */
HSHeaderSidePushRight.prototype.destroy = function() {

  this.body.removeClass('u-body--header-side-push-right');
  this.hide();

  if( $.HSCore.components.HSScrollBar && this.scrollContainer.length ) {
    $.HSCore.components.HSScrollBar.destroy( this.scrollContainer );
  }

  this.element.removeClass(this.headerClasses);
  this.body.removeClass(this.bodyClasses);
  if(this.invoker.length){
    this.invoker.off('click.HSHeaderSide').css('display', 'none');
  }
  if(this.overlay) {
    this.overlay.off('click.HSHeaderSide');
    this.overlay.remove();
    this.overlay = null;
  }

  return this;

};

/**
 * Checks whether instance has been initialized.
 * 
 * @public
 * @returns {Boolean}
 */
HSHeaderSidePushRight.prototype.isInit = function() {

  return this.body.hasClass('u-body--header-side-push-right');

};

/**
 * Shows the Header.
 * 
 * @public
 * @returns {HSHeaderSidePushRight}
 */
HSHeaderSidePushRight.prototype.show = function() {

  this.body.addClass('u-body--header-side-opened');

  return this;
};

/**
 * Hides the Header.
 * 
 * @public
 * @returns {HSHeaderSidePushRight}
 */
HSHeaderSidePushRight.prototype.hide = function() {

  // var hamburgers = this.invoker.length ? this.invoker.find('.is-active') : $();
  // if(hamburgers.length) hamburgers.removeClass('is-active');

  this.body.removeClass('u-body--header-side-opened');

  return this;
};

})(jQuery);