<commit_before>;(function ($, window, document, undefined) {
  'use strict';

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

    version : '{{VERSION}}',

    settings : {
      active_class : 'open',
      disabled_class : 'disabled',
      mega_class : 'mega',
      align : 'bottom',
      is_hover : false,
      hover_timeout : 150,
      opened : function () {},
      closed : function () {}
    },

    init : function (scope, method, options) {
      Foundation.inherit(this, 'throttle');

      $.extend(true, this.settings, method, options);
      this.bindings(method, options);
    },

    events : function (scope) {
      var self = this,
          S = self.S;

      S(this.scope)
        .off('.dropdown')
        .on('click.fndtn.dropdown', '[' + this.attr_name() + ']', function (e) {
          var settings = S(this).data(self.attr_name(true) + '-init') || self.settings;
          if (!settings.is_hover || Modernizr.touch) {
            e.preventDefault();
            if (S(this).parent('[data-reveal-id]').length) {
              e.stopPropagation();
            }
            self.toggle($(this));
          }
        })
        .on('mouseenter.fndtn.dropdown', '[' + this.attr_name() + '], [' + this.attr_name() + '-content]', function (e) {
          var $this = S(this),
              dropdown,
              target;

          clearTimeout(self.timeout);

          if ($this.data(self.data_attr())) {
            dropdown = S('#' + $this.data(self.data_attr()));
            target = $this;
          } else {
            dropdown = $this;
            target = S('[' + self.attr_name() + '="' + dropdown.attr('id') + '"]');
          }

          var settings = target.data(self.attr_name(true) + '-init') || self.settings;

          if (S(e.currentTarget).data(self.data_attr()) && settings.is_hover) {
            self.closeall.call(self);
          }

          if (settings.is_hover) {
            self.open.apply(self, [dropdown, target]);
          }
        })
        .on('mouseleave.fndtn.dropdown', '[' + this.attr_name() + '], [' + this.attr_name() + '-content]', function (e) {
          var $this = S(this);
          var settings;

          if ($this.data(self.data_attr())) {
              settings = $this.data(self.data_attr(true) + '-init') || self.settings;
          } else {
              var target   = S('[' + self.attr_name() + '="' + S(this).attr('id') + '"]'),
                  settings = target.data(self.attr_name(true) + '-init') || self.settings;
          }

          self.timeout = setTimeout(function () {
            if ($this.data(self.data_attr())) {
              if (settings.is_hover) {
                self.close.call(self, S('#' + $this.data(self.data_attr())));
              }
            } else {
              if (settings.is_hover) {
                self.close.call(self, $this);
              }
            }
          }.bind(this), settings.hover_timeout);
        })
        .on('click.fndtn.dropdown', function (e) {
          var parent = S(e.target).closest('[' + self.attr_name() + '-content]');
          var links  = parent.find('a');

          if (links.length > 0 && parent.attr('aria-autoclose') !== 'false') {
              self.close.call(self, S('[' + self.attr_name() + '-content]'));
          }

          if (e.target !== document && !$.contains(document.documentElement, e.target)) {
            return;
          }

          if (S(e.target).closest('[' + self.attr_name() + ']').length > 0) {
            return;
          }

          if (!(S(e.target).data('revealId')) &&
            (parent.length > 0 && (S(e.target).is('[' + self.attr_name() + '-content]') ||
              $.contains(parent.first()[0], e.target)))) {
            e.stopPropagation();
            return;
          }

          self.close.call(self, S('[' + self.attr_name() + '-content]'));
        })
        .on('opened.fndtn.dropdown', '[' + self.attr_name() + '-content]', function () {
          self.settings.opened.call(this);
        })
        .on('closed.fndtn.dropdown', '[' + self.attr_name() + '-content]', function () {
          self.settings.closed.call(this);
        });

      S(window)
        .off('.dropdown')
        .on('resize.fndtn.dropdown', self.throttle(function () {
          self.resize.call(self);
        }, 50));

      this.resize();
    },

    close : function (dropdown) {
      var self = this;
      dropdown.each(function (idx) {
        var original_target = $('[' + self.attr_name() + '=' + dropdown[idx].id + ']') || $('aria-controls=' + dropdown[idx].id + ']');
        original_target.attr('aria-expanded', 'false');
        if (self.S(this).hasClass(self.settings.active_class)) {
          self.S(this)
            .css(Foundation.rtl ? 'right' : 'left', '-99999px')
            .attr('aria-hidden', 'true')
            .removeClass(self.settings.active_class)
            .prev('[' + self.attr_name() + ']')
            .removeClass(self.settings.active_class)
            .removeData('target');

          self.S(this).trigger('closed.fndtn.dropdown', [dropdown]);
        }
      });
      dropdown.removeClass('f-open-' + this.attr_name(true));
    },

    closeall : function () {
      var self = this;
      $.each(self.S('.f-open-' + this.attr_name(true)), function () {
        self.close.call(self, self.S(this));
      });
    },

    open : function (dropdown, target) {
      this
        .css(dropdown
        .addClass(this.settings.active_class), target);
      dropdown.prev('[' + this.attr_name() + ']').addClass(this.settings.active_class);
      dropdown.data('target', target.get(0)).trigger('opened.fndtn.dropdown', [dropdown, target]);
      dropdown.attr('aria-hidden', 'false');
      target.attr('aria-expanded', 'true');
      dropdown.focus();
      dropdown.addClass('f-open-' + this.attr_name(true));
    },

    data_attr : function () {
      if (this.namespace.length > 0) {
        return this.namespace + '-' + this.name;
      }

      return this.name;
    },

    toggle : function (target) {
      if (target.hasClass(this.settings.disabled_class)) {
        return;
      }
      var dropdown = this.S('#' + target.data(this.data_attr()));
      if (dropdown.length === 0) {
        // No dropdown found, not continuing
        return;
      }

      this.close.call(this, this.S('[' + this.attr_name() + '-content]').not(dropdown));

      if (dropdown.hasClass(this.settings.active_class)) {
        this.close.call(this, dropdown);
        if (dropdown.data('target') !== target.get(0)) {
          this.open.call(this, dropdown, target);
        }
      } else {
        this.open.call(this, dropdown, target);
      }
    },

    resize : function () {
      var dropdown = this.S('[' + this.attr_name() + '-content].open');
      var target = $(dropdown.data("target"));

      if (dropdown.length && target.length) {
        this.css(dropdown, target);
      }
    },

    css : function (dropdown, target) {
      var left_offset = Math.max((target.width() - dropdown.width()) / 2, 8),
          settings = target.data(this.attr_name(true) + '-init') || this.settings,
          parentOverflow = dropdown.parent().css('overflow-y') || dropdown.parent().css('overflow');

      this.clear_idx();

      if (this.small()) {
        var p = this.dirs.bottom.call(dropdown, target, settings);

        dropdown.attr('style', '').removeClass('drop-left drop-right drop-top').css({
          position : 'absolute',
          width : '95%',
          'max-width' : 'none',
          top : p.top
        });

        dropdown.css(Foundation.rtl ? 'right' : 'left', left_offset);
      }
      // detect if dropdown is in an overflow container
      else if (parentOverflow !== 'visible') {
        var offset = target[0].offsetTop + target[0].offsetHeight;

        dropdown.attr('style', '').css({
          position : 'absolute',
          top : offset
        });

        dropdown.css(Foundation.rtl ? 'right' : 'left', left_offset);
      }
      else {

        this.style(dropdown, target, settings);
      }

      return dropdown;
    },

    style : function (dropdown, target, settings) {
      var css = $.extend({position : 'absolute'},
        this.dirs[settings.align].call(dropdown, target, settings));

      dropdown.attr('style', '').css(css);
    },

    // return CSS property object
    // `this` is the dropdown
    dirs : {
      // Calculate target offset
      _base : function (t, s) {
        var o_p = this.offsetParent(),
            o = o_p.offset(),
            p = t.offset();

        p.top -= o.top;
        p.left -= o.left;

        //set some flags on the p object to pass along
        p.missRight = false;
        p.missTop = false;
        p.missLeft = false;
        p.leftRightFlag = false;

        //lets see if the panel will be off the screen
        //get the actual width of the page and store it
        var actualBodyWidth;
        if (document.getElementsByClassName('row')[0]) {
          actualBodyWidth = document.getElementsByClassName('row')[0].clientWidth;
        } else {
          actualBodyWidth = window.innerWidth;
        }

        var actualMarginWidth = (window.innerWidth - actualBodyWidth) / 2;
        var actualBoundary = actualBodyWidth;

        if (!this.hasClass('mega') && !s.ignore_repositioning) {
		  var outerWidth = this.outerWidth();
		  var o_left = t.offset().left;

          //miss top
          if (t.offset().top <= this.outerHeight()) {
            p.missTop = true;
            actualBoundary = window.innerWidth - actualMarginWidth;
            p.leftRightFlag = true;
          }

          //miss right
          if (o_left + outerWidth > o_left + actualMarginWidth && o_left - actualMarginWidth > outerWidth) {
            p.missRight = true;
            p.missLeft = false;
          }

          //miss left
          if (o_left - outerWidth <= 0) {
            p.missLeft = true;
            p.missRight = false;
          }
        }

        return p;
      },

      top : function (t, s) {
        var self = Foundation.libs.dropdown,
            p = self.dirs._base.call(this, t, s);

        this.addClass('drop-top');

        if (p.missTop == true) {
          p.top = p.top + t.outerHeight() + this.outerHeight();
          this.removeClass('drop-top');
        }

        if (p.missRight == true) {
          p.left = p.left - this.outerWidth() + t.outerWidth();
        }

        if (t.outerWidth() < this.outerWidth() || self.small() || this.hasClass(s.mega_menu)) {
          self.adjust_pip(this, t, s, p);
        }

        if (Foundation.rtl) {
          return {left : p.left - this.outerWidth() + t.outerWidth(),
            top : p.top - this.outerHeight()};
        }

        return {left : p.left, top : p.top - this.outerHeight()};
      },

      bottom : function (t, s) {
        var self = Foundation.libs.dropdown,
            p = self.dirs._base.call(this, t, s);

        if (p.missRight == true) {
          p.left = p.left - this.outerWidth() + t.outerWidth();
        }

        if (t.outerWidth() < this.outerWidth() || self.small() || this.hasClass(s.mega_menu)) {
          self.adjust_pip(this, t, s, p);
        }

        if (self.rtl) {
          return {left : p.left - this.outerWidth() + t.outerWidth(), top : p.top + t.outerHeight()};
        }

        return {left : p.left, top : p.top + t.outerHeight()};
      },

      left : function (t, s) {
        var p = Foundation.libs.dropdown.dirs._base.call(this, t, s);

        this.addClass('drop-left');

        if (p.missLeft == true) {
          p.left =  p.left + this.outerWidth();
          p.top = p.top + t.outerHeight();
          this.removeClass('drop-left');
        }

        return {left : p.left - this.outerWidth(), top : p.top};
      },

      right : function (t, s) {
        var p = Foundation.libs.dropdown.dirs._base.call(this, t, s);

        this.addClass('drop-right');

        if (p.missRight == true) {
          p.left = p.left - this.outerWidth();
          p.top = p.top + t.outerHeight();
          this.removeClass('drop-right');
        } else {
          p.triggeredRight = true;
        }

        var self = Foundation.libs.dropdown;

        if (t.outerWidth() < this.outerWidth() || self.small() || this.hasClass(s.mega_menu)) {
          self.adjust_pip(this, t, s, p);
        }

        return {left : p.left + t.outerWidth(), top : p.top};
      }
    },

    // Insert rule to style psuedo elements
    adjust_pip : function (dropdown, target, settings, position) {
      var sheet = Foundation.stylesheet,
          pip_offset_base = 8;

      if (dropdown.hasClass(settings.mega_class)) {
        pip_offset_base = position.left + (target.outerWidth() / 2) - 8;
      } else if (this.small()) {
        pip_offset_base += position.left - 8;
      }

      this.rule_idx = sheet.cssRules.length;

      //default
      var sel_before = '.f-dropdown.open:before',
          sel_after  = '.f-dropdown.open:after',
          css_before = 'left: ' + pip_offset_base + 'px;',
          css_after  = 'left: ' + (pip_offset_base - 1) + 'px;';

      if (position.missRight == true) {
        pip_offset_base = dropdown.outerWidth() - 23;
        sel_before = '.f-dropdown.open:before',
        sel_after  = '.f-dropdown.open:after',
        css_before = 'left: ' + pip_offset_base + 'px;',
        css_after  = 'left: ' + (pip_offset_base - 1) + 'px;';
      }

      //just a case where right is fired, but its not missing right
      if (position.triggeredRight == true) {
        sel_before = '.f-dropdown.open:before',
        sel_after  = '.f-dropdown.open:after',
        css_before = 'left:-12px;',
        css_after  = 'left:-14px;';
      }

      if (sheet.insertRule) {
        sheet.insertRule([sel_before, '{', css_before, '}'].join(' '), this.rule_idx);
        sheet.insertRule([sel_after, '{', css_after, '}'].join(' '), this.rule_idx + 1);
      } else {
        sheet.addRule(sel_before, css_before, this.rule_idx);
        sheet.addRule(sel_after, css_after, this.rule_idx + 1);
      }
    },

    // Remove old dropdown rule index
    clear_idx : function () {
      var sheet = Foundation.stylesheet;

      if (typeof this.rule_idx !== 'undefined') {
        sheet.deleteRule(this.rule_idx);
        sheet.deleteRule(this.rule_idx);
        delete this.rule_idx;
      }
    },

    small : function () {
      return matchMedia(Foundation.media_queries.small).matches &&
        !matchMedia(Foundation.media_queries.medium).matches;
    },

    off : function () {
      this.S(this.scope).off('.fndtn.dropdown');
      this.S('html, body').off('.fndtn.dropdown');
      this.S(window).off('.fndtn.dropdown');
      this.S('[data-dropdown-content]').off('.fndtn.dropdown');
    },

    reflow : function () {}
  };
}(jQuery, window, window.document));
<commit_msg>windowWidth variable added; indentation<commit_after>;(function ($, window, document, undefined) {
  'use strict';

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

    version : '{{VERSION}}',

    settings : {
      active_class : 'open',
      disabled_class : 'disabled',
      mega_class : 'mega',
      align : 'bottom',
      is_hover : false,
      hover_timeout : 150,
      opened : function () {},
      closed : function () {}
    },

    init : function (scope, method, options) {
      Foundation.inherit(this, 'throttle');

      $.extend(true, this.settings, method, options);
      this.bindings(method, options);
    },

    events : function (scope) {
      var self = this,
          S = self.S;

      S(this.scope)
        .off('.dropdown')
        .on('click.fndtn.dropdown', '[' + this.attr_name() + ']', function (e) {
          var settings = S(this).data(self.attr_name(true) + '-init') || self.settings;
          if (!settings.is_hover || Modernizr.touch) {
            e.preventDefault();
            if (S(this).parent('[data-reveal-id]').length) {
              e.stopPropagation();
            }
            self.toggle($(this));
          }
        })
        .on('mouseenter.fndtn.dropdown', '[' + this.attr_name() + '], [' + this.attr_name() + '-content]', function (e) {
          var $this = S(this),
              dropdown,
              target;

          clearTimeout(self.timeout);

          if ($this.data(self.data_attr())) {
            dropdown = S('#' + $this.data(self.data_attr()));
            target = $this;
          } else {
            dropdown = $this;
            target = S('[' + self.attr_name() + '="' + dropdown.attr('id') + '"]');
          }

          var settings = target.data(self.attr_name(true) + '-init') || self.settings;

          if (S(e.currentTarget).data(self.data_attr()) && settings.is_hover) {
            self.closeall.call(self);
          }

          if (settings.is_hover) {
            self.open.apply(self, [dropdown, target]);
          }
        })
        .on('mouseleave.fndtn.dropdown', '[' + this.attr_name() + '], [' + this.attr_name() + '-content]', function (e) {
          var $this = S(this);
          var settings;

          if ($this.data(self.data_attr())) {
              settings = $this.data(self.data_attr(true) + '-init') || self.settings;
          } else {
              var target   = S('[' + self.attr_name() + '="' + S(this).attr('id') + '"]'),
                  settings = target.data(self.attr_name(true) + '-init') || self.settings;
          }

          self.timeout = setTimeout(function () {
            if ($this.data(self.data_attr())) {
              if (settings.is_hover) {
                self.close.call(self, S('#' + $this.data(self.data_attr())));
              }
            } else {
              if (settings.is_hover) {
                self.close.call(self, $this);
              }
            }
          }.bind(this), settings.hover_timeout);
        })
        .on('click.fndtn.dropdown', function (e) {
          var parent = S(e.target).closest('[' + self.attr_name() + '-content]');
          var links  = parent.find('a');

          if (links.length > 0 && parent.attr('aria-autoclose') !== 'false') {
              self.close.call(self, S('[' + self.attr_name() + '-content]'));
          }

          if (e.target !== document && !$.contains(document.documentElement, e.target)) {
            return;
          }

          if (S(e.target).closest('[' + self.attr_name() + ']').length > 0) {
            return;
          }

          if (!(S(e.target).data('revealId')) &&
            (parent.length > 0 && (S(e.target).is('[' + self.attr_name() + '-content]') ||
              $.contains(parent.first()[0], e.target)))) {
            e.stopPropagation();
            return;
          }

          self.close.call(self, S('[' + self.attr_name() + '-content]'));
        })
        .on('opened.fndtn.dropdown', '[' + self.attr_name() + '-content]', function () {
          self.settings.opened.call(this);
        })
        .on('closed.fndtn.dropdown', '[' + self.attr_name() + '-content]', function () {
          self.settings.closed.call(this);
        });

      S(window)
        .off('.dropdown')
        .on('resize.fndtn.dropdown', self.throttle(function () {
          self.resize.call(self);
        }, 50));

      this.resize();
    },

    close : function (dropdown) {
      var self = this;
      dropdown.each(function (idx) {
        var original_target = $('[' + self.attr_name() + '=' + dropdown[idx].id + ']') || $('aria-controls=' + dropdown[idx].id + ']');
        original_target.attr('aria-expanded', 'false');
        if (self.S(this).hasClass(self.settings.active_class)) {
          self.S(this)
            .css(Foundation.rtl ? 'right' : 'left', '-99999px')
            .attr('aria-hidden', 'true')
            .removeClass(self.settings.active_class)
            .prev('[' + self.attr_name() + ']')
            .removeClass(self.settings.active_class)
            .removeData('target');

          self.S(this).trigger('closed.fndtn.dropdown', [dropdown]);
        }
      });
      dropdown.removeClass('f-open-' + this.attr_name(true));
    },

    closeall : function () {
      var self = this;
      $.each(self.S('.f-open-' + this.attr_name(true)), function () {
        self.close.call(self, self.S(this));
      });
    },

    open : function (dropdown, target) {
      this
        .css(dropdown
        .addClass(this.settings.active_class), target);
      dropdown.prev('[' + this.attr_name() + ']').addClass(this.settings.active_class);
      dropdown.data('target', target.get(0)).trigger('opened.fndtn.dropdown', [dropdown, target]);
      dropdown.attr('aria-hidden', 'false');
      target.attr('aria-expanded', 'true');
      dropdown.focus();
      dropdown.addClass('f-open-' + this.attr_name(true));
    },

    data_attr : function () {
      if (this.namespace.length > 0) {
        return this.namespace + '-' + this.name;
      }

      return this.name;
    },

    toggle : function (target) {
      if (target.hasClass(this.settings.disabled_class)) {
        return;
      }
      var dropdown = this.S('#' + target.data(this.data_attr()));
      if (dropdown.length === 0) {
        // No dropdown found, not continuing
        return;
      }

      this.close.call(this, this.S('[' + this.attr_name() + '-content]').not(dropdown));

      if (dropdown.hasClass(this.settings.active_class)) {
        this.close.call(this, dropdown);
        if (dropdown.data('target') !== target.get(0)) {
          this.open.call(this, dropdown, target);
        }
      } else {
        this.open.call(this, dropdown, target);
      }
    },

    resize : function () {
      var dropdown = this.S('[' + this.attr_name() + '-content].open');
      var target = $(dropdown.data("target"));

      if (dropdown.length && target.length) {
        this.css(dropdown, target);
      }
    },

    css : function (dropdown, target) {
      var left_offset = Math.max((target.width() - dropdown.width()) / 2, 8),
          settings = target.data(this.attr_name(true) + '-init') || this.settings,
          parentOverflow = dropdown.parent().css('overflow-y') || dropdown.parent().css('overflow');

      this.clear_idx();

      if (this.small()) {
        var p = this.dirs.bottom.call(dropdown, target, settings);

        dropdown.attr('style', '').removeClass('drop-left drop-right drop-top').css({
          position : 'absolute',
          width : '95%',
          'max-width' : 'none',
          top : p.top
        });

        dropdown.css(Foundation.rtl ? 'right' : 'left', left_offset);
      }
      // detect if dropdown is in an overflow container
      else if (parentOverflow !== 'visible') {
        var offset = target[0].offsetTop + target[0].offsetHeight;

        dropdown.attr('style', '').css({
          position : 'absolute',
          top : offset
        });

        dropdown.css(Foundation.rtl ? 'right' : 'left', left_offset);
      }
      else {

        this.style(dropdown, target, settings);
      }

      return dropdown;
    },

    style : function (dropdown, target, settings) {
      var css = $.extend({position : 'absolute'},
        this.dirs[settings.align].call(dropdown, target, settings));

      dropdown.attr('style', '').css(css);
    },

    // return CSS property object
    // `this` is the dropdown
    dirs : {
      // Calculate target offset
      _base : function (t, s) {
        var o_p = this.offsetParent(),
            o = o_p.offset(),
            p = t.offset();

        p.top -= o.top;
        p.left -= o.left;

        //set some flags on the p object to pass along
        p.missRight = false;
        p.missTop = false;
        p.missLeft = false;
        p.leftRightFlag = false;

        //lets see if the panel will be off the screen
        //get the actual width of the page and store it
        var actualBodyWidth;
        var windowWidth = window.innerWidth;

        if (document.getElementsByClassName('row')[0]) {
          actualBodyWidth = document.getElementsByClassName('row')[0].clientWidth;
        } else {
          actualBodyWidth = windowWidth;
        }

        var actualMarginWidth = (windowWidth - actualBodyWidth) / 2;
        var actualBoundary = actualBodyWidth;

        if (!this.hasClass('mega') && !s.ignore_repositioning) {
          var outerWidth = this.outerWidth();
          var o_left = t.offset().left;

          //miss top
          if (t.offset().top <= this.outerHeight()) {
            p.missTop = true;
            actualBoundary = windowWidth - actualMarginWidth;
            p.leftRightFlag = true;
          }

          //miss right
          if (o_left + outerWidth > o_left + actualMarginWidth && o_left - actualMarginWidth > outerWidth) {
            p.missRight = true;
            p.missLeft = false;
          }

          //miss left
          if (o_left - outerWidth <= 0) {
            p.missLeft = true;
            p.missRight = false;
          }
        }

        return p;
      },

      top : function (t, s) {
        var self = Foundation.libs.dropdown,
            p = self.dirs._base.call(this, t, s);

        this.addClass('drop-top');

        if (p.missTop == true) {
          p.top = p.top + t.outerHeight() + this.outerHeight();
          this.removeClass('drop-top');
        }

        if (p.missRight == true) {
          p.left = p.left - this.outerWidth() + t.outerWidth();
        }

        if (t.outerWidth() < this.outerWidth() || self.small() || this.hasClass(s.mega_menu)) {
          self.adjust_pip(this, t, s, p);
        }

        if (Foundation.rtl) {
          return {left : p.left - this.outerWidth() + t.outerWidth(),
            top : p.top - this.outerHeight()};
        }

        return {left : p.left, top : p.top - this.outerHeight()};
      },

      bottom : function (t, s) {
        var self = Foundation.libs.dropdown,
            p = self.dirs._base.call(this, t, s);

        if (p.missRight == true) {
          p.left = p.left - this.outerWidth() + t.outerWidth();
        }

        if (t.outerWidth() < this.outerWidth() || self.small() || this.hasClass(s.mega_menu)) {
          self.adjust_pip(this, t, s, p);
        }

        if (self.rtl) {
          return {left : p.left - this.outerWidth() + t.outerWidth(), top : p.top + t.outerHeight()};
        }

        return {left : p.left, top : p.top + t.outerHeight()};
      },

      left : function (t, s) {
        var p = Foundation.libs.dropdown.dirs._base.call(this, t, s);

        this.addClass('drop-left');

        if (p.missLeft == true) {
          p.left =  p.left + this.outerWidth();
          p.top = p.top + t.outerHeight();
          this.removeClass('drop-left');
        }

        return {left : p.left - this.outerWidth(), top : p.top};
      },

      right : function (t, s) {
        var p = Foundation.libs.dropdown.dirs._base.call(this, t, s);

        this.addClass('drop-right');

        if (p.missRight == true) {
          p.left = p.left - this.outerWidth();
          p.top = p.top + t.outerHeight();
          this.removeClass('drop-right');
        } else {
          p.triggeredRight = true;
        }

        var self = Foundation.libs.dropdown;

        if (t.outerWidth() < this.outerWidth() || self.small() || this.hasClass(s.mega_menu)) {
          self.adjust_pip(this, t, s, p);
        }

        return {left : p.left + t.outerWidth(), top : p.top};
      }
    },

    // Insert rule to style psuedo elements
    adjust_pip : function (dropdown, target, settings, position) {
      var sheet = Foundation.stylesheet,
          pip_offset_base = 8;

      if (dropdown.hasClass(settings.mega_class)) {
        pip_offset_base = position.left + (target.outerWidth() / 2) - 8;
      } else if (this.small()) {
        pip_offset_base += position.left - 8;
      }

      this.rule_idx = sheet.cssRules.length;

      //default
      var sel_before = '.f-dropdown.open:before',
          sel_after  = '.f-dropdown.open:after',
          css_before = 'left: ' + pip_offset_base + 'px;',
          css_after  = 'left: ' + (pip_offset_base - 1) + 'px;';

      if (position.missRight == true) {
        pip_offset_base = dropdown.outerWidth() - 23;
        sel_before = '.f-dropdown.open:before',
        sel_after  = '.f-dropdown.open:after',
        css_before = 'left: ' + pip_offset_base + 'px;',
        css_after  = 'left: ' + (pip_offset_base - 1) + 'px;';
      }

      //just a case where right is fired, but its not missing right
      if (position.triggeredRight == true) {
        sel_before = '.f-dropdown.open:before',
        sel_after  = '.f-dropdown.open:after',
        css_before = 'left:-12px;',
        css_after  = 'left:-14px;';
      }

      if (sheet.insertRule) {
        sheet.insertRule([sel_before, '{', css_before, '}'].join(' '), this.rule_idx);
        sheet.insertRule([sel_after, '{', css_after, '}'].join(' '), this.rule_idx + 1);
      } else {
        sheet.addRule(sel_before, css_before, this.rule_idx);
        sheet.addRule(sel_after, css_after, this.rule_idx + 1);
      }
    },

    // Remove old dropdown rule index
    clear_idx : function () {
      var sheet = Foundation.stylesheet;

      if (typeof this.rule_idx !== 'undefined') {
        sheet.deleteRule(this.rule_idx);
        sheet.deleteRule(this.rule_idx);
        delete this.rule_idx;
      }
    },

    small : function () {
      return matchMedia(Foundation.media_queries.small).matches &&
        !matchMedia(Foundation.media_queries.medium).matches;
    },

    off : function () {
      this.S(this.scope).off('.fndtn.dropdown');
      this.S('html, body').off('.fndtn.dropdown');
      this.S(window).off('.fndtn.dropdown');
      this.S('[data-dropdown-content]').off('.fndtn.dropdown');
    },

    reflow : function () {}
  };
}(jQuery, window, window.document));
<|endoftext|>


