drhenner/ror_ecommerce

View on GitHub
app/assets/javascripts/foundation/foundation.section.js

Summary

Maintainability
D
2 days
Test Coverage
/*jslint unparam: true, browser: true, indent: 2 */

;
(function($, window, document) {
  'use strict';

  Foundation.libs.section = {
    name : 'section',

    version : '4.3.2',

    settings: {
      deep_linking: false,
      small_breakpoint: 768,
      one_up: true,
      multi_expand: false,
      section_selector: '[data-section]',
      region_selector: 'section, .section, [data-section-region]',
      title_selector: '.title, [data-section-title]',
      //marker: container is resized
      resized_data_attr: 'data-section-resized',
      //marker: container should apply accordion style
      small_style_data_attr: 'data-section-small-style',
      content_selector: '.content, [data-section-content]',
      nav_selector: '[data-section="vertical-nav"], [data-section="horizontal-nav"]',
      active_class: 'active',
      callback: function() {}
    },

    init: function(scope, method, options) {
      var self = this;
      Foundation.inherit(this, 'throttle data_options position_right offset_right');

      if (typeof method === 'object') {
        $.extend(true, self.settings, method);
      }

      if (typeof method !== 'string') {
        this.events();
        return true;
      } else {
        return this[method].call(this, options);
      }
    },

    events: function() {
      var self = this;

      //combine titles selector from settings for click event binding
      var click_title_selectors = [],
          section_selector = self.settings.section_selector,
          region_selectors = self.settings.region_selector.split(","),
          title_selectors = self.settings.title_selector.split(",");

      for (var i = 0, len = region_selectors.length; i < len; i++) {
        var region_selector = region_selectors[i];

        for (var j = 0, len1 = title_selectors.length; j < len1; j++) {
          var title_selector = section_selector + ">" + region_selector + ">" + title_selectors[j];

          click_title_selectors.push(title_selector + " a"); //or we can not do preventDefault for click event of <a>
          click_title_selectors.push(title_selector);
        }
      }

      $(self.scope)
        .on('click.fndtn.section', click_title_selectors.join(","), function(e) {
          var title = $(this).closest(self.settings.title_selector);

          self.close_navs(title);
          if (title.siblings(self.settings.content_selector).length > 0) {
            self.toggle_active.call(title[0], e);
          }
        });

      $(window)
        .on('resize.fndtn.section', self.throttle(function() { self.resize(); }, 30))
        .on('hashchange.fndtn.section', self.set_active_from_hash);

      $(document).on('click.fndtn.section', function (e) {
        if (e.isPropagationStopped && e.isPropagationStopped()) return;
        if (e.target === document) return;
        self.close_navs($(e.target).closest(self.settings.title_selector));
      });

      $(window).triggerHandler('resize.fndtn.section');
      $(window).triggerHandler('hashchange.fndtn.section');
    },
    
    //close nav !one_up on click elsewhere
    close_navs: function(except_nav_with_title) {
      var self = Foundation.libs.section,
          navsToClose = $(self.settings.nav_selector)
            .filter(function() { return !$.extend({}, 
              self.settings, self.data_options($(this))).one_up; });

      if (except_nav_with_title.length > 0) {
        var section = except_nav_with_title.parent().parent();

        if (self.is_horizontal_nav(section) || self.is_vertical_nav(section)) {
          //exclude current nav from list
          navsToClose = navsToClose.filter(function() { return this !== section[0]; });
        }
      }
      //close navs on click on title
      navsToClose.children(self.settings.region_selector).removeClass(self.settings.active_class);
    },

    toggle_active: function(e) {
      var $this = $(this),
          self = Foundation.libs.section,
          region = $this.parent(),
          content = $this.siblings(self.settings.content_selector),
          section = region.parent(),
          settings = $.extend({}, self.settings, self.data_options(section)),
          prev_active_region = section.children(self.settings.region_selector).filter("." + self.settings.active_class);

      //for anchors inside [data-section-title]
      if (!settings.deep_linking && content.length > 0) {
        e.preventDefault();
      }

      e.stopPropagation(); //do not catch same click again on parent

      if (!region.hasClass(self.settings.active_class)) {
        if (!self.is_accordion(section) || (self.is_accordion(section) && !self.settings.multi_expand)) {
          prev_active_region.removeClass(self.settings.active_class);
          prev_active_region.trigger('closed.fndtn.section');
        }
        region.addClass(self.settings.active_class);
        //force resize for better performance (do not wait timer)
        self.resize(region.find(self.settings.section_selector).not("[" + self.settings.resized_data_attr + "]"), true);
        region.trigger('opened.fndtn.section');
      } else if (region.hasClass(self.settings.active_class) && self.is_accordion(section) || !settings.one_up && (self.small(section) || self.is_vertical_nav(section) || self.is_horizontal_nav(section) || self.is_accordion(section))) {
        region.removeClass(self.settings.active_class);
        region.trigger('closed.fndtn.section');
      }
      settings.callback(section);
    },

    check_resize_timer: null,

    //main function that sets title and content positions; runs for :not(.resized) and :visible once when window width is medium up
    //sections:
    //  selected sections to resize, are defined on resize forced by visibility changes
    //ensure_has_active_region:
    //  is true when we force resize for no resized sections that were hidden and became visible, 
    //  these sections can have no selected region, because all regions were hidden along with section on executing set_active_from_hash
    resize: function(sections, ensure_has_active_region) {

      var self = Foundation.libs.section,
          section_container = $(self.settings.section_selector),
          is_small_window = self.small(section_container),
          //filter for section resize
          should_be_resized = function (section, now_is_hidden) {
            return !self.is_accordion(section) && 
              !section.is("[" + self.settings.resized_data_attr + "]") && 
              (!is_small_window || self.is_horizontal_tabs(section)) && 
              now_is_hidden === (section.css('display') === 'none' || 
              !section.parent().is(':visible'));
          };

      sections = sections || $(self.settings.section_selector);

      clearTimeout(self.check_resize_timer);

      if (!is_small_window) {
        sections.removeAttr(self.settings.small_style_data_attr);
      }

      //resize
      sections.filter(function() { return should_be_resized($(this), false); })
        .each(function() {
          var section = $(this),
            regions = section.children(self.settings.region_selector),
            titles = regions.children(self.settings.title_selector),
            content = regions.children(self.settings.content_selector),
            titles_max_height = 0;

          if (ensure_has_active_region && 
            section.children(self.settings.region_selector).filter("." + self.settings.active_class).length == 0) {
            var settings = $.extend({}, self.settings, self.data_options(section));

            if (!settings.deep_linking && (settings.one_up || !self.is_horizontal_nav(section) &&
              !self.is_vertical_nav(section) && !self.is_accordion(section))) {
                regions.filter(":visible").first().addClass(self.settings.active_class);
            }
          }

          if (self.is_horizontal_tabs(section) || self.is_auto(section)) {
            //    region: position relative
            //    title: position absolute
            //    content: position static
            var titles_sum_width = 0;

            titles.each(function() {
              var title = $(this);

              if (title.is(":visible")) {
                title.css(!self.rtl ? 'left' : 'right', titles_sum_width);
                var title_h_border_width = parseInt(title.css("border-" + (self.rtl ? 'left' : 'right') + "-width"), 10);

                if (title_h_border_width.toString() === 'Nan') {
                  title_h_border_width = 0;
                }

                titles_sum_width += self.outerWidth(title) - title_h_border_width;
                titles_max_height = Math.max(titles_max_height, self.outerHeight(title));
              }
            });
            titles.css('height', titles_max_height);
            regions.each(function() {
              var region = $(this),
                  region_content = region.children(self.settings.content_selector),
                  content_top_border_width = parseInt(region_content.css("border-top-width"), 10);

              if (content_top_border_width.toString() === 'Nan') {
                content_top_border_width = 0;
              }

              region.css('padding-top', titles_max_height - content_top_border_width);
            });

            section.css("min-height", titles_max_height);
          } else if (self.is_horizontal_nav(section)) {
            var first = true;
            //    region: positon relative, float left
            //    title: position static
            //    content: position absolute
            titles.each(function() {
              titles_max_height = Math.max(titles_max_height, self.outerHeight($(this)));
            });

            regions.each(function() {
              var region = $(this);

              region.css("margin-left", "-" + (first ? section : region.children(self.settings.title_selector)).css("border-left-width"));
              first = false;
            });

            regions.css("margin-top", "-" + section.css("border-top-width"));
            titles.css('height', titles_max_height);
            content.css('top', titles_max_height);
            section.css("min-height", titles_max_height);
          } else if (self.is_vertical_tabs(section)) {
            var titles_sum_height = 0;
            //    region: position relative, for .active: fixed padding==title.width
            //    title: fixed width, position absolute
            //    content: position static
            titles.each(function() {
              var title = $(this);

              if (title.is(":visible")) {
                title.css('top', titles_sum_height);
                var title_top_border_width = parseInt(title.css("border-top-width"), 10);

                if (title_top_border_width.toString() === 'Nan') {
                  title_top_border_width = 0;
                }

                titles_sum_height += self.outerHeight(title) - title_top_border_width;
              }
            });

            content.css('min-height', titles_sum_height + 1);
          } else if (self.is_vertical_nav(section)) {
            var titles_max_width = 0,
                first1 = true;
            //    region: positon relative
            //    title: position static
            //    content: position absolute
            titles.each(function() {
              titles_max_width = Math.max(titles_max_width, self.outerWidth($(this)));
            });

            regions.each(function () {
              var region = $(this);

              region.css("margin-top", "-" + (first1 ? section : region.children(self.settings.title_selector)).css("border-top-width"));
              first1 = false;
            });

            titles.css('width', titles_max_width);
            content.css(!self.rtl ? 'left' : 'right', titles_max_width);
            section.css('width', titles_max_width);
          }

          section.attr(self.settings.resized_data_attr, true);
        });

      //wait elements to become visible then resize
      if ($(self.settings.section_selector).filter(function() { return should_be_resized($(this), true); }).length > 0)
        self.check_resize_timer = setTimeout(function() {
          self.resize(sections.filter(function() { return should_be_resized($(this), false); }), true);
        }, 700);

      if (is_small_window) {
        sections.attr(self.settings.small_style_data_attr, true);
      }
    },

    is_vertical_nav: function(el) {
      return /vertical-nav/i.test(el.data('section'));
    },

    is_horizontal_nav: function(el) {
      return /horizontal-nav/i.test(el.data('section'));
    },

    is_accordion: function(el) {
      return /accordion/i.test(el.data('section'));
    },

    is_horizontal_tabs: function(el) {
      return /^tabs$/i.test(el.data('section'));
    },

    is_vertical_tabs: function(el) {
      return /vertical-tabs/i.test(el.data('section'));
    },
    
    is_auto: function (el) {
      var data_section = el.data('section');
      return data_section === '' || /auto/i.test(data_section);
    },

    set_active_from_hash: function() {
      var self = Foundation.libs.section,
          hash = window.location.hash.substring(1),
          sections = $(self.settings.section_selector);

      var selectedSection;
      
      sections.each(function() {
          var section = $(this),
          regions = section.children(self.settings.region_selector);
          regions.each(function() {
            var region = $(this),
            data_slug = region.children(self.settings.content_selector).data('slug');
            if (new RegExp(data_slug, 'i').test(hash)) {
              selectedSection=section;
              return false;
              }
          });
          
          if (selectedSection != null) {
            return false;
          }
      });
      
      if (selectedSection != null) {
        sections.each(function() {
          if (selectedSection == $(this)) {
            var section = $(this),
                settings = $.extend({}, self.settings, self.data_options(section)),
                regions = section.children(self.settings.region_selector),
                set_active_from_hash = settings.deep_linking && hash.length > 0,
                selected = false;
    
            regions.each(function() {
              var region = $(this);
    
              if (selected) {
                region.removeClass(self.settings.active_class);
              } else if (set_active_from_hash) {
                var data_slug = region.children(self.settings.content_selector).data('slug');
    
                if (data_slug && new RegExp(data_slug, 'i').test(hash)) {
                  if (!region.hasClass(self.settings.active_class))
                    region.addClass(self.settings.active_class);
                  selected = true;
                } else {
                  region.removeClass(self.settings.active_class);
                }
              } else if (region.hasClass(self.settings.active_class)) {
                selected = true;
              }
            });
    
            if (!selected && (settings.one_up || !self.is_horizontal_nav(section) &&
             !self.is_vertical_nav(section) && !self.is_accordion(section)))
              regions.filter(":visible").first().addClass(self.settings.active_class);
          }
        });
      }
    },

    reflow: function() {
      var self = Foundation.libs.section;

      $(self.settings.section_selector).removeAttr(self.settings.resized_data_attr);
      self.throttle(function() { self.resize(); }, 30)();
    },

    small: function(el) {
      var settings = $.extend({}, this.settings, this.data_options(el));

      if (this.is_horizontal_tabs(el)) {
        return false;
      }
      if (el && this.is_accordion(el)) {
        return true;
      }
      if ($('html').hasClass('lt-ie9')) {
        return true;
      }
      if ($('html').hasClass('ie8compat')) {
        return true;
      }
      return $(this.scope).width() < settings.small_breakpoint;
    },

    off: function() {
      $(this.scope).off('.fndtn.section');
      $(window).off('.fndtn.section');
      $(document).off('.fndtn.section');
    }
  };

  //resize selected sections
  $.fn.reflow_section = function(ensure_has_active_region) {
    var section = this,
        self = Foundation.libs.section;

    section.removeAttr(self.settings.resized_data_attr);
    self.throttle(function() { self.resize(section, ensure_has_active_region); }, 30)();
    return this;
  };

}(Foundation.zj, window, document));