/*!
 * Theia Sticky Sidebar v1.7.0
 * https://github.com/WeCodePixels/theia-sticky-sidebar
 *
 * Glues your website's sidebars, making them permanently visible while scrolling.
 *
 * Copyright 2013-2016 WeCodePixels and other contributors
 * Released under the MIT license
 */

(function ($) {
  $.fn.theiaStickySidebar = function (options) {
      var defaults = {
          'containerSelector': '',
          'additionalMarginTop': 0,
          'additionalMarginBottom': 0,
          'updateSidebarHeight': true,
          'minWidth': 0,
          'disableOnResponsiveLayouts': true,
          'sidebarBehavior': 'modern',
          'defaultPosition': 'relative',
          'namespace': 'TSS'
      };
      options = $.extend(defaults, options);

      // Validate options
      options.additionalMarginTop = parseInt(options.additionalMarginTop) || 0;
      options.additionalMarginBottom = parseInt(options.additionalMarginBottom) || 0;

      tryInitOrHookIntoEvents(options, this);

      // Try doing init, otherwise hook into window.resize and document.scroll and try again then.
      function tryInitOrHookIntoEvents(options, $that) {
          var success = tryInit(options, $that);

          if (!success) {
              console.log('TSS: Body width smaller than options.minWidth. Init is delayed.');

              $(document).on('scroll.' + options.namespace, function (options, $that) {
                  return function (evt) {
                      var success = tryInit(options, $that);

                      if (success) {
                          $(this).unbind(evt);
                      }
                  };
              }(options, $that));
              $(window).on('resize.' + options.namespace, function (options, $that) {
                  return function (evt) {
                      var success = tryInit(options, $that);

                      if (success) {
                          $(this).unbind(evt);
                      }
                  };
              }(options, $that))
          }
      }

      // Try doing init if proper conditions are met.
      function tryInit(options, $that) {
          if (options.initialized === true) {
              return true;
          }

          if ($('body').width() < options.minWidth) {
              return false;
          }

          init(options, $that);

          return true;
      }

      // Init the sticky sidebar(s).
      function init(options, $that) {
          options.initialized = true;

          // Add CSS
          var existingStylesheet = $('#theia-sticky-sidebar-stylesheet-' + options.namespace);
          if (existingStylesheet.length === 0) {
              $('head').append($('<style id="theia-sticky-sidebar-stylesheet-' + options.namespace + '">.theiaStickySidebar:after {content: ""; display: table; clear: both;}</style>'));
          }

          $that.each(function () {
              var o = {};

              o.sidebar = $(this);

              // Save options
              o.options = options || {};

              // Get container
              o.container = $(o.options.containerSelector);
              if (o.container.length == 0) {
                  o.container = o.sidebar.parent();
              }

              // Create sticky sidebar
              o.sidebar.parents().css('-webkit-transform', 'none'); // Fix for WebKit bug - https://code.google.com/p/chromium/issues/detail?id=20574
              o.sidebar.css({
                  'position': o.options.defaultPosition,
                  'overflow': 'visible',
                  // The "box-sizing" must be set to "content-box" because we set a fixed height to this element when the sticky sidebar has a fixed position.
                  '-webkit-box-sizing': 'border-box',
                  '-moz-box-sizing': 'border-box',
                  'box-sizing': 'border-box'
              });

              // Get the sticky sidebar element. If none has been found, then create one.
              o.stickySidebar = o.sidebar.find('.theiaStickySidebar');
              if (o.stickySidebar.length == 0) {
                  // Remove <script> tags, otherwise they will be run again when added to the stickySidebar.
                  var javaScriptMIMETypes = /(?:text|application)\/(?:x-)?(?:javascript|ecmascript)/i;
                  o.sidebar.find('script').filter(function (index, script) {
                      return script.type.length === 0 || script.type.match(javaScriptMIMETypes);
                  }).remove();

                  o.stickySidebar = $('<div>').addClass('theiaStickySidebar').append(o.sidebar.children());
                  o.sidebar.append(o.stickySidebar);
              }

              // Get existing top and bottom margins and paddings
              o.marginBottom = parseInt(o.sidebar.css('margin-bottom'));
              o.paddingTop = parseInt(o.sidebar.css('padding-top'));
              o.paddingBottom = parseInt(o.sidebar.css('padding-bottom'));

              // Add a temporary padding rule to check for collapsable margins.
              var collapsedTopHeight = o.stickySidebar.offset().top;
              var collapsedBottomHeight = o.stickySidebar.outerHeight();
              o.stickySidebar.css('padding-top', 1);
              o.stickySidebar.css('padding-bottom', 1);
              collapsedTopHeight -= o.stickySidebar.offset().top;
              collapsedBottomHeight = o.stickySidebar.outerHeight() - collapsedBottomHeight - collapsedTopHeight;
              if (collapsedTopHeight == 0) {
                  o.stickySidebar.css('padding-top', 0);
                  o.stickySidebarPaddingTop = 0;
              }
              else {
                  o.stickySidebarPaddingTop = 1;
              }

              if (collapsedBottomHeight == 0) {
                  o.stickySidebar.css('padding-bottom', 0);
                  o.stickySidebarPaddingBottom = 0;
              }
              else {
                  o.stickySidebarPaddingBottom = 1;
              }

              // We use this to know whether the user is scrolling up or down.
              o.previousScrollTop = null;

              // Scroll top (value) when the sidebar has fixed position.
              o.fixedScrollTop = 0;

              // Set sidebar to default values.
              resetSidebar();

              o.onScroll = function (o) {
                  // Stop if the sidebar isn't visible.
                  if (!o.stickySidebar.is(":visible")) {
                      return;
                  }

                  // Stop if the window is too small.
                  if ($('body').width() < o.options.minWidth) {
                      resetSidebar();
                      return;
                  }

                  // Stop if the sidebar width is larger than the container width (e.g. the theme is responsive and the sidebar is now below the content)
                  if (o.options.disableOnResponsiveLayouts) {
                      var sidebarWidth = o.sidebar.outerWidth(o.sidebar.css('float') == 'none');

                      if (sidebarWidth + 50 > o.container.width()) {
                          resetSidebar();
                          return;
                      }
                  }

                  var scrollTop = $(document).scrollTop();
                  var position = 'static';

                  // If the user has scrolled down enough for the sidebar to be clipped at the top, then we can consider changing its position.
                  if (scrollTop >= o.sidebar.offset().top + (o.paddingTop - o.options.additionalMarginTop)) {
                      // The top and bottom offsets, used in various calculations.
                      var offsetTop = o.paddingTop + options.additionalMarginTop;
                      var offsetBottom = o.paddingBottom + o.marginBottom + options.additionalMarginBottom;

                      // All top and bottom positions are relative to the window, not to the parent elemnts.
                      var containerTop = o.sidebar.offset().top;
                      var containerBottom = o.sidebar.offset().top + getClearedHeight(o.container);

                      // The top and bottom offsets relative to the window screen top (zero) and bottom (window height).
                      var windowOffsetTop = 0 + options.additionalMarginTop;
                      var windowOffsetBottom;

                      var sidebarSmallerThanWindow = (o.stickySidebar.outerHeight() + offsetTop + offsetBottom) < $(window).height();
                      if (sidebarSmallerThanWindow) {
                          windowOffsetBottom = windowOffsetTop + o.stickySidebar.outerHeight();
                      }
                      else {
                          windowOffsetBottom = $(window).height() - o.marginBottom - o.paddingBottom - options.additionalMarginBottom;
                      }

                      var staticLimitTop = containerTop - scrollTop + o.paddingTop;
                      var staticLimitBottom = containerBottom - scrollTop - o.paddingBottom - o.marginBottom;

                      var top = o.stickySidebar.offset().top - scrollTop;
                      var scrollTopDiff = o.previousScrollTop - scrollTop;

                      // If the sidebar position is fixed, then it won't move up or down by itself. So, we manually adjust the top coordinate.
                      if (o.stickySidebar.css('position') == 'fixed') {
                          if (o.options.sidebarBehavior == 'modern') {
                              top += scrollTopDiff;
                          }
                      }

                      if (o.options.sidebarBehavior == 'stick-to-top') {
                          top = options.additionalMarginTop;
                      }

                      if (o.options.sidebarBehavior == 'stick-to-bottom') {
                          top = windowOffsetBottom - o.stickySidebar.outerHeight();
                      }

                      if (scrollTopDiff > 0) { // If the user is scrolling up.
                          top = Math.min(top, windowOffsetTop);
                      }
                      else { // If the user is scrolling down.
                          top = Math.max(top, windowOffsetBottom - o.stickySidebar.outerHeight());
                      }

                      top = Math.max(top, staticLimitTop);

                      top = Math.min(top, staticLimitBottom - o.stickySidebar.outerHeight());

                      // If the sidebar is the same height as the container, we won't use fixed positioning.
                      var sidebarSameHeightAsContainer = o.container.height() == o.stickySidebar.outerHeight();

                      if (!sidebarSameHeightAsContainer && top == windowOffsetTop) {
                          position = 'fixed';
                      }
                      else if (!sidebarSameHeightAsContainer && top == windowOffsetBottom - o.stickySidebar.outerHeight()) {
                          position = 'fixed';
                      }
                      else if (scrollTop + top - o.sidebar.offset().top - o.paddingTop <= options.additionalMarginTop) {
                          // Stuck to the top of the page. No special behavior.
                          position = 'static';
                      }
                      else {
                          // Stuck to the bottom of the page.
                          position = 'absolute';
                      }
                  }

                  /*
                   * Performance notice: It's OK to set these CSS values at each resize/scroll, even if they don't change.
                   * It's way slower to first check if the values have changed.
                   */
                  if (position == 'fixed') {
                      var scrollLeft = $(document).scrollLeft();

                      o.stickySidebar.css({
                          'position': 'fixed',
                          'width': getWidthForObject(o.stickySidebar) + 'px',
                          'transform': 'translateY(' + top + 'px)',
                          'left': (o.sidebar.offset().left + parseInt(o.sidebar.css('padding-left')) - scrollLeft) + 'px',
                          'top': '0px'
                      });
                  }
                  else if (position == 'absolute') {
                      var css = {};

                      if (o.stickySidebar.css('position') != 'absolute') {
                          css.position = 'absolute';
                          css.transform = 'translateY(' + (scrollTop + top - o.sidebar.offset().top - o.stickySidebarPaddingTop - o.stickySidebarPaddingBottom) + 'px)';
                          css.top = '0px';
                      }

                      css.width = getWidthForObject(o.stickySidebar) + 'px';
                      css.left = '';

                      o.stickySidebar.css(css);
                  }
                  else if (position == 'static') {
                      resetSidebar();
                  }

                  if (position != 'static') {
                      if (o.options.updateSidebarHeight == true) {
                          o.sidebar.css({
                              'min-height': o.stickySidebar.outerHeight() + o.stickySidebar.offset().top - o.sidebar.offset().top + o.paddingBottom
                          });
                      }
                  }

                  o.previousScrollTop = scrollTop;
              };

              // Initialize the sidebar's position.
              o.onScroll(o);

              // Recalculate the sidebar's position on every scroll and resize.
              $(document).on('scroll.' + o.options.namespace, function (o) {
                  return function () {
                      o.onScroll(o);
                  };
              }(o));
              $(window).on('resize.' + o.options.namespace, function (o) {
                  return function () {
                      o.stickySidebar.css({'position': 'static'});
                      o.onScroll(o);
                  };
              }(o));

              // Recalculate the sidebar's position every time the sidebar changes its size.
              if (typeof ResizeSensor !== 'undefined') {
                  new ResizeSensor(o.stickySidebar[0], function (o) {
                      return function () {
                          o.onScroll(o);
                      };
                  }(o));
              }

              // Reset the sidebar to its default state
              function resetSidebar() {
                  o.fixedScrollTop = 0;
                  o.sidebar.css({
                      'min-height': '1px'
                  });
                  o.stickySidebar.css({
                      'position': 'static',
                      'width': '',
                      'transform': 'none'
                  });
              }

              // Get the height of a div as if its floated children were cleared. Note that this function fails if the floats are more than one level deep.
              function getClearedHeight(e) {
                  var height = e.height();

                  e.children().each(function () {
                      height = Math.max(height, $(this).height());
                  });

                  return height;
              }
          });
      }

      function getWidthForObject(object) {
          var width;

          try {
              width = object[0].getBoundingClientRect().width;
          }
          catch (err) {
          }

          if (typeof width === "undefined") {
              width = object.width();
          }

          return width;
      }

      return this;
  }
})(scriptJquery);