<commit_before>define(
    ['core/module', 'jquery', 'marionette', 'backbone', 'lodash', 'template', 'accounting', 'client/view/client_select'],
    function(Module, $, Mn, Backbone, _, Template, Accounting, ClientSelectView) {
        "use strict";

        var ViewModel = Backbone.Model.extend({
            defaults: {
                subTotal: 0,
                discount: 0,
                tax: 0,
                total: 0
            }
        });

        var InvoiceModel = new (Backbone.Model.extend({
                defaults: {
                    total: 0
                }
            }));

        var viewModel = new ViewModel();

        var Collection = Backbone.Collection.extend({
            initialize: function() {
                this.listenTo(this, 'change reset add remove', this.updateTotals);
            },
            updateTotals: function() {
                var total = 0;

                _.each(this.models, function(model) {
                    total += model.get('total');
                });

                var discount = (total * InvoiceModel.get('total')) / 100;

                viewModel.set('subTotal', total);
                viewModel.set('total', total - discount);
                viewModel.set('discount', discount);

                this.trigger('update:totals');
            }
        });

        return Module.extend({
            regions: {
                'clientInfo': '#client-info',
                'invoiceRows': '#invoice-items'
            },
            _renderClientSelect: function(options) {
                var model = new Backbone.Model(options.client);
                var viewOptions = {type: 'invoice', model: model};

                this.app.getRegion('clientInfo').show(new ClientSelectView(_.merge(options, viewOptions)));
            },
            initialize: function(options) {

                var Model = Backbone.Model.extend({
                    defaults: {
                        fields: options.fieldData,
                        description: '',
                        price: 0,
                        qty: 0,
                        total: 0
                    }
                });

                this._renderClientSelect(options);

                var model = new Model({
                    id: 0
                });

                var collection = new Collection([model]);
                var counter = collection.size();

                var InvoiceView = new (Mn.ItemView.extend({
                    el: '#discount',
                    ui: {
                        discount: '#invoice_discount'
                    },
                    events: {
                        'keyup @ui.discount': 'setDiscount'
                    },
                    setDiscount: function (event) {
                        this.model.set('total', $(event.target).val());

                        collection.trigger('change');
                    }
                }))({model: InvoiceModel});

                var childView = Mn.ItemView.extend({
                    template: Template['invoice/row'],
                    tagName: 'tr',
                    ui: {
                        removeItem: '.remove-item',
                        input: ':input'
                    },
                    events: {
                        'click @ui.removeItem': 'removeItem',
                        'keyup @ui.input': 'calcPrice'
                    },
                    removeItem: function(event) {
                        event.preventDefault();

                        this.model.collection.remove(this.model);
                    },
                    calcPrice: function() {
                        this.$(':input').each(_.bind(function(index, input) {
                            var $this = $(input),
                                type = $this.closest('td')[0].className.split('-')[1],
                                val = $this.val();

                            if ('price' === type) {
                                val = Accounting.unformat(val);
                            }

                            this.model.set(type, val);
                        }, this));

                        var amount = this.model.get('qty') * this.model.get('price');

                        this.model.set('total', amount);
                        this.$('.column-total').html(Accounting.formatMoney(this.model.get('total')));

                        console.log(InvoiceModel);
                    }
                });

                var FooterView = Mn.ItemView.extend({
                    template: Template['invoice/footer']
                });

                var view = Mn.CompositeView.extend({
                    regions: {
                        'invoiceFooter': '#invoice-footer'
                    },
                    template: Template['invoice/table'],
                    childView: childView,
                    collection: collection,
                    childViewContainer: "#invoice-rows",
                    footerView: null,
                    ui: {
                        'addItem': '.add-item'
                    },
                    events: {
                        'click @ui.addItem': 'addItem'
                    },
                    collectionEvents: {
                        "update:totals": "renderTotals"
                    },
                    renderTotals: function() {
                        var footer = this.$('#invoice-footer');

                        setTimeout(_.bind(function() {
                            footer.empty();
                            this.footerView.render().$el.find('tr').appendTo(footer);
                        }, this), 0);
                    },
                    onRender: function() {
                        this.footerView = new FooterView({model: viewModel});

                        this.footerView.render().$el.find('tr').appendTo(this.$('#invoice-footer'));
                    },
                    addItem: function(event) {
                        event.preventDefault();

                        this.collection.add(new Model({
                            id: counter++
                        }));
                    }
                });

                this.app.getRegion('invoiceRows').show(new view());
            }
        });
    }
);

<commit_msg>Remove unnecessary variable and debug code<commit_after>define(
    ['core/module', 'marionette', 'backbone', 'lodash', 'template', 'accounting', 'client/view/client_select'],
    function(Module, Mn, Backbone, _, Template, Accounting, ClientSelectView) {
        "use strict";

        var ViewModel = Backbone.Model.extend({
            defaults: {
                subTotal: 0,
                discount: 0,
                tax: 0,
                total: 0
            }
        });

        var InvoiceModel = new (Backbone.Model.extend({
                defaults: {
                    total: 0
                }
            }));

        var viewModel = new ViewModel();

        var Collection = Backbone.Collection.extend({
            initialize: function() {
                this.listenTo(this, 'change reset add remove', this.updateTotals);
            },
            updateTotals: function() {
                var total = 0;

                _.each(this.models, function(model) {
                    total += model.get('total');
                });

                var discount = (total * InvoiceModel.get('total')) / 100;

                viewModel.set('subTotal', total);
                viewModel.set('total', total - discount);
                viewModel.set('discount', discount);

                this.trigger('update:totals');
            }
        });

        return Module.extend({
            regions: {
                'clientInfo': '#client-info',
                'invoiceRows': '#invoice-items'
            },
            _renderClientSelect: function(options) {
                var model = new Backbone.Model(options.client);
                var viewOptions = {type: 'invoice', model: model};

                this.app.getRegion('clientInfo').show(new ClientSelectView(_.merge(options, viewOptions)));
            },
            initialize: function(options) {

                var Model = Backbone.Model.extend({
                    defaults: {
                        fields: options.fieldData,
                        description: '',
                        price: 0,
                        qty: 0,
                        total: 0
                    }
                });

                this._renderClientSelect(options);

                var model = new Model({
                    id: 0
                });

                var collection = new Collection([model]);
                var counter = collection.size();

                var InvoiceView = new (Mn.ItemView.extend({
                    el: '#discount',
                    ui: {
                        discount: '#invoice_discount'
                    },
                    events: {
                        'keyup @ui.discount': 'setDiscount'
                    },
                    setDiscount: function (event) {
                        this.model.set('total', $(event.target).val());

                        collection.trigger('change');
                    }
                }))({model: InvoiceModel});

                var childView = Mn.ItemView.extend({
                    template: Template['invoice/row'],
                    tagName: 'tr',
                    ui: {
                        removeItem: '.remove-item',
                        input: ':input'
                    },
                    events: {
                        'click @ui.removeItem': 'removeItem',
                        'keyup @ui.input': 'calcPrice'
                    },
                    removeItem: function(event) {
                        event.preventDefault();

                        this.model.collection.remove(this.model);
                    },
                    calcPrice: function() {
                        this.$(':input').each(_.bind(function(index, input) {
                            var $this = $(input),
                                type = $this.closest('td')[0].className.split('-')[1],
                                val = $this.val();

                            if ('price' === type) {
                                val = Accounting.unformat(val);
                            }

                            this.model.set(type, val);
                        }, this));

                        var amount = this.model.get('qty') * this.model.get('price');

                        this.model.set('total', amount);
                        this.$('.column-total').html(Accounting.formatMoney(this.model.get('total')));
                    }
                });

                var FooterView = Mn.ItemView.extend({
                    template: Template['invoice/footer']
                });

                var view = Mn.CompositeView.extend({
                    regions: {
                        'invoiceFooter': '#invoice-footer'
                    },
                    template: Template['invoice/table'],
                    childView: childView,
                    collection: collection,
                    childViewContainer: "#invoice-rows",
                    footerView: null,
                    ui: {
                        'addItem': '.add-item'
                    },
                    events: {
                        'click @ui.addItem': 'addItem'
                    },
                    collectionEvents: {
                        "update:totals": "renderTotals"
                    },
                    renderTotals: function() {
                        var footer = this.$('#invoice-footer');

                        setTimeout(_.bind(function() {
                            footer.empty();
                            this.footerView.render().$el.find('tr').appendTo(footer);
                        }, this), 0);
                    },
                    onRender: function() {
                        this.footerView = new FooterView({model: viewModel});

                        this.footerView.render().$el.find('tr').appendTo(this.$('#invoice-footer'));
                    },
                    addItem: function(event) {
                        event.preventDefault();

                        this.collection.add(new Model({
                            id: counter++
                        }));
                    }
                });

                this.app.getRegion('invoiceRows').show(new view());
            }
        });
    }
);

<|endoftext|>


<commit_before>/*globals d3*/

isic.views.ImagesViewSubViews = isic.views.ImagesViewSubViews || {};

isic.views.ImagesViewSubViews.ImageWall = isic.View.extend({
    initialize: function (settings) {
        this.image = settings.image;
        // For now we'll hard code this (and probably change it in the future),
        // depending on the page size
        this.imageSize = 128;

        this.listenTo(this.model.images, 'g:changed', this.render);
    },
    render: _.debounce(function () {
        var self = this;

        // Since tooltip-enabled elements are about to be destroyed, first
        // remove any active tooltips from them.
        this.clearTooltips();
        // Ordinarily, we would use the exit selection to clean up after
        // ourselves, but deleting all the img elements has the effect of
        // visually "streaming in" the new data, rather than updating the old
        // images, which feels error-prone.
        d3.select(this.el)
          .selectAll('img')
          .remove();

        d3.select(this.el)
            .selectAll('img')
            .data(this.model.images.map(function (image) {
                return {
                    id: image.id,
                    name: image.get('name')
                };
            }))
            .enter()
            .append('img')
            .classed('thumb', true)
            .attr('src', _.bind(function (d) {
                return girder.apiRoot + '/image/' + d.id + '/thumbnail?width=' + this.imageSize;
            }, this))
            .attr('height', this.imageSize * 0.75)
            .attr('width', this.imageSize)
            .attr('data-toggle', 'tooltip')
            .attr('data-placement', 'auto')
            .attr('data-viewport', '#isic-images-imageWall')
            .on('click', function (d) {
                self.clearTooltips();
                if (d3.event.shiftKey) {
                    new isic.views.ImageFullscreenWidget({ // eslint-disable-line no-new
                        el: $('#g-dialog-container'),
                        model: self.model.images.findWhere({_id: d.id}),
                        parentView: self
                    }).render();
                } else {
                    // Clear 'selected' class from all elements.
                    d3.select(self.el)
                        .selectAll('img')
                        .classed('selected', false);

                    if (d.id === self.image.id) {
                        self.image.clear();
                    } else {
                        d3.select(this)
                            .classed('selected', true);

                        self.image.clear({silent: true});
                        self.image.set('_id', d.id);
                        self.image.fetch();
                    }
                }
            })
            .each(function (d) {
                $(this).tooltip({
                    title: d.name
                });
            });
    }, 50),
    clearTooltips: function () {
        this.$('[data-toggle="tooltip"]').tooltip('hide');
        // For unknown reasons, tooltips sometimes remain after they've been
        // hidden, so manually destroy the tooltip element.
        this.$('.tooltip').remove();
    }
});
<commit_msg>Only trigger gallery tooltips on hover (not focus)<commit_after>/*globals d3*/

isic.views.ImagesViewSubViews = isic.views.ImagesViewSubViews || {};

isic.views.ImagesViewSubViews.ImageWall = isic.View.extend({
    initialize: function (settings) {
        this.image = settings.image;
        // For now we'll hard code this (and probably change it in the future),
        // depending on the page size
        this.imageSize = 128;

        this.listenTo(this.model.images, 'g:changed', this.render);
    },
    render: _.debounce(function () {
        var self = this;

        // Since tooltip-enabled elements are about to be destroyed, first
        // remove any active tooltips from them.
        this.clearTooltips();
        // Ordinarily, we would use the exit selection to clean up after
        // ourselves, but deleting all the img elements has the effect of
        // visually "streaming in" the new data, rather than updating the old
        // images, which feels error-prone.
        d3.select(this.el)
          .selectAll('img')
          .remove();

        d3.select(this.el)
            .selectAll('img')
            .data(this.model.images.map(function (image) {
                return {
                    id: image.id,
                    name: image.get('name')
                };
            }))
            .enter()
            .append('img')
            .classed('thumb', true)
            .attr('src', _.bind(function (d) {
                return girder.apiRoot + '/image/' + d.id + '/thumbnail?width=' + this.imageSize;
            }, this))
            .attr('height', this.imageSize * 0.75)
            .attr('width', this.imageSize)
            .on('click', function (d) {
                self.clearTooltips();
                if (d3.event.shiftKey) {
                    new isic.views.ImageFullscreenWidget({ // eslint-disable-line no-new
                        el: $('#g-dialog-container'),
                        model: self.model.images.findWhere({_id: d.id}),
                        parentView: self
                    }).render();
                } else {
                    // Clear 'selected' class from all elements.
                    d3.select(self.el)
                        .selectAll('img')
                        .classed('selected', false);

                    if (d.id === self.image.id) {
                        self.image.clear();
                    } else {
                        d3.select(this)
                            .classed('selected', true);

                        self.image.clear({silent: true});
                        self.image.set('_id', d.id);
                        self.image.fetch();
                    }
                }
            })
            .attr('data-toggle', 'tooltip')
            .each(function (d) {
                $(this).tooltip({
                    title: d.name,
                    placement: 'auto',
                    viewport: '#isic-images-imageWall',
                    trigger: 'hover'
                });
            });
    }, 50),
    clearTooltips: function () {
        this.$('[data-toggle="tooltip"]').tooltip('hide');
        // For unknown reasons, tooltips sometimes remain after they've been
        // hidden, so manually destroy the tooltip element.
        this.$('.tooltip').remove();
    }
});
<|endoftext|>


<commit_before>import React from 'react';
import Link from 'next/link';
import Toggle from 'react-bootstrap-toggle';
import showdown from 'showdown';
import 'isomorphic-fetch';
import ReactMarkdown from 'react-markdown';

export default ({topic, handleUpdates, user}) => (

    { topic.introduction ?

{ topic.introduction }

 : null }

      { topic.comments.filter(comm => !comm.parent).map((comment, i) => (
        <Bubble topic={topic} comment={comment} handleUpdates={handleUpdates} key={i} user={user} root={true}/>
      ))}

 What do you think?

        <BubbleMaker topic={topic} handleUpdates={handleUpdates} inFavor={true} user={user}/>

)

class BubbleMaker extends React.Component {

  constructor(props){
    super(props);
    let base = { inFavor: this.props.inFavor }
    if(props.comment){
      base.parentId = props.comment._id
    }
    this.state = base;
  }

  onToggle() {
    this.setState({ inFavor: !this.state.inFavor });
  }

  onChange(event) {
    const target = event.target;
    const name = target.name;
    const value = target.value;
    this.setState({[name]: value})
  }

  componentWillReceiveProps(props, state){
    this.setState({inFavor: props.inFavor})
  }

  onSubmit(e){
    e.preventDefault()
    fetch(`/api/topics/${this.props.topic._id}/comments`, {
      method: "POST",
      credentials: "same-origin",
      headers: {
        'Content-Type': 'application/json',
        'Accept':  'application/json'
      },
      body: JSON.stringify(this.state)
    }).then((response)=>{
      return response.json()
    }).then((json)=>{
      console.log("response", json)
      this.props.handleUpdates(json)
    })
  }

  render(){
    return(

      { this.props.user ? <form onSubmit={(e)=>this.onSubmit(e)}>

              <textarea required={true} name="content" placeholder="I think that..." className="content form-control" onChange={(e)=>{this.onChange(e)}}/>

              <Toggle
                onClick={(e)=> { this.onToggle()}}
                on={
In Favor
}
                off={
Against
}
                size="md"
                offstyle="danger"
                active={this.state.inFavor}
              />

             this.setState({showSyntax: !this.state.showSyntax})}>

              Syntax Help

            <input type="submit" className="btn-primary btn" value="Send" />

      </form> :

        <Link prefetch href="/login">Log In or Create an account</Link> to make comments

}
      { this.state.showSyntax ?

Markdown Syntax

 Currently most Markdown is supported

                You type
                You see

                *italics*
                italics

                **bold**
                bold

                [Source](https://gapclose.com)
                <Link prefetch href="/">Source</Link>

                 > Quote

Quote

 : null }

)
  }

}

class Bubble extends React.Component {

  constructor(props){
    super(props);
    this.state = { showBubbleMaker: false };
  }

  handleReply(inFavor){

    if(inFavor != this.state.inFavor){
      this.setState({ inFavor: inFavor, showBubbleMaker: true })
    } else {
      this.setState({ showBubbleMaker: !this.state.showBubbleMaker, inFavor: inFavor})
    }
  }

  deleteComment(comment){
    fetch(`/api/topics/${this.props.topic._id}/comments/${comment._id}`, {
      credentials: 'same-origin',
      method: "DELETE"
    }).then(resp => resp.json()).then(json => this.props.handleUpdates())
  }

  upvoteComment(comment, upvote=true){
    fetch(`/api/topics/${this.props.topic._id}/comments/${comment._id}/${upvote ? 'upvote' : 'downvote'}`, {
      credentials: 'same-origin',
      method: "POST",
    }).then(resp => resp.json()).then(json => this.props.handleUpdates())
  }

  render(){

    let { root, comment, topic, handleUpdates,user } = this.props;

    return(<div className={`bubble ${ root ? 'root' : null}`}>

        <div className={`${ root ? 'col-md-8 col-sm-8 col-lg-8 col-md-offset-2 col-sm-offset-2 col-lg-offset-2' : 'col-md-12 col-sm-12 col-lg-12'}`}>
          <div className={`panel ${comment.inFavor ? "panel-success" : "panel-danger"}`}>

                  { comment.inFavor ? "InFavor" : "Against"}

                  { comment.upvotes.length ?
                    { comment.upvotes.length } : null
                  }

{ comment.user.username }:

              <ReactMarkdown source={comment.content} escapeHtml={true} />

                  { user && comment.upvotes.includes(user._id) ?
                     this.upvoteComment(comment, false)}>

                      Remove Upvote

                    :  this.upvoteComment(comment)}>

                      Upvote

                  }
                  { root ? [
                     this.handleReply(false)}>Object,
                     this.handleReply(true)}>Support
                  ] : null}
                  { comment.user._id == this.props.user._id && !comment.children.length ?
                     {
                      let res = confirm("Are you sure you want to delete this comment?");
                      if(res){ this.deleteComment(comment)}
                    }}>

                      Delete

                   : null }

          { this.state.showBubbleMaker ? <BubbleMaker topic={topic}
            user={user}
            comment={comment} inFavor={this.state.inFavor}
            handleUpdates={()=> {
              let fn = (resp)=> (this.setState({ showBubbleMaker: false }))
              console.log("pre call", fn)
              handleUpdates(fn)
            }
          }/> : null}

          { comment.children.filter(a => (a.inFavor )).map((subComment, i) => (
            <Bubble key={i} {...this.props} comment={subComment} root={false} />
          ))}

          { comment.children.filter(a => !a.inFavor).map((subComment, i) => (
            <Bubble key={i} {...this.props} comment={subComment} root={false} />
          ))}

)
  }
}
<commit_msg>fix logged out bug<commit_after>import React from 'react';
import Link from 'next/link';
import Toggle from 'react-bootstrap-toggle';
import showdown from 'showdown';
import 'isomorphic-fetch';
import ReactMarkdown from 'react-markdown';

export default ({topic, handleUpdates, user}) => (

    { topic.introduction ?

{ topic.introduction }

 : null }

      { topic.comments.filter(comm => !comm.parent).map((comment, i) => (
        <Bubble topic={topic} comment={comment} handleUpdates={handleUpdates} key={i} user={user} root={true}/>
      ))}

 What do you think?

        <BubbleMaker topic={topic} handleUpdates={handleUpdates} inFavor={true} user={user}/>

)

class BubbleMaker extends React.Component {

  constructor(props){
    super(props);
    let base = { inFavor: this.props.inFavor }
    if(props.comment){
      base.parentId = props.comment._id
    }
    this.state = base;
  }

  onToggle() {
    this.setState({ inFavor: !this.state.inFavor });
  }

  onChange(event) {
    const target = event.target;
    const name = target.name;
    const value = target.value;
    this.setState({[name]: value})
  }

  componentWillReceiveProps(props, state){
    this.setState({inFavor: props.inFavor})
  }

  onSubmit(e){
    e.preventDefault()
    fetch(`/api/topics/${this.props.topic._id}/comments`, {
      method: "POST",
      credentials: "same-origin",
      headers: {
        'Content-Type': 'application/json',
        'Accept':  'application/json'
      },
      body: JSON.stringify(this.state)
    }).then((response)=>{
      return response.json()
    }).then((json)=>{
      console.log("response", json)
      this.props.handleUpdates(json)
    })
  }

  render(){
    return(

      { this.props.user ? <form onSubmit={(e)=>this.onSubmit(e)}>

              <textarea required={true} name="content" placeholder="I think that..." className="content form-control" onChange={(e)=>{this.onChange(e)}}/>

              <Toggle
                onClick={(e)=> { this.onToggle()}}
                on={
In Favor
}
                off={
Against
}
                size="md"
                offstyle="danger"
                active={this.state.inFavor}
              />

             this.setState({showSyntax: !this.state.showSyntax})}>

              Syntax Help

            <input type="submit" className="btn-primary btn" value="Send" />

      </form> :

        <Link prefetch href="/login">Log In or Create an account</Link> to make comments

}
      { this.state.showSyntax ?

Markdown Syntax

 Currently most Markdown is supported

                You type
                You see

                *italics*
                italics

                **bold**
                bold

                [Source](https://gapclose.com)
                <Link prefetch href="/">Source</Link>

                 > Quote

Quote

 : null }

)
  }

}

class Bubble extends React.Component {

  constructor(props){
    super(props);
    this.state = { showBubbleMaker: false };
  }

  handleReply(inFavor){

    if(inFavor != this.state.inFavor){
      this.setState({ inFavor: inFavor, showBubbleMaker: true })
    } else {
      this.setState({ showBubbleMaker: !this.state.showBubbleMaker, inFavor: inFavor})
    }
  }

  deleteComment(comment){
    fetch(`/api/topics/${this.props.topic._id}/comments/${comment._id}`, {
      credentials: 'same-origin',
      method: "DELETE"
    }).then(resp => resp.json()).then(json => this.props.handleUpdates())
  }

  upvoteComment(comment, upvote=true){
    fetch(`/api/topics/${this.props.topic._id}/comments/${comment._id}/${upvote ? 'upvote' : 'downvote'}`, {
      credentials: 'same-origin',
      method: "POST",
    }).then(resp => resp.json()).then(json => this.props.handleUpdates())
  }

  render(){

    let { root, comment, topic, handleUpdates,user } = this.props;

    return(<div className={`bubble ${ root ? 'root' : null}`}>

        <div className={`${ root ? 'col-md-8 col-sm-8 col-lg-8 col-md-offset-2 col-sm-offset-2 col-lg-offset-2' : 'col-md-12 col-sm-12 col-lg-12'}`}>
          <div className={`panel ${comment.inFavor ? "panel-success" : "panel-danger"}`}>

                  { comment.inFavor ? "InFavor" : "Against"}

                  { comment.upvotes.length ?
                    { comment.upvotes.length } : null
                  }

{ comment.user.username }:

              <ReactMarkdown source={comment.content} escapeHtml={true} />

                  { user && comment.upvotes.includes(user._id) ?
                     this.upvoteComment(comment, false)}>

                      Remove Upvote

                    :  this.upvoteComment(comment)}>

                      Upvote

                  }
                  { root ? [
                     this.handleReply(false)}>Object,
                     this.handleReply(true)}>Support
                  ] : null}
                  { user && comment.user._id == user._id && !comment.children.length ?
                     {
                      let res = confirm("Are you sure you want to delete this comment?");
                      if(res){ this.deleteComment(comment)}
                    }}>

                      Delete

                   : null }

          { this.state.showBubbleMaker ? <BubbleMaker topic={topic}
            user={user}
            comment={comment} inFavor={this.state.inFavor}
            handleUpdates={()=> {
              let fn = (resp)=> (this.setState({ showBubbleMaker: false }))
              console.log("pre call", fn)
              handleUpdates(fn)
            }
          }/> : null}

          { comment.children.filter(a => (a.inFavor )).map((subComment, i) => (
            <Bubble key={i} {...this.props} comment={subComment} root={false} />
          ))}

          { comment.children.filter(a => !a.inFavor).map((subComment, i) => (
            <Bubble key={i} {...this.props} comment={subComment} root={false} />
          ))}

)
  }
}
<|endoftext|>


<commit_before>import {
  WINDOWS_CREATE, WINDOWS_REMOVE, WINDOWS_GET,
  TABS_CREATE, TABS_REMOVE, TABS_SELECT_AT, TABS_GET_ZOOM, TABS_SET_ZOOM,
  EVENT_KEYPRESS, EVENT_KEYDOWN, EVENT_KEYUP,
  SCROLL_GET, SCROLL_SET,
} from '../shared/messages';
import * as tabs from './tabs';
import { receiveContentMessage } from './ipc';

receiveContentMessage((message) => {
  switch (message.type) {
  case WINDOWS_CREATE:
    return browser.windows.create({ url: message.url });
  case WINDOWS_REMOVE:
    return browser.windows.remove(message.windowId);
  case WINDOWS_GET:
    return browser.windows.get(message.windowId, { populate: true });
  case TABS_CREATE:
    return tabs.create({
      url: message.url,
      windowId: message.windowId,
    });
  case TABS_REMOVE:
    return browser.tabs.remove(message.tabId);
  case TABS_SELECT_AT:
    return tabs.selectAt({
      windowId: message.windowId,
      index: message.index,
    });
  case TABS_GET_ZOOM:
    return browser.tabs.getZoom(message.tabId);
  case TABS_SET_ZOOM:
    return browser.tabs.setZoom(message.tabId, message.factor);
  case EVENT_KEYPRESS:
  case EVENT_KEYDOWN:
  case EVENT_KEYUP:
  case SCROLL_GET:
  case SCROLL_SET:
    return browser.tabs.sendMessage(
      message.tabId,
      message
    );
  }
});
<commit_msg>fix e2e test<commit_after>import {
  WINDOWS_CREATE, WINDOWS_REMOVE, WINDOWS_GET,
  TABS_CREATE, TABS_SELECT_AT, TABS_GET_ZOOM, TABS_SET_ZOOM,
  EVENT_KEYPRESS, EVENT_KEYDOWN, EVENT_KEYUP,
  SCROLL_GET, SCROLL_SET,
} from '../shared/messages';
import * as tabs from './tabs';
import { receiveContentMessage } from './ipc';

receiveContentMessage((message) => {
  switch (message.type) {
  case WINDOWS_CREATE:
    return browser.windows.create({ url: message.url });
  case WINDOWS_REMOVE:
    return browser.windows.remove(message.windowId);
  case WINDOWS_GET:
    return browser.windows.get(message.windowId, { populate: true });
  case TABS_CREATE:
    return tabs.create({
      url: message.url,
      windowId: message.windowId,
    });
  case TABS_SELECT_AT:
    return tabs.selectAt({
      windowId: message.windowId,
      index: message.index,
    });
  case TABS_GET_ZOOM:
    return browser.tabs.getZoom(message.tabId);
  case TABS_SET_ZOOM:
    return browser.tabs.setZoom(message.tabId, message.factor);
  case EVENT_KEYPRESS:
  case EVENT_KEYDOWN:
  case EVENT_KEYUP:
  case SCROLL_GET:
  case SCROLL_SET:
    return browser.tabs.sendMessage(
      message.tabId,
      message
    );
  }
});
<|endoftext|>


<commit_before>var express = require('express');
var router = express.Router();
var mongodb = require('../mongo_connection');
var studentsCollection = mongodb.db.collection('students');
var bodyParser = require('body-parser');
var wrapResult = require('./wrap-result').wrapResult;

/*
 * /students GET
 *
 * Returns all students in query array or all students
 * if there's no query array.
 */
router.get('/', function(req, res) {
    var filter = null;
    if (req.query.ids != null) {
	filter = { _id: { $in: req.query.ids } };
    }
    //var order = { last_name: 1, first_name: 1 };

    studentsCollection.find(filter)/*.sort(order)*/.toArray()
	.then(function(students) {
	    res.json(wrapResult(students));
	})
	.catch(function(err) {
	    res.status(500);
	    res.json(err);
	});
});

/*
 * /students/:id GET
 *
 * Returns the student identified by 'id'.
 */
router.get('/:id', function(req, res) {
    studentsCollection.findOne({'_id': req.params.id})
	.then(function(student) {
	    res.json(wrapResult(student));
	})
	.catch(function(err) {
	    res.status(500);
	    res.json(err);
	});
});

/*
 * /students POST
 *
 * Inserts a new student.
 */
router.post('/', bodyParser.json(), function(req, res) {
    studentsCollection.insertOne(req.body, null)
	.then(function(result) {
	    console.info('studentPOST: ' + result);
	    res.json(wrapResult(result));
	})
	.catch(function(err) {
	    console.error('students POST: ' + err);
	    res.status(500);
	    res.json(err);
	});
});

/*
 * /students/many POST
 *
 * Inserts an array of new students.
 */
router.post('/many', bodyParser.json(), function(req, res) {
    studentsCollection.insertMany(req.body, null)
	.then(function(result) {
	    console.info('students/many POST: ' + result);
	    res.json(wrapResult(result));
	})
	.catch(function(err) {
	    console.error('students POST: ' + err);
	    res.status(500);
	    res.json(err);
	});
});

/*
 * /students/bygroup/:id/:year GET
 *
 * Returns the ACTIVE students corresponding to group with 'id'
 * for the group's 'year'.
 * Group data are also included.
 */
router.get('/bygroup/:id/:year', function(req, res) {
    var pipe = [
	{$match:{enrolments:{$elemMatch: {year:req.params.year,group_id:req.params.id,active: true}}}},{$unwind:'$enrolments'},{$project:{first_name: 1,last_name: 1,birth_date: 1,gender: 1,repeats:'$enrolments.repeats',ed_measures:'$enrolments.ed_measures',year:'$enrolments.year',group_id:'$enrolments.group_id',course_id:'$enrolments.course_id',student_subjects:'$enrolments.subjects',course_subjects:'$enrolments.subjects'}},{$lookup:{from:'groups',localField:'group_id',foreignField:'_id',as:'group'}},{$unwind:'$group'},{$unwind:'$course_subjects'},{$sort:{last_name:1,first_name:1}},{$group:{_id:{_id:'$group_id',name:'$group.name',short_name:'$group.short_name',year:'$year',course_id:'$course_id'},subjects:{$addToSet:{_id:'$course_subjects.subject_id',adapted:'$course_subjects.adapted'}},students:{$addToSet:{_id:'$_id',first_name:'$first_name',last_name:'$last_name',birthdate:'$birth_date',gender:'$gender',repeats:'$repeats',ed_measures:'$ed_measures',subjects:'$student_subjects'}}}},{$project:{_id:'$_id._id',name:'$_id.name',short_name:'$_id.short_name',year:'$_id.year',course_id:'$_id.course_id',subjects:1,students:1}}
    ];

    studentsCollection.aggregate(pipe, function(err, result) {
        if (err != null) {
            res.status(500);
            res.json(err);
        }
        else {
            result = result.length > 0 ? result[0] : result;
            res.json(wrapResult(result));
        }
    });
});

module.exports = router;
<commit_msg>Students by group query WIP.<commit_after>var express = require('express');
var router = express.Router();
var mongodb = require('../mongo_connection');
var studentsCollection = mongodb.db.collection('students');
var bodyParser = require('body-parser');
var wrapResult = require('./wrap-result').wrapResult;

/*
 * /students GET
 *
 * Returns all students in query array or all students
 * if there's no query array.
 */
router.get('/', function(req, res) {
    var filter = null;
    if (req.query.ids != null) {
	filter = { _id: { $in: req.query.ids } };
    }
    //var order = { last_name: 1, first_name: 1 };

    studentsCollection.find(filter)/*.sort(order)*/.toArray()
	.then(function(students) {
	    res.json(wrapResult(students));
	})
	.catch(function(err) {
	    res.status(500);
	    res.json(err);
	});
});

/*
 * /students/:id GET
 *
 * Returns the student identified by 'id'.
 */
router.get('/:id', function(req, res) {
    studentsCollection.findOne({'_id': req.params.id})
	.then(function(student) {
	    res.json(wrapResult(student));
	})
	.catch(function(err) {
	    res.status(500);
	    res.json(err);
	});
});

/*
 * /students POST
 *
 * Inserts a new student.
 */
router.post('/', bodyParser.json(), function(req, res) {
    studentsCollection.insertOne(req.body, null)
	.then(function(result) {
	    console.info('studentPOST: ' + result);
	    res.json(wrapResult(result));
	})
	.catch(function(err) {
	    console.error('students POST: ' + err);
	    res.status(500);
	    res.json(err);
	});
});

/*
 * /students/many POST
 *
 * Inserts an array of new students.
 */
router.post('/many', bodyParser.json(), function(req, res) {
    studentsCollection.insertMany(req.body, null)
	.then(function(result) {
	    console.info('students/many POST: ' + result);
	    res.json(wrapResult(result));
	})
	.catch(function(err) {
	    console.error('students POST: ' + err);
	    res.status(500);
	    res.json(err);
	});
});

/*
 * /students/bygroup/:id/:year GET
 *
 * Returns the ACTIVE students corresponding to group with 'id'
 * for the group's 'year'.
 * Group data are also included.
 */
router.get('/bygroup/:id/:year', function(req, res) {
    var pipe = [
	{$match:{enrolments:{$elemMatch: {year:req.params.year,group_id:req.params.id,active: true}}}},{$unwind:'$enrolments'},{$project:{first_name: 1,last_name: 1,birth_date: 1,gender: 1,repeats:'$enrolments.repeats',ed_measures:'$enrolments.ed_measures',year:'$enrolments.year',group_id:'$enrolments.group_id',course_id:'$enrolments.course_id',course_subjects:'$enrolments.subjects',student_subjects:'$enrolments.subjects'}},{$unwind:'$course_subjects'},{$lookup:{from:'subjects',localField:'course_subjects.subject_id',foreignField:'_id',as:'subjects'}},{$unwind:'$subjects'},{$group:{_id:{_id:'$group_id',course_id:'$course_id',year:'$year'},subjects:{$addToSet:{_id:'$subjects._id',name:'$subjects.name'}},students:{$addToSet:{_id:'$_id',first_name:'$first_name',last_name:'$last_name',birthdate:'$birth_date',gender:'$gender',repeats:'$repeats',ed_measures:'$ed_measures',subjects:'$student_subjects'}}}},{$project:{_id:'$_id._id',course_id:'$_id.course_id',year:'$_id.year',subjects:1,students:1}},{$unwind:'$subjects'},{$sort:{'subjects._id':1}},{$group:{_id:{_id:'$_id',course_id:'$course_id',year:'$year',students:'$students'},subjects:{$push:{_id:'$subjects._id',name:'$subjects.name'}}}},{$project:{_id:'$_id._id',course_id:'$_id.course_id',year:'$_id.year',students:'$_id.students',subjects:1}},{$unwind:'$students'},{$sort:{'students.last_name':1,'students.first_name':1}},{$group:{_id:{_id:'$_id',course_id:'$course_id',year:'$year',subjects:'$subjects'},students:{$push:{_id:'$students._id',first_name:'$students.first_name',last_name:'$students.last_name',birthdate:'$students.birthdate',gender:'$students.gender',repeats:'$students.repeat',ed_measures:'$students.ed_measures',subjects:'$students.subjects'}}}},{$project:{_id:'$_id._id',course_id:'$_id.course_id',year:'$_id.year',subjects:'$_id.subjects',students:1}},{$lookup:{from:'groups',localField:'_id',foreignField:'_id',as:'group'}},{$unwind:'$group'},{$project:{name:'$group.name',short_name:'$group.short_name',year:1,course_id:1,subjects:1,students:1}}
    ];

    studentsCollection.aggregate(pipe, function(err, result) {
        if (err != null) {
            res.status(500);
            res.json(err);
        }
        else {
            result = result.length > 0 ? result[0] : result;
            res.json(wrapResult(result));
        }
    });
});

module.exports = router;
<|endoftext|>


<commit_before>/* eslint-disable no-console */
const Board = require('../lib/Board');
const ai1 = require('./old_ai');
const ai2 = require('./ai');

const board = new Board();

board.on('win', (winner) => {
	console.log(`AI${winner + 1} won`);
});

ai1.time = 0;
ai2.time = 0;
ai1.n = 0;
ai2.n = 0;
ai1.ainame = 'AI1';
ai2.ainame = 'AI2';

const reverse = false;

const ais = reverse ? [ai1, ai2] : [ai2, ai1];

for (;;) {
	const point = board.getCurrentPoint();
	if (!point || point.movableDirections.size === 0) {
		break;
	}
	const ai = ais[board.activePlayer];
	let hist = board.trace.reduce((prev, curr) => prev.concat(curr), []);
	console.log(JSON.stringify(hist));
	if (board.activePlayer === 0) {
		hist = hist.map(([x, y]) => [8 - x, 8 - y]);
	}
	const start = new Date();
	let aiDir = ai(hist);
	ai.time += new Date().valueOf() - start.valueOf();
	ai.n++;
	if (aiDir === -1) {
		throw new Error('Ω＼ζ°)ﾁｰﾝ');
	}
	aiDir = (aiDir + (1 - board.activePlayer) * 4) % 8;
	console.log(`${ai.ainame} moves in ${aiDir}`);
	board.moveTo(aiDir);
}

console.log('ended: hist=', JSON.stringify(board.trace));
console.log(`ai1: avgtime=${ai1.time / ai1.n}; ai2: avgtime=${ai2.time / ai2.n}`);
<commit_msg>Show which AI's turn<commit_after>/* eslint-disable no-console */
const Board = require('../lib/Board');
const ai1 = require('./old_ai');
const ai2 = require('./ai');

const board = new Board();

board.on('win', (winner) => {
	console.log(`AI${winner + 1} won`);
});

ai1.time = 0;
ai2.time = 0;
ai1.n = 0;
ai2.n = 0;
ai1.ainame = 'AI1';
ai2.ainame = 'AI2';

const reverse = false;

const ais = reverse ? [ai1, ai2] : [ai2, ai1];

for (;;) {
	const point = board.getCurrentPoint();
	if (!point || point.movableDirections.size === 0) {
		break;
	}
	const ai = ais[board.activePlayer];
	let hist = board.trace.reduce((prev, curr) => prev.concat(curr), []);
	console.log(`${ai.ainame}'s turn: ${JSON.stringify(hist)}`);
	if (board.activePlayer === 0) {
		hist = hist.map(([x, y]) => [8 - x, 8 - y]);
	}
	const start = new Date();
	let aiDir = ai(hist);
	ai.time += new Date().valueOf() - start.valueOf();
	ai.n++;
	if (aiDir === -1) {
		throw new Error('Ω＼ζ°)ﾁｰﾝ');
	}
	aiDir = (aiDir + (1 - board.activePlayer) * 4) % 8;
	console.log(`${ai.ainame} moves in ${aiDir}`);
	board.moveTo(aiDir);
}

console.log('ended: hist=', JSON.stringify(board.trace));
console.log(`ai1: avgtime=${ai1.time / ai1.n}; ai2: avgtime=${ai2.time / ai2.n}`);
<|endoftext|>


<commit_before>/**
 * @flow
 * All About Olaf
 * Contact page
 */

import React from 'react'
import {SectionList, StyleSheet} from 'react-native'
import {ListSeparator, ListSectionHeader} from '../components/list'
import {ListEmpty} from '../components/list'
import {ContactRow} from './contact-row'
import delay from 'delay'
import {reportNetworkProblem} from '../../lib/report-network-problem'
import * as defaultData from '../../../docs/contact-info.json'
import groupBy from 'lodash/groupBy'
import toPairs from 'lodash/toPairs'
import * as c from '../components/colors'
import type {ContactType} from './types'
import type {TopLevelViewPropsType} from '../types'

const GITHUB_URL =
  'https://stodevx.github.io/AAO-React-Native/contact-info.json'

const groupContacts = (contacts: ContactType[]) => {
  const grouped = groupBy(contacts, c => c.category)
  return toPairs(grouped).map(([key, value]) => ({title: key, data: value}))
}

const styles = StyleSheet.create({
  listContainer: {
    backgroundColor: c.white,
  },
})

type Props = TopLevelViewPropsType

type State = {
  contacts: Array<ContactType>,
  loading: boolean,
  refreshing: boolean,
}

export class ContactsListView extends React.PureComponent<void, Props, State> {
  static navigationOptions = {
    title: 'Important Contacts',
    headerBackTitle: 'Contacts',
  }

  state = {
    contacts: defaultData.data,
    loading: true,
    refreshing: false,
  }

  componentWillMount() {
    this.fetchData()
  }

  refresh = async () => {
    const start = Date.now()
    this.setState(() => ({refreshing: true}))

    await this.fetchData()

    // wait 0.5 seconds – if we let it go at normal speed, it feels broken.
    const elapsed = Date.now() - start
    if (elapsed < 500) {
      await delay(500 - elapsed)
    }

    this.setState(() => ({refreshing: false}))
  }

  fetchData = async () => {
    this.setState(() => ({loading: true}))

    let {data: contacts} = await fetchJson(GITHUB_URL).catch(err => {
      reportNetworkProblem(err)
      return defaultData
    })

    if (process.env.NODE_ENV === 'development') {
      contacts = defaultData.data
    }

    this.setState(() => ({contacts, loading: false}))
  }

  onPressContact = (contact: ContactType) => {
    this.props.navigation.navigate('ContactsDetailView', {
      contact,
    })
  }

  renderSectionHeader = ({section: {title}}: any) => (
    <ListSectionHeader title={title} />
  )

  renderItem = ({item}: {item: ContactType}) => (
    <ContactRow contact={item} onPress={this.onPressContact} />
  )

  keyExtractor = (item: ContactType) => item.title

  render() {
    const groupedData = groupContacts(this.state.contacts)
    return (
      <SectionList
        ItemSeparatorComponent={ListSeparator}
        ListEmptyComponent={<ListEmpty mode="bug" />}
        style={styles.listContainer}
        data={groupedData}
        sections={groupedData}
        keyExtractor={this.keyExtractor}
        renderSectionHeader={this.renderSectionHeader}
        renderItem={this.renderItem}
        refreshing={this.state.refreshing}
        onRefresh={this.refresh}
      />
    )
  }
}
<commit_msg>remove knowledge of loading from ContactList<commit_after>/**
 * @flow
 * All About Olaf
 * Contact page
 */

import React from 'react'
import {SectionList, StyleSheet} from 'react-native'
import {ListSeparator, ListSectionHeader} from '../components/list'
import {ListEmpty} from '../components/list'
import {ContactRow} from './contact-row'
import delay from 'delay'
import {reportNetworkProblem} from '../../lib/report-network-problem'
import * as defaultData from '../../../docs/contact-info.json'
import groupBy from 'lodash/groupBy'
import toPairs from 'lodash/toPairs'
import * as c from '../components/colors'
import type {ContactType} from './types'
import type {TopLevelViewPropsType} from '../types'

const GITHUB_URL =
  'https://stodevx.github.io/AAO-React-Native/contact-info.json'

const groupContacts = (contacts: ContactType[]) => {
  const grouped = groupBy(contacts, c => c.category)
  return toPairs(grouped).map(([key, value]) => ({title: key, data: value}))
}

const styles = StyleSheet.create({
  listContainer: {
    backgroundColor: c.white,
  },
})

type Props = TopLevelViewPropsType

type State = {
  contacts: Array<ContactType>,
  loading: boolean,
  refreshing: boolean,
}

export class ContactsListView extends React.PureComponent<void, Props, State> {
  static navigationOptions = {
    title: 'Important Contacts',
    headerBackTitle: 'Contacts',
  }

  state = {
    contacts: defaultData.data,
    loading: true,
    refreshing: false,
  }

  componentWillMount() {
    this.fetchData().then(() => {
      this.setState(() => ({loading: false}))
    })
  }

  refresh = async () => {
    const start = Date.now()
    this.setState(() => ({refreshing: true}))

    await this.fetchData()

    // wait 0.5 seconds – if we let it go at normal speed, it feels broken.
    const elapsed = Date.now() - start
    if (elapsed < 500) {
      await delay(500 - elapsed)
    }

    this.setState(() => ({refreshing: false}))
  }

  fetchData = async () => {
    let {data: contacts} = await fetchJson(GITHUB_URL).catch(err => {
      reportNetworkProblem(err)
      return defaultData
    })

    if (process.env.NODE_ENV === 'development') {
      contacts = defaultData.data
    }

    this.setState(() => ({contacts}))
  }

  onPressContact = (contact: ContactType) => {
    this.props.navigation.navigate('ContactsDetailView', {
      contact,
    })
  }

  renderSectionHeader = ({section: {title}}: any) => (
    <ListSectionHeader title={title} />
  )

  renderItem = ({item}: {item: ContactType}) => (
    <ContactRow contact={item} onPress={this.onPressContact} />
  )

  keyExtractor = (item: ContactType) => item.title

  render() {
    const groupedData = groupContacts(this.state.contacts)
    return (
      <SectionList
        ItemSeparatorComponent={ListSeparator}
        ListEmptyComponent={<ListEmpty mode="bug" />}
        style={styles.listContainer}
        data={groupedData}
        sections={groupedData}
        keyExtractor={this.keyExtractor}
        renderSectionHeader={this.renderSectionHeader}
        renderItem={this.renderItem}
        refreshing={this.state.refreshing}
        onRefresh={this.refresh}
      />
    )
  }
}
<|endoftext|>