<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]')) {
              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) {
        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')) {
          //miss top
          if (t.offset().top <= this.outerHeight()) {
            p.missTop = true;
            actualBoundary = window.innerWidth - actualMarginWidth;
            p.leftRightFlag = true;
          }

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

          //miss left
          if (t.offset().left - this.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);

        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);

        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);

        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);

        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>Prevent dropdown from always calling e.stopPropagation() on click, fixes #6473<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) {
        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')) {
          //miss top
          if (t.offset().top <= this.outerHeight()) {
            p.missTop = true;
            actualBoundary = window.innerWidth - actualMarginWidth;
            p.leftRightFlag = true;
          }

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

          //miss left
          if (t.offset().left - this.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);

        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);

        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);

        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);

        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>/**
 * Copyright 2014, 2015 IBM Corp.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/

var ws = require("ws");
var log = require("./log");

var server;
var settings;

var wsServer;
var pendingConnections = [];
var activeConnections = [];

var retained = {};

var heartbeatTimer;
var lastSentTime;

function init(_server,_settings) {
    server = _server;
    settings = _settings;
}

function start() {
    var Tokens = require("./api/auth/tokens");
    var Users = require("./api/auth/users");
    var Permissions = require("./api/auth/permissions");
    if (!settings.disableEditor) {
        Users.default().then(function(anonymousUser) {
            var webSocketKeepAliveTime = settings.webSocketKeepAliveTime || 15000;
            var path = settings.httpAdminRoot || "/";
            path = (path.slice(0,1) != "/" ? "/":"") + path + (path.slice(-1) == "/" ? "":"/") + "comms";
            wsServer = new ws.Server({server:server,path:path});

            wsServer.on('connection',function(ws) {
                log.audit({event: "comms.open"});
                var pendingAuth = (settings.adminAuth != null);
                if (!pendingAuth) {
                    activeConnections.push(ws);
                } else {
                    pendingConnections.push(ws);
                }
                ws.on('close',function() {
                    log.audit({event: "comms.close",user:ws.user});
                    removeActiveConnection(ws);
                    removePendingConnection(ws);
                });
                ws.on('message', function(data,flags) {
                    var msg = null;
                    try {
                        msg = JSON.parse(data);
                    } catch(err) {
                        log.trace("comms received malformed message : "+err.toString());
                        return;
                    }
                    if (!pendingAuth) {
                        if (msg.subscribe) {
                            handleRemoteSubscription(ws,msg.subscribe);
                        }
                    } else {
                        var completeConnection = function(userScope,sendAck) {
                            if (!userScope || !Permissions.hasPermission(userScope,"status.read")) {
                                ws.close();
                            } else {
                                pendingAuth = false;
                                removePendingConnection(ws);
                                activeConnections.push(ws);
                                if (sendAck) {
                                    ws.send(JSON.stringify({auth:"ok"}));
                                }
                            }
                        }
                        if (msg.auth) {
                            Tokens.get(msg.auth).then(function(client) {
                                if (client) {
                                    Users.get(client.user).then(function(user) {
                                        if (user) {
                                            ws.user = user;
                                            log.audit({event: "comms.auth",user:ws.user});
                                            completeConnection(client.scope,true);
                                        } else {
                                            log.audit({event: "comms.auth.fail"});
                                            completeConnection(null,false);
                                        }
                                    });
                                } else {
                                    log.audit({event: "comms.auth.fail"});
                                    completeConnection(null,false);
                                }
                            });
                        } else {
                            if (anonymousUser) {
                                log.audit({event: "comms.auth",user:anonymousUser});
                                completeConnection(anonymousUser.permissions,false);
                            } else {
                                log.audit({event: "comms.auth.fail"});
                                completeConnection(null,false);
                            }
                            //TODO: duplicated code - pull non-auth message handling out
                            if (msg.subscribe) {
                                handleRemoteSubscription(ws,msg.subscribe);
                            }
                        }
                    }
                });
                ws.on('error', function(err) {
                    log.warn(log._("comms.error",{message:err.toString()}));
                });
            });

            wsServer.on('error', function(err) {
                log.warn(log._("comms.error-server",{message:err.toString()}));
            });

            lastSentTime = Date.now();

            heartbeatTimer = setInterval(function() {
                var now = Date.now();
                if (now-lastSentTime > webSocketKeepAliveTime) {
                    publish("hb",lastSentTime);
                }
            }, webSocketKeepAliveTime);
        });
    }
}

function stop() {
    if (heartbeatTimer) {
        clearInterval(heartbeatTimer);
        heartbeatTimer = null;
    }
    if (wsServer) {
        wsServer.close();
        wsServer = null;
    }
}

function publish(topic,data,retain) {
    if (retain) {
        retained[topic] = data;
    } else {
        delete retained[topic];
    }
    lastSentTime = Date.now();
    activeConnections.forEach(function(conn) {
        publishTo(conn,topic,data);
    });
}

function publishTo(ws,topic,data) {
    var msg = JSON.stringify({topic:topic,data:data});
    try {
        ws.send(msg);
    } catch(err) {
        removeActiveConnection(ws);
        removePendingConnection(ws);
        log.warn(log._("comms.error-send",{message:err.toString()}));
    }
}

function handleRemoteSubscription(ws,topic) {
    var re = new RegExp("^"+topic.replace(/([\[\]\?\(\)\\\\$\^\*\.|])/g,"\\$1").replace(/\+/g,"[^/]+").replace(/\/#$/,"(\/.*)?")+"$");
    for (var t in retained) {
        if (re.test(t)) {
            publishTo(ws,t,retained[t]);
        }
    }
}

function removeActiveConnection(ws) {
    for (var i=0;i<activeConnections.length;i++) {
        if (activeConnections[i] === ws) {
            activeConnections.splice(i,1);
            break;
        }
    }
}
function removePendingConnection(ws) {
    for (var i=0;i<pendingConnections.length;i++) {
        if (pendingConnections[i] === ws) {
            pendingConnections.splice(i,1);
            break;
        }
    }
}

module.exports = {
    init:init,
    start:start,
    stop:stop,
    publish:publish,
}
<commit_msg>remove annoying comma from comm.js<commit_after>/**
 * Copyright 2014, 2015 IBM Corp.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/

var ws = require("ws");
var log = require("./log");

var server;
var settings;

var wsServer;
var pendingConnections = [];
var activeConnections = [];

var retained = {};

var heartbeatTimer;
var lastSentTime;

function init(_server,_settings) {
    server = _server;
    settings = _settings;
}

function start() {
    var Tokens = require("./api/auth/tokens");
    var Users = require("./api/auth/users");
    var Permissions = require("./api/auth/permissions");
    if (!settings.disableEditor) {
        Users.default().then(function(anonymousUser) {
            var webSocketKeepAliveTime = settings.webSocketKeepAliveTime || 15000;
            var path = settings.httpAdminRoot || "/";
            path = (path.slice(0,1) != "/" ? "/":"") + path + (path.slice(-1) == "/" ? "":"/") + "comms";
            wsServer = new ws.Server({server:server,path:path});

            wsServer.on('connection',function(ws) {
                log.audit({event: "comms.open"});
                var pendingAuth = (settings.adminAuth != null);
                if (!pendingAuth) {
                    activeConnections.push(ws);
                } else {
                    pendingConnections.push(ws);
                }
                ws.on('close',function() {
                    log.audit({event: "comms.close",user:ws.user});
                    removeActiveConnection(ws);
                    removePendingConnection(ws);
                });
                ws.on('message', function(data,flags) {
                    var msg = null;
                    try {
                        msg = JSON.parse(data);
                    } catch(err) {
                        log.trace("comms received malformed message : "+err.toString());
                        return;
                    }
                    if (!pendingAuth) {
                        if (msg.subscribe) {
                            handleRemoteSubscription(ws,msg.subscribe);
                        }
                    } else {
                        var completeConnection = function(userScope,sendAck) {
                            if (!userScope || !Permissions.hasPermission(userScope,"status.read")) {
                                ws.close();
                            } else {
                                pendingAuth = false;
                                removePendingConnection(ws);
                                activeConnections.push(ws);
                                if (sendAck) {
                                    ws.send(JSON.stringify({auth:"ok"}));
                                }
                            }
                        }
                        if (msg.auth) {
                            Tokens.get(msg.auth).then(function(client) {
                                if (client) {
                                    Users.get(client.user).then(function(user) {
                                        if (user) {
                                            ws.user = user;
                                            log.audit({event: "comms.auth",user:ws.user});
                                            completeConnection(client.scope,true);
                                        } else {
                                            log.audit({event: "comms.auth.fail"});
                                            completeConnection(null,false);
                                        }
                                    });
                                } else {
                                    log.audit({event: "comms.auth.fail"});
                                    completeConnection(null,false);
                                }
                            });
                        } else {
                            if (anonymousUser) {
                                log.audit({event: "comms.auth",user:anonymousUser});
                                completeConnection(anonymousUser.permissions,false);
                            } else {
                                log.audit({event: "comms.auth.fail"});
                                completeConnection(null,false);
                            }
                            //TODO: duplicated code - pull non-auth message handling out
                            if (msg.subscribe) {
                                handleRemoteSubscription(ws,msg.subscribe);
                            }
                        }
                    }
                });
                ws.on('error', function(err) {
                    log.warn(log._("comms.error",{message:err.toString()}));
                });
            });

            wsServer.on('error', function(err) {
                log.warn(log._("comms.error-server",{message:err.toString()}));
            });

            lastSentTime = Date.now();

            heartbeatTimer = setInterval(function() {
                var now = Date.now();
                if (now-lastSentTime > webSocketKeepAliveTime) {
                    publish("hb",lastSentTime);
                }
            }, webSocketKeepAliveTime);
        });
    }
}

function stop() {
    if (heartbeatTimer) {
        clearInterval(heartbeatTimer);
        heartbeatTimer = null;
    }
    if (wsServer) {
        wsServer.close();
        wsServer = null;
    }
}

function publish(topic,data,retain) {
    if (retain) {
        retained[topic] = data;
    } else {
        delete retained[topic];
    }
    lastSentTime = Date.now();
    activeConnections.forEach(function(conn) {
        publishTo(conn,topic,data);
    });
}

function publishTo(ws,topic,data) {
    var msg = JSON.stringify({topic:topic,data:data});
    try {
        ws.send(msg);
    } catch(err) {
        removeActiveConnection(ws);
        removePendingConnection(ws);
        log.warn(log._("comms.error-send",{message:err.toString()}));
    }
}

function handleRemoteSubscription(ws,topic) {
    var re = new RegExp("^"+topic.replace(/([\[\]\?\(\)\\\\$\^\*\.|])/g,"\\$1").replace(/\+/g,"[^/]+").replace(/\/#$/,"(\/.*)?")+"$");
    for (var t in retained) {
        if (re.test(t)) {
            publishTo(ws,t,retained[t]);
        }
    }
}

function removeActiveConnection(ws) {
    for (var i=0;i<activeConnections.length;i++) {
        if (activeConnections[i] === ws) {
            activeConnections.splice(i,1);
            break;
        }
    }
}
function removePendingConnection(ws) {
    for (var i=0;i<pendingConnections.length;i++) {
        if (pendingConnections[i] === ws) {
            pendingConnections.splice(i,1);
            break;
        }
    }
}

module.exports = {
    init:init,
    start:start,
    stop:stop,
    publish:publish
}
<|endoftext|>


<commit_before>/** @scratch /panels/5
 *
 * include::panels/pcap.asciidoc[]
 */

/** @scratch /panels/pcap/0
 * == pcap
 * Status: *Stable*
 *
 * The pcap panel is used for displaying static pcap formated as markdown, sanitized html or as plain
 * pcap.
 *
 */

define([
  'angular',
  'app',
  'lodash',
  'require',
  'jquery',
  'kbn'
],
function (angular, app, _, require, kbn) {
  'use strict';

  var module = angular.module('kibana.panels.pcap', []);
  app.useModule(module);

  module.controller('pcap', function($filter, $scope, $http) {
    $scope.user = window.user;

$scope.panelMeta = {
      modals : [
        {
          description: "Inspect",
          icon: "icon-info-sign",
          partial: "app/partials/inspector.html",
          show: $scope.panel.spyable
        }
      ],
      status  : 'Stable',
      description : 'OpenSoc PCAP viewer'
    };

    $scope.durations = [
      {desc: '1d', sec: 24 * 60 * 60},
      {desc: '6h', sec: 6 * 60 * 60},
      {desc: '1h', sec: 60 * 60},
      {desc: '30m', sec: 30 * 60},
      {desc: '15m', sec: 15 * 60},
      {desc: '5m', sec: 5 * 60},
      {desc: '1m', sec: 60},
      {desc: '30s', sec: 10},
      {desc: '10s', sec: 10},
      {desc: '5s', sec: 5},
      {desc: '1s', sec: 1}
    ];

    $scope.streamPage = 0;
    $scope.duration = $scope.durations[$scope.durations.length - 1];

    function serialize(obj) {
      var str = [];
      for(var p in obj)
        if (obj.hasOwnProperty(p)) {
          str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
        }
      return str.join("&");
    }

    function makeTimeFilter(filter) {
      if($scope.ts) {
        var ts = Date.parse($scope.ts);
        ts = ts > 0 ? ts : parseInt($scope.ts);
        if (ts <= 999999999999) {
          ts *= 1000;
        }

        filter = (filter || ejs.BoolFilter())
          .must(ejs.RangeFilter('ts_micro')
            .gte((ts - $scope.duration.sec * 1000) * 1000)
            .lte((ts + $scope.duration.sec * 1000) * 1000))
      }
      return filter;
    }

    function makeFilter() {
      var fields = [
        'ip_src_addr', 'ip_dst_addr',
        'ip_src_port', 'ip_dst_port',
        'ip_protocol'
      ];

      var filter;
      $.each(fields, function(i, f) {
        if ($scope[f]) {
          filter = (filter || ejs.BoolFilter()).must(ejs.TermFilter(f, $scope[f]))
        }
      });

      return makeTimeFilter(filter);
    }

    function makePcapQuery(keys) {
      var params = {
        keys: keys,
        includeReverseTraffic: $scope.includeReverseTraffic || false,
      };

      if ($scope.ts) {
        var ts = Date.parse($scope.ts);
        ts = ts > 0 ? ts : parseInt($scope.ts);
        if (ts <= 9999999999999) {
          ts *= 1000;
        }

        $.extend(params, {
          startTime: ts - $scope.duration.sec * 1000,
          endTime: ts + $scope.duration.sec + 1000
        });
      }
      return params;
    }

    function reversePcapId(pcapId) {
      var identifiers = pcapId.split('-');
      return [
        identifiers[1], identifiers[0], identifiers[2],
        identifiers[4], identifiers[3]
      ].join('-');
    }

    $scope.setStream = function(idx, pcapId, page) {
      $scope.stream = null;
      $scope.selectedStream = idx;
      $scope.selectedPacket = null;
      $scope.packetData = [];
      $scope.packet = null;
      $scope.streamPage = page;

      var filter = makeTimeFilter() || ejs.BoolFilter();
      var termFilters = [ejs.TermFilter('pcap_id', pcapId)];
      if ($scope.includeReverseTraffic) {
        termFilters.push(
          ejs.TermFilter('pcap_id', reversePcapId(pcapId)));
      }

      filter = filter.must(ejs.OrFilter(termFilters))

      var size = 500;
      $scope.streamFrom = (page || 0) * size;
      var request = ejs.Request()
        .indices('pcap_all')
        .types('pcap_doc')
        .filter(filter)
        .sort('ts_micro')
        .from($scope.streamFrom)
        .size(size);

      $scope.inspector = angular.toJson(JSON.parse(request.toString()),true);
      request.doSearch(function(stream) {
        $scope.stream = stream;
      });
    };

    $scope.downloadStream = function(doc) {
      var response = true;
      if (doc.count > 500) {
        var message = [
          'Are you sure you want to download', doc.count, 'packets?'
        ].join(' ');
        response = window.confirm(message);
      }

      if (response) {
        var params = makePcapQuery(doc.term);
        params.raw = true;
        window.location = '/pcap/getPcapsByKeys?' + serialize(params);
      }
    };

    $scope.setPacket = function(idx, message) {
      $scope.selectedPacket = idx;
      $scope.packetData = [];
      $scope.packet = null;
      var params = makePcapQuery([
        message.pcap_id, message.ip_id, message.frag_offset
      ].join('-'));

      $scope.evtSource && $scope.evtSource.close();
      $scope.evtSource = new EventSource('/pcap/getPcapsByKeys?' + serialize(params));

      $scope.evtSource.onmessage = function(e) {
        $scope.packetData.push(JSON.parse(e.data));
        $scope.$apply();
      };

      $scope.evtSource.onerror = function () {
        $scope.evtSource.close();
        $scope.evtSource = null;
      };
    };

    $scope.setPcapPacket = function(p) {
      $scope.packet = p;
    };

    $scope.parsePcapId = function(pcapId) {
      var identifiers = pcapId.split('-');
      return {
        src_ip: $filter('hexToIp')(identifiers[0]),
        dst_ip: $filter('hexToIp')(identifiers[1]),
        protocol: parseInt(identifiers[2], 10),
        src_port: parseInt(identifiers[3], 10),
        dst_port: parseInt(identifiers[4], 10)
      };
    };

    $scope.search = function() {
      $scope.selectedStream = null;
      $scope.selectedPacket = null;
      $scope.packetData = null;
      $scope.packet = null;
      $scope.results = null;
      $scope.stream = null;
      $scope.streamPage = 0;
      $scope.streamFrom = 0;

      var filter = makeFilter();
      var facet = ejs.TermsFacet('packets').field('pcap_id');

      if (filter) {
        facet = facet.facetFilter(filter)
      }

      var size = 100;
      var request = ejs.Request()
        .indices('pcap_all')
        .types('pcap_doc')
        .facet(facet.size(size))

      $scope.inspector = angular.toJson(JSON.parse(request.toString()),true);
      request.doSearch(function(results) {
        $scope.results = results;
      });
    };
  });

  // From: http://stackoverflow.com/questions/14430655/recursion-in-angular-directives
  module.factory('RecursionHelper', ['$compile', function($compile) {
    /* The MIT License (MIT)

    Copyright (c) 2014 Mark Lagendijk

    Permission is hereby granted, free of charge, to any person obtaining a copy of
    this software and associated documentation files (the "Software"), to deal in
    the Software without restriction, including without limitation the rights to
    use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
    the Software, and to permit persons to whom the Software is furnished to do so,
    subject to the following conditions:

    The above copyright notice and this permission notice shall be included in all
    copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
    FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
    COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
    IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
    CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

    This license only applies to this angular factory.
    */

    return {
      /**
       * Manually compiles the element, fixing the recursion loop.
       * @param element
       * @param [link] A post-link function, or an object with function(s) registered via pre and post properties.
       * @returns An object containing the linking functions.
       */
      compile: function(element, link) {
        // Normalize the link parameter
        if(angular.isFunction(link)){
          link = { post: link };
        }

        // Break the recursion loop by removing the contents
        var contents = element.contents().remove();
        var compiledContents;
        return {
          pre: (link && link.pre) ? link.pre : null,
          /**
           * Compiles and re-adds the contents
           */
          post: function(scope, element){
            // Compile the contents
            if(!compiledContents){
              compiledContents = $compile(contents);
            }
            // Re-add the compiled contents to the element
            compiledContents(scope, function(clone) {
              element.append(clone);
            });

            // Call the post-linking function, if any
            if(link && link.post){
              link.post.apply(null, arguments);
            }
          }
        };
      }
    };
  }]);

  module.directive('packet', function() {
    return {
      templateUrl: 'packet.html',
      scope: {
        packet: '='
      },
      controller: function($scope) {
        return {
          toggle: function(proto, value) {
            proto.expanded = value;
            if (proto.parent) {
              this.toggle(proto.parent.$, value);
            }
          },
          select: function(proto) {
            if ($scope.selected == proto.$) {
              $scope.selected.expanded = !$scope.selected.expanded;
              return;
            }

            $scope.selected && this.toggle($scope.selected, false);
            this.toggle(proto.$, true);
            $scope.selected = proto.$;
          },
          uid: function(obj) {
            return [
              String(obj.showname),
              (Math.random() + 1).toString(36).substring(7)
            ].join('_');
          },
          buildHighlightMap: function(proto) {
            var self = this;
            $.each(proto.field || [], function(i, field) {
              self.processField(proto, field);
            });
          },
          processField: function(proto, field) {
            var start = parseInt(field.$.pos);
            var end = start + parseInt(field.$.size) - 1;
            field.$.uid = this.uid(field.$);
            field.$.parent = proto;

            if (proto.$.name == 'geninfo') {
              field.$.showname += ': ' + field.$.value;
              return;
            }

            if (end >= start) {
              this.highlightMap.push({
                $: field.$,
                uid: field.$.uid,
                start: start,
                end: end
              });

              field.field && this.buildHighlightMap(field);
            }
          }
        };
      },
      link: function(scope, elm, attr, controller) {
        scope.$watch('packet', function(value, oldValue) {
          console.log(value);
          controller.highlightMap = [];
          scope.selected = {};
          if (value) {
            $.each(value.proto, function(i, proto) {
              proto.$.uid = controller.uid(proto.$);
              controller.buildHighlightMap(proto);
            });
          }
        });
      }
    };
  });

  module.directive('proto', function(RecursionHelper) {
    return {
      templateUrl: 'proto.html',
      scope: {
        proto: '=',
        selected: '=',
        padding: '='
      },
      require: '^packet',
      compile: function(element) {
        return RecursionHelper.compile(element, function(scope, elm, attr, packet) {

          scope.select = $.proxy(packet.select, packet);
          scope.$watch('padding', function(value) {
            scope.nextPadding = parseInt(value || '0') + 30;
          })

        });
      }
    };
  });

  module.directive('hexBytes', function() {
    return {
      templateUrl: 'hexBytes.html',
      require: '^packet',
      scope: {
        selected: '=',
        dump: '='
      },
      link: function(scope, elm, attr, packet) {
        scope.$watch('dump', function(value, oldValue) {
          scope.bytes = [];
          for (var b = 0; b < scope.dump.length; b += 2) {
            scope.bytes.push(scope.dump.slice(b, b + 2));
          }
        });

        scope.selectByte = function(offset) {
          var selected = $.grep(packet.highlightMap, function(obj) {
            return obj.start <= offset && obj.end >= offset;
          })[0];

          selected && packet.select(selected);
        };
      }
    };
  });

  module.filter('sourcePort', function() {
    return function(sourcePort) {
      if (sourcePort) {
        var parts = sourcePort.split(': ');
        return parts[parts.length - 1];
      }
    }
  });

  module.filter('hexToIp', function() {
    return function(hex) {
      var parts = [];
      for (var p = 0; p < 4; p++) {
        var hexPart = hex.slice(p * 2, p * 2 + 2)
        parts.push(parseInt(hexPart, 16));
      }
      return parts.join('.');
    }
  });

  module.filter('num', function() {
    return function(input) {
      return parseInt(input, 10);
    }
  });

  module.filter('toAscii', function() {
    return function (text) {
      var code = parseInt(text, 16);
      var c = code <= 32 || code > 127 ? '.' : String.fromCharCode(code);
      return c + ' ';
    };
  });
});
<commit_msg>add timestamps to pcap service request.<commit_after>/** @scratch /panels/5
 *
 * include::panels/pcap.asciidoc[]
 */

/** @scratch /panels/pcap/0
 * == pcap
 * Status: *Stable*
 *
 * The pcap panel is used for displaying static pcap formated as markdown, sanitized html or as plain
 * pcap.
 *
 */

define([
  'angular',
  'app',
  'lodash',
  'require',
  'jquery',
  'kbn'
],
function (angular, app, _, require, kbn) {
  'use strict';

  var module = angular.module('kibana.panels.pcap', []);
  app.useModule(module);

  module.controller('pcap', function($filter, $scope, $http) {
    $scope.user = window.user;

$scope.panelMeta = {
      modals : [
        {
          description: "Inspect",
          icon: "icon-info-sign",
          partial: "app/partials/inspector.html",
          show: $scope.panel.spyable
        }
      ],
      status  : 'Stable',
      description : 'OpenSoc PCAP viewer'
    };

    $scope.durations = [
      {desc: '1d', sec: 24 * 60 * 60},
      {desc: '6h', sec: 6 * 60 * 60},
      {desc: '1h', sec: 60 * 60},
      {desc: '30m', sec: 30 * 60},
      {desc: '15m', sec: 15 * 60},
      {desc: '5m', sec: 5 * 60},
      {desc: '1m', sec: 60},
      {desc: '30s', sec: 10},
      {desc: '10s', sec: 10},
      {desc: '5s', sec: 5},
      {desc: '1s', sec: 1}
    ];

    $scope.streamPage = 0;
    $scope.duration = $scope.durations[$scope.durations.length - 1];

    function serialize(obj) {
      var str = [];
      for(var p in obj)
        if (obj.hasOwnProperty(p)) {
          str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
        }
      return str.join("&");
    }

    function makeTimeFilter(filter) {
      if($scope.ts) {
        var ts = Date.parse($scope.ts);
        ts = ts > 0 ? ts : parseInt($scope.ts);
        if (ts <= 999999999999) {
          ts *= 1000;
        }

        filter = (filter || ejs.BoolFilter())
          .must(ejs.RangeFilter('ts_micro')
            .gte((ts - $scope.duration.sec * 1000) * 1000)
            .lte((ts + $scope.duration.sec * 1000) * 1000))
      }
      return filter;
    }

    function makeFilter() {
      var fields = [
        'ip_src_addr', 'ip_dst_addr',
        'ip_src_port', 'ip_dst_port',
        'ip_protocol'
      ];

      var filter;
      $.each(fields, function(i, f) {
        if ($scope[f]) {
          filter = (filter || ejs.BoolFilter()).must(ejs.TermFilter(f, $scope[f]))
        }
      });

      return makeTimeFilter(filter);
    }

    function makePcapQuery(keys) {
      var params = {
        keys: keys,
        includeReverseTraffic: $scope.includeReverseTraffic || false,
      };

      if ($scope.ts) {
        var ts = Date.parse($scope.ts);
        ts = ts > 0 ? ts : parseInt($scope.ts);
        if (ts <= 9999999999999) {
          ts *= 1000;
        }

        $.extend(params, {
          startTime: ts - $scope.duration.sec * 1000,
          endTime: ts + $scope.duration.sec + 1000
        });
      }
      return params;
    }

    function reversePcapId(pcapId) {
      var identifiers = pcapId.split('-');
      return [
        identifiers[1], identifiers[0], identifiers[2],
        identifiers[4], identifiers[3]
      ].join('-');
    }

    $scope.setStream = function(idx, pcapId, page) {
      $scope.stream = null;
      $scope.selectedStream = idx;
      $scope.selectedPacket = null;
      $scope.packetData = [];
      $scope.packet = null;
      $scope.streamPage = page;

      var filter = makeTimeFilter() || ejs.BoolFilter();
      var termFilters = [ejs.TermFilter('pcap_id', pcapId)];
      if ($scope.includeReverseTraffic) {
        termFilters.push(
          ejs.TermFilter('pcap_id', reversePcapId(pcapId)));
      }

      filter = filter.must(ejs.OrFilter(termFilters))

      var size = 500;
      $scope.streamFrom = (page || 0) * size;
      var request = ejs.Request()
        .indices('pcap_all')
        .types('pcap_doc')
        .filter(filter)
        .sort('ts_micro')
        .from($scope.streamFrom)
        .size(size);

      $scope.inspector = angular.toJson(JSON.parse(request.toString()),true);
      request.doSearch(function(stream) {
        $scope.stream = stream;
      });
    };

    $scope.downloadStream = function(doc) {
      var response = true;
      if (doc.count > 500) {
        var message = [
          'Are you sure you want to download', doc.count, 'packets?'
        ].join(' ');
        response = window.confirm(message);
      }

      if (response) {
        var params = makePcapQuery(doc.term);
        params.raw = true;
        window.location = '/pcap/getPcapsByKeys?' + serialize(params);
      }
    };

    $scope.setPacket = function(idx, message) {
      $scope.selectedPacket = idx;
      $scope.packetData = [];
      $scope.packet = null;
      var params = makePcapQuery([
        message.pcap_id, message.ip_id, message.frag_offset
      ].join('-'));

      params.startTime = Math.floor(message.ts_micro / 1000);
      params.endTime = params.startTime + 1;

      $scope.evtSource && $scope.evtSource.close();
      $scope.evtSource = new EventSource('/pcap/getPcapsByKeys?' + serialize(params));

      $scope.evtSource.onmessage = function(e) {
        $scope.packetData.push(JSON.parse(e.data));
        $scope.$apply();
      };

      $scope.evtSource.onerror = function () {
        $scope.evtSource.close();
        $scope.evtSource = null;
      };
    };

    $scope.setPcapPacket = function(p) {
      $scope.packet = p;
    };

    $scope.parsePcapId = function(pcapId) {
      var identifiers = pcapId.split('-');
      return {
        src_ip: $filter('hexToIp')(identifiers[0]),
        dst_ip: $filter('hexToIp')(identifiers[1]),
        protocol: parseInt(identifiers[2], 10),
        src_port: parseInt(identifiers[3], 10),
        dst_port: parseInt(identifiers[4], 10)
      };
    };

    $scope.search = function() {
      $scope.selectedStream = null;
      $scope.selectedPacket = null;
      $scope.packetData = null;
      $scope.packet = null;
      $scope.results = null;
      $scope.stream = null;
      $scope.streamPage = 0;
      $scope.streamFrom = 0;

      var filter = makeFilter();
      var facet = ejs.TermsFacet('packets').field('pcap_id');

      if (filter) {
        facet = facet.facetFilter(filter)
      }

      var size = 100;
      var request = ejs.Request()
        .indices('pcap_all')
        .types('pcap_doc')
        .facet(facet.size(size))

      $scope.inspector = angular.toJson(JSON.parse(request.toString()),true);
      request.doSearch(function(results) {
        $scope.results = results;
      });
    };
  });

  // From: http://stackoverflow.com/questions/14430655/recursion-in-angular-directives
  module.factory('RecursionHelper', ['$compile', function($compile) {
    /* The MIT License (MIT)

    Copyright (c) 2014 Mark Lagendijk

    Permission is hereby granted, free of charge, to any person obtaining a copy of
    this software and associated documentation files (the "Software"), to deal in
    the Software without restriction, including without limitation the rights to
    use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
    the Software, and to permit persons to whom the Software is furnished to do so,
    subject to the following conditions:

    The above copyright notice and this permission notice shall be included in all
    copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
    FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
    COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
    IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
    CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

    This license only applies to this angular factory.
    */

    return {
      /**
       * Manually compiles the element, fixing the recursion loop.
       * @param element
       * @param [link] A post-link function, or an object with function(s) registered via pre and post properties.
       * @returns An object containing the linking functions.
       */
      compile: function(element, link) {
        // Normalize the link parameter
        if(angular.isFunction(link)){
          link = { post: link };
        }

        // Break the recursion loop by removing the contents
        var contents = element.contents().remove();
        var compiledContents;
        return {
          pre: (link && link.pre) ? link.pre : null,
          /**
           * Compiles and re-adds the contents
           */
          post: function(scope, element){
            // Compile the contents
            if(!compiledContents){
              compiledContents = $compile(contents);
            }
            // Re-add the compiled contents to the element
            compiledContents(scope, function(clone) {
              element.append(clone);
            });

            // Call the post-linking function, if any
            if(link && link.post){
              link.post.apply(null, arguments);
            }
          }
        };
      }
    };
  }]);

  module.directive('packet', function() {
    return {
      templateUrl: 'packet.html',
      scope: {
        packet: '='
      },
      controller: function($scope) {
        return {
          toggle: function(proto, value) {
            proto.expanded = value;
            if (proto.parent) {
              this.toggle(proto.parent.$, value);
            }
          },
          select: function(proto) {
            if ($scope.selected == proto.$) {
              $scope.selected.expanded = !$scope.selected.expanded;
              return;
            }

            $scope.selected && this.toggle($scope.selected, false);
            this.toggle(proto.$, true);
            $scope.selected = proto.$;
          },
          uid: function(obj) {
            return [
              String(obj.showname),
              (Math.random() + 1).toString(36).substring(7)
            ].join('_');
          },
          buildHighlightMap: function(proto) {
            var self = this;
            $.each(proto.field || [], function(i, field) {
              self.processField(proto, field);
            });
          },
          processField: function(proto, field) {
            var start = parseInt(field.$.pos);
            var end = start + parseInt(field.$.size) - 1;
            field.$.uid = this.uid(field.$);
            field.$.parent = proto;

            if (proto.$.name == 'geninfo') {
              field.$.showname += ': ' + field.$.value;
              return;
            }

            if (end >= start) {
              this.highlightMap.push({
                $: field.$,
                uid: field.$.uid,
                start: start,
                end: end
              });

              field.field && this.buildHighlightMap(field);
            }
          }
        };
      },
      link: function(scope, elm, attr, controller) {
        scope.$watch('packet', function(value, oldValue) {
          console.log(value);
          controller.highlightMap = [];
          scope.selected = {};
          if (value) {
            $.each(value.proto, function(i, proto) {
              proto.$.uid = controller.uid(proto.$);
              controller.buildHighlightMap(proto);
            });
          }
        });
      }
    };
  });

  module.directive('proto', function(RecursionHelper) {
    return {
      templateUrl: 'proto.html',
      scope: {
        proto: '=',
        selected: '=',
        padding: '='
      },
      require: '^packet',
      compile: function(element) {
        return RecursionHelper.compile(element, function(scope, elm, attr, packet) {

          scope.select = $.proxy(packet.select, packet);
          scope.$watch('padding', function(value) {
            scope.nextPadding = parseInt(value || '0') + 30;
          })

        });
      }
    };
  });

  module.directive('hexBytes', function() {
    return {
      templateUrl: 'hexBytes.html',
      require: '^packet',
      scope: {
        selected: '=',
        dump: '='
      },
      link: function(scope, elm, attr, packet) {
        scope.$watch('dump', function(value, oldValue) {
          scope.bytes = [];
          for (var b = 0; b < scope.dump.length; b += 2) {
            scope.bytes.push(scope.dump.slice(b, b + 2));
          }
        });

        scope.selectByte = function(offset) {
          var selected = $.grep(packet.highlightMap, function(obj) {
            return obj.start <= offset && obj.end >= offset;
          })[0];

          selected && packet.select(selected);
        };
      }
    };
  });

  module.filter('sourcePort', function() {
    return function(sourcePort) {
      if (sourcePort) {
        var parts = sourcePort.split(': ');
        return parts[parts.length - 1];
      }
    }
  });

  module.filter('hexToIp', function() {
    return function(hex) {
      var parts = [];
      for (var p = 0; p < 4; p++) {
        var hexPart = hex.slice(p * 2, p * 2 + 2)
        parts.push(parseInt(hexPart, 16));
      }
      return parts.join('.');
    }
  });

  module.filter('num', function() {
    return function(input) {
      return parseInt(input, 10);
    }
  });

  module.filter('toAscii', function() {
    return function (text) {
      var code = parseInt(text, 16);
      var c = code <= 32 || code > 127 ? '.' : String.fromCharCode(code);
      return c + ' ';
    };
  });
});
<|endoftext|>


<commit_before>/**
 * Utility javascript functions used across the different CT FE pages.
 */
this.ctfe = this.ctfe || function() {
  "use strict";

  var ctfe = {};

  /**
   * Converts the timestamp used in CTFE DB into a user friendly string.
   **/
  ctfe.getFormattedTimestamp = function(timestamp) {
    if (timestamp == 0) {
      return "<pending>";
    }
    return ctfe.getTimestamp(timestamp).toLocaleString();
  }

  /**
   * Converts the timestamp used in CTFE DB into a Javascript timestamp.
   */
  ctfe.getTimestamp = function(timestamp) {
    if (timestamp == 0) {
      return timestamp;
    }
    var pattern = /(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/;
    return new Date(String(timestamp).replace(pattern,'$1-$2-$3T$4:$5:$6'));
  }

  /**
   * Convert from Javascript timestamp to one recognized by CTFE DB.
   */
  ctfe.getCtDbTimestamp = function(dbTimestamp) {
    var d = new Date(dbTimestamp);
    var timestamp = String(d.getUTCFullYear()) + sk.human.pad(d.getUTCMonth()+1, 2) +
                    sk.human.pad(d.getUTCDate(), 2) + sk.human.pad(d.getUTCHours(), 2) +
                    sk.human.pad(d.getUTCMinutes(), 2) + sk.human.pad(d.getUTCSeconds(), 2);
    return timestamp
  }

  /**
   * Get user friendly string for repeat after days.
   */
  ctfe.formatRepeatAfterDays = function(num) {
    if (num == 0) {
      return "N/A";
    } else if (num == 1) {
      return "Daily";
    } else if (num == 7) {
      return "Weekly";
    } else {
      return "Every " + num + " days";
    }
  }

  /**
   * Functions to work with information about page sets.
   */
  ctfe.pageSets = {};

  /**
   * Returns a Promise that resolves to an array of defined page sets.
   **/
  ctfe.pageSets.getPageSets = function() {
    return sk.post("/_/page_sets/")
        .then(JSON.parse);
  }

  /**
   * Returns an identifier for the given page set.
   **/
  ctfe.pageSets.getKey = function(pageSet) {
    return pageSet.key;
  }

  /**
   * Returns a short description for the given page set.
   **/
  ctfe.pageSets.getDescription = function(pageSet) {
    return pageSet.description;
  }

  /**
   * Functions to work with information about Chromium builds.
   */
  ctfe.chromiumBuild = {};

  /**
   * Returns a Promise that resolves to an array of completed builds.
   **/
  ctfe.chromiumBuild.getBuilds = function() {
    var queryParams = {
      "size": 20,
      "successful": true,
    }
    var queryStr = "?" + sk.query.fromObject(queryParams);
    return sk.post("/_/get_chromium_build_tasks" + queryStr)
        .then(JSON.parse)
        .then(function (json) {
          return json.data;
        });
  }

  /**
   * Returns a more human-readable GIT commit hash.
   */
  ctfe.chromiumBuild.shortHash = function(commitHash) {
    return commitHash.substr(0, 7);
  }

  /**
   * Returns a short description for the given build.
   **/
  ctfe.chromiumBuild.getDescription = function(build) {
    return ctfe.chromiumBuild.shortHash(build.ChromiumRev) + "-" +
        ctfe.chromiumBuild.shortHash(build.SkiaRev) + " (Chromium rev created on " +
        ctfe.getFormattedTimestamp(build.ChromiumRevTs.Int64) + ")";
  }

  /**
   * Returns a URL with details about the given Chromium commit hash.
   **/
  ctfe.chromiumBuild.chromiumCommitUrl = function(commitHash) {
    return "https://chromium.googlesource.com/chromium/src.git/+/" + commitHash;
  }

  /**
   * Returns a URL with details about the given Skia commit hash.
   **/
  ctfe.chromiumBuild.skiaCommitUrl = function(commitHash) {
    return "https://skia.googlesource.com/skia/+/" + commitHash;
  }

  /**
   * Functions to work with information about SKP repositories.
   */
  ctfe.skpRepositories = {};

  /**
   * Returns a Promise that resolves to an array of defined SKP repositories.
   **/
  ctfe.skpRepositories.getRepositories = function() {
    var queryParams = {
      "size": 20,
      "successful": true,
    }
    var queryStr = "?" + sk.query.fromObject(queryParams);
    return sk.post("/_/get_capture_skp_tasks" + queryStr)
        .then(JSON.parse)
        .then(function (json) {
          return json.data;
        });
  }

  /**
   * Returns a short description for the given SKP repository.
   **/
  ctfe.skpRepositories.getDescription = function(repository) {
    return repository.PageSets + " captured with " +
        ctfe.chromiumBuild.shortHash(repository.ChromiumRev) + "-" +
        ctfe.chromiumBuild.shortHash(repository.SkiaRev) + " (Desc: " +
        repository.Description + ")";
  }

  /**
   * Returns true and displays an error if user has more than 1 active task.
   */
  ctfe.moreThanOneActiveTask = function(sizeOfUserQueue) {
    if (sizeOfUserQueue > 1) {
        sk.errorMessage("You have " + sizeOfUserQueue + " currently running tasks. Please wait " +
                        "for them to complete before scheduling more CT tasks.");
    }
    return sizeOfUserQueue > 1;
  }

  /**
   * Returns a string that describes the specified CLs.
   **/
  ctfe.getDescriptionOfCls = function(chromiumClDesc, skiaClDesc, catapultClDesc) {
    if (!chromiumClDesc && !skiaClDesc && !catapultClDesc) {
      return "";
    }
    var str = "Testing ";
    var prev = false;
    if (chromiumClDesc) {
      str += chromiumClDesc;
      prev = true;
    }
    if (skiaClDesc) {
      if (prev) {
        str += " and ";
      }
      str += skiaClDesc;
      prev = true;
    }
    if (catapultClDesc) {
      if (prev) {
        str += " and ";
      }
      str += catapultClDesc;
      prev = true;
    }
    return str;
  }

  return ctfe;
}();
<commit_msg>Disable currently running task count check<commit_after>/**
 * Utility javascript functions used across the different CT FE pages.
 */
this.ctfe = this.ctfe || function() {
  "use strict";

  var ctfe = {};

  /**
   * Converts the timestamp used in CTFE DB into a user friendly string.
   **/
  ctfe.getFormattedTimestamp = function(timestamp) {
    if (timestamp == 0) {
      return "<pending>";
    }
    return ctfe.getTimestamp(timestamp).toLocaleString();
  }

  /**
   * Converts the timestamp used in CTFE DB into a Javascript timestamp.
   */
  ctfe.getTimestamp = function(timestamp) {
    if (timestamp == 0) {
      return timestamp;
    }
    var pattern = /(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/;
    return new Date(String(timestamp).replace(pattern,'$1-$2-$3T$4:$5:$6'));
  }

  /**
   * Convert from Javascript timestamp to one recognized by CTFE DB.
   */
  ctfe.getCtDbTimestamp = function(dbTimestamp) {
    var d = new Date(dbTimestamp);
    var timestamp = String(d.getUTCFullYear()) + sk.human.pad(d.getUTCMonth()+1, 2) +
                    sk.human.pad(d.getUTCDate(), 2) + sk.human.pad(d.getUTCHours(), 2) +
                    sk.human.pad(d.getUTCMinutes(), 2) + sk.human.pad(d.getUTCSeconds(), 2);
    return timestamp
  }

  /**
   * Get user friendly string for repeat after days.
   */
  ctfe.formatRepeatAfterDays = function(num) {
    if (num == 0) {
      return "N/A";
    } else if (num == 1) {
      return "Daily";
    } else if (num == 7) {
      return "Weekly";
    } else {
      return "Every " + num + " days";
    }
  }

  /**
   * Functions to work with information about page sets.
   */
  ctfe.pageSets = {};

  /**
   * Returns a Promise that resolves to an array of defined page sets.
   **/
  ctfe.pageSets.getPageSets = function() {
    return sk.post("/_/page_sets/")
        .then(JSON.parse);
  }

  /**
   * Returns an identifier for the given page set.
   **/
  ctfe.pageSets.getKey = function(pageSet) {
    return pageSet.key;
  }

  /**
   * Returns a short description for the given page set.
   **/
  ctfe.pageSets.getDescription = function(pageSet) {
    return pageSet.description;
  }

  /**
   * Functions to work with information about Chromium builds.
   */
  ctfe.chromiumBuild = {};

  /**
   * Returns a Promise that resolves to an array of completed builds.
   **/
  ctfe.chromiumBuild.getBuilds = function() {
    var queryParams = {
      "size": 20,
      "successful": true,
    }
    var queryStr = "?" + sk.query.fromObject(queryParams);
    return sk.post("/_/get_chromium_build_tasks" + queryStr)
        .then(JSON.parse)
        .then(function (json) {
          return json.data;
        });
  }

  /**
   * Returns a more human-readable GIT commit hash.
   */
  ctfe.chromiumBuild.shortHash = function(commitHash) {
    return commitHash.substr(0, 7);
  }

  /**
   * Returns a short description for the given build.
   **/
  ctfe.chromiumBuild.getDescription = function(build) {
    return ctfe.chromiumBuild.shortHash(build.ChromiumRev) + "-" +
        ctfe.chromiumBuild.shortHash(build.SkiaRev) + " (Chromium rev created on " +
        ctfe.getFormattedTimestamp(build.ChromiumRevTs.Int64) + ")";
  }

  /**
   * Returns a URL with details about the given Chromium commit hash.
   **/
  ctfe.chromiumBuild.chromiumCommitUrl = function(commitHash) {
    return "https://chromium.googlesource.com/chromium/src.git/+/" + commitHash;
  }

  /**
   * Returns a URL with details about the given Skia commit hash.
   **/
  ctfe.chromiumBuild.skiaCommitUrl = function(commitHash) {
    return "https://skia.googlesource.com/skia/+/" + commitHash;
  }

  /**
   * Functions to work with information about SKP repositories.
   */
  ctfe.skpRepositories = {};

  /**
   * Returns a Promise that resolves to an array of defined SKP repositories.
   **/
  ctfe.skpRepositories.getRepositories = function() {
    var queryParams = {
      "size": 20,
      "successful": true,
    }
    var queryStr = "?" + sk.query.fromObject(queryParams);
    return sk.post("/_/get_capture_skp_tasks" + queryStr)
        .then(JSON.parse)
        .then(function (json) {
          return json.data;
        });
  }

  /**
   * Returns a short description for the given SKP repository.
   **/
  ctfe.skpRepositories.getDescription = function(repository) {
    return repository.PageSets + " captured with " +
        ctfe.chromiumBuild.shortHash(repository.ChromiumRev) + "-" +
        ctfe.chromiumBuild.shortHash(repository.SkiaRev) + " (Desc: " +
        repository.Description + ")";
  }

  /**
   * Returns true and displays an error if user has more than 1 active task.
   */
  ctfe.moreThanOneActiveTask = function(sizeOfUserQueue) {
    return false;
    /*
    if (sizeOfUserQueue > 1) {
        sk.errorMessage("You have " + sizeOfUserQueue + " currently running tasks. Please wait " +
                        "for them to complete before scheduling more CT tasks.");
    }
    return sizeOfUserQueue > 1;
    */
  }

  /**
   * Returns a string that describes the specified CLs.
   **/
  ctfe.getDescriptionOfCls = function(chromiumClDesc, skiaClDesc, catapultClDesc) {
    if (!chromiumClDesc && !skiaClDesc && !catapultClDesc) {
      return "";
    }
    var str = "Testing ";
    var prev = false;
    if (chromiumClDesc) {
      str += chromiumClDesc;
      prev = true;
    }
    if (skiaClDesc) {
      if (prev) {
        str += " and ";
      }
      str += skiaClDesc;
      prev = true;
    }
    if (catapultClDesc) {
      if (prev) {
        str += " and ";
      }
      str += catapultClDesc;
      prev = true;
    }
    return str;
  }

  return ctfe;
}();
<|endoftext|>


<commit_before>require.config({
  baseUrl: "/Oskari/", // the base is set to requirejs lib to help requiring 3rd party libs
  paths: { // some path shortcuts to ease declarations
    "oskari": "src/oskari/oskari",
    "oskari-with-app": "src/oskari/oskari-with-app",
    "oskari-with-loader": "src/oskari/oskari-with-loader",
    "jquery": "http://code.jquery.com/jquery-1.9.1",
    "jquery-ui": "libraries/jquery/jquery-ui-1.9.2.custom",
    "dragevent": "libraries/jquery/jquery.event.drag-2.0.min",
    "jquery-migrate": "libraries/jquery/jquery-migrate-1.2.1-modified",
    "css": "libraries/requirejs/lib/css",
    "json": "libraries/requirejs/lib/json",
    "domReady": "libraries/requirejs/lib/domReady",
    "text": "libraries/requirejs/lib/text",
    "i18n": "libraries/requirejs/lib/i18n",
    "normalize": "libraries/requirejs/lib/normalize",
    "lodash": "libraries/lodash/2.3.0/lodash"
  },
  map: {
    // '*' means all modules will get 'jquery-private'
    // for their 'jquery' dependency.
    "*": {
      "oskari": "oskari-with-app",
      "jquery": "jquery-migrate",
      // TODO: rename openlayers-default-theme to map or something
      "openlayers-default-theme": "src/oskari/map-ol2/module",
      "mapfull": "src/mapping/mapmodule/ol2/mapfull/module",
      "divmanazer": "src/framework/divmanazer/module",
      "toolbar": "src/framework/toolbar/module",
      "statehandler": "src/framework/statehandler/module",
      "infobox": "src/framework/infobox/module",
      "search": "src/framework/search/module",
      "routesearch": "src/framework/routesearch/module",
      "layerselector2": "src/framework/layerselector2/module",
      "layerselection2": "src/framework/layerselection2/module",
      "personaldata": "src/framework/personaldata/module",
      "featuredata2": "src/framework/featuredata2/module",
      "maplegend": "src/framework/maplegend/module",
      "userguide": "src/framework/userguide/module",
      "backendstatus": "src/framework/backendstatus/module",
      "postprocessor": "src/framework/postprocessor/module",
      "publisher": "src/framework/publisher/module",
      "guidedtour": "src/framework/guidedtour/module",
      "mapstats": "src/framework/mapstats/module",
      "mapwfs2": "src/framework/mapwfs2/module",
      "mapwmts": "src/framework/mapwmts/module",
      "mapmyplaces": "src/framework/mapmyplaces/module",
      "mapanalysis": "src/framework/mapanalysis/module",
      "statsgrid": "src/statistics/statsgrid/module",
      "metadataflyout": "src/catalogue/metadataflyout/module",
      "metadatacatalogue": "src/catalogue/metadatacatalogue/module",
      "printout": "src/framework/printout/module",
      "coordinatedisplay": "src/framework/coordinatedisplay/module",
      "analyse": "src/analysis/analyse/module",
      "myplaces2": "src/framework/myplaces2/module",
      "promote": "src/framework/promote/module",
      "oskariui": "src/framework/oskariui/module"
    },

    // 'jquery-private' wants the real jQuery module
    // though. If this line was not here, there would
    // be an unresolvable cyclic dependency.
    "jquery-migrate": {
      "jquery": "jquery"
    }
  },
  shim: {
    "jquery-ui": {
      exports: "$",
      deps: ['jquery']
    },
    "dragevent": {
      exports: "$",
      deps: ['jquery']
    },
    "oskari": {
      exports: "Oskari"
    },
    "lodash": {
      exports: "_"
    }
  },
  waitSeconds: 30
});

/* loading base requirements */
require(["jquery", "oskari-with-app", "domReady"],
/**
 * ... now we have jQuery and Oskari
 */
function(jQuery, Oskari) {

  function getURLParameter(name) {
      var re = name + '=' + '([^&]*)(&|$)';
      var value = RegExp(re).exec(location.search);
      if (value && value.length && value.length > 1) {
          value = value[1];
      }
      if (value) {
          return decodeURI(value);
      }
      return null;
  }

  function gfiParamHandler(sandbox) {
      if (getURLParameter('showGetFeatureInfo') != 'true') {
          return;
      }
      var lon = sandbox.getMap().getX();
      var lat = sandbox.getMap().getY();
      var mapModule = sandbox.findRegisteredModuleInstance('MainMapModule');
      var px = mapModule.getMap().getViewPortPxFromLonLat({
          lon: lon,
          lat: lat
      });
      sandbox.postRequestByName('MapModulePlugin.GetFeatureInfoRequest', [lon, lat, px.x, px.y]);
  }

  var config = "json!applications/oskari2/full-map_guest/minifierAppSetup.json";
  if (window.ajaxUrl) {
      // populate url with possible control parameters
      var getAppSetupParams = "";
      if(typeof window.controlParams == 'object') {
          for(var key in controlParams) {
              getAppSetupParams += "&" + key + "=" + controlParams[key];
          }
      }

      config = "json!" + window.ajaxUrl + "action_route=GetAppSetup" + getAppSetupParams;
  }

  /* loading configuration */
  require([config], function(appSetup) {

      Oskari.setLang(language);

      Oskari.Application
          .create()
          .setStartupSequence(appSetup.startupSequence)
          .setConfiguration(appSetup.configuration)
          .start()
          .success(function() {
              var sb = Oskari.getSandbox();
              gfiParamHandler(sb);
          });
  });
});<commit_msg>Fixed appsetupconfig.json path<commit_after>require.config({
  baseUrl: "/Oskari/", // the base is set to requirejs lib to help requiring 3rd party libs
  paths: { // some path shortcuts to ease declarations
    "oskari": "src/oskari/oskari",
    "oskari-with-app": "src/oskari/oskari-with-app",
    "oskari-with-loader": "src/oskari/oskari-with-loader",
    "jquery": "http://code.jquery.com/jquery-1.9.1",
    "jquery-ui": "libraries/jquery/jquery-ui-1.9.2.custom",
    "dragevent": "libraries/jquery/jquery.event.drag-2.0.min",
    "jquery-migrate": "libraries/jquery/jquery-migrate-1.2.1-modified",
    "css": "libraries/requirejs/lib/css",
    "json": "libraries/requirejs/lib/json",
    "domReady": "libraries/requirejs/lib/domReady",
    "text": "libraries/requirejs/lib/text",
    "i18n": "libraries/requirejs/lib/i18n",
    "normalize": "libraries/requirejs/lib/normalize",
    "lodash": "libraries/lodash/2.3.0/lodash"
  },
  map: {
    // '*' means all modules will get 'jquery-private'
    // for their 'jquery' dependency.
    "*": {
      "oskari": "oskari-with-app",
      "jquery": "jquery-migrate",
      // TODO: rename openlayers-default-theme to map or something
      "openlayers-default-theme": "src/oskari/map-ol2/module",
      "mapfull": "src/mapping/mapmodule/ol2/mapfull/module",
      "divmanazer": "src/framework/divmanazer/module",
      "toolbar": "src/framework/toolbar/module",
      "statehandler": "src/framework/statehandler/module",
      "infobox": "src/framework/infobox/module",
      "search": "src/framework/search/module",
      "routesearch": "src/framework/routesearch/module",
      "layerselector2": "src/framework/layerselector2/module",
      "layerselection2": "src/framework/layerselection2/module",
      "personaldata": "src/framework/personaldata/module",
      "featuredata2": "src/framework/featuredata2/module",
      "maplegend": "src/framework/maplegend/module",
      "userguide": "src/framework/userguide/module",
      "backendstatus": "src/framework/backendstatus/module",
      "postprocessor": "src/framework/postprocessor/module",
      "publisher": "src/framework/publisher/module",
      "guidedtour": "src/framework/guidedtour/module",
      "mapstats": "src/framework/mapstats/module",
      "mapwfs2": "src/framework/mapwfs2/module",
      "mapwmts": "src/framework/mapwmts/module",
      "mapmyplaces": "src/framework/mapmyplaces/module",
      "mapanalysis": "src/framework/mapanalysis/module",
      "statsgrid": "src/statistics/statsgrid/module",
      "metadataflyout": "src/catalogue/metadataflyout/module",
      "metadatacatalogue": "src/catalogue/metadatacatalogue/module",
      "printout": "src/framework/printout/module",
      "coordinatedisplay": "src/framework/coordinatedisplay/module",
      "analyse": "src/analysis/analyse/module",
      "myplaces2": "src/framework/myplaces2/module",
      "promote": "src/framework/promote/module",
      "oskariui": "src/framework/oskariui/module"
    },

    // 'jquery-private' wants the real jQuery module
    // though. If this line was not here, there would
    // be an unresolvable cyclic dependency.
    "jquery-migrate": {
      "jquery": "jquery"
    }
  },
  shim: {
    "jquery-ui": {
      exports: "$",
      deps: ['jquery']
    },
    "dragevent": {
      exports: "$",
      deps: ['jquery']
    },
    "oskari": {
      exports: "Oskari"
    },
    "lodash": {
      exports: "_"
    }
  },
  waitSeconds: 30
});

/* loading base requirements */
require(["jquery", "oskari-with-app", "domReady"],
/**
 * ... now we have jQuery and Oskari
 */
function(jQuery, Oskari) {

  function getURLParameter(name) {
      var re = name + '=' + '([^&]*)(&|$)';
      var value = RegExp(re).exec(location.search);
      if (value && value.length && value.length > 1) {
          value = value[1];
      }
      if (value) {
          return decodeURI(value);
      }
      return null;
  }

  function gfiParamHandler(sandbox) {
      if (getURLParameter('showGetFeatureInfo') != 'true') {
          return;
      }
      var lon = sandbox.getMap().getX();
      var lat = sandbox.getMap().getY();
      var mapModule = sandbox.findRegisteredModuleInstance('MainMapModule');
      var px = mapModule.getMap().getViewPortPxFromLonLat({
          lon: lon,
          lat: lat
      });
      sandbox.postRequestByName('MapModulePlugin.GetFeatureInfoRequest', [lon, lat, px.x, px.y]);
  }

  var config = "json!applications/oskari2/full-map_guest/appsetupconfig.json";
  if (window.ajaxUrl) {
      // populate url with possible control parameters
      var getAppSetupParams = "";
      if(typeof window.controlParams == 'object') {
          for(var key in controlParams) {
              getAppSetupParams += "&" + key + "=" + controlParams[key];
          }
      }

      config = "json!" + window.ajaxUrl + "action_route=GetAppSetup" + getAppSetupParams;
  }

  /* loading configuration */
  require([config], function(appSetup) {

      Oskari.setLang(language);

      Oskari.Application
          .create()
          .setStartupSequence(appSetup.startupSequence)
          .setConfiguration(appSetup.configuration)
          .start()
          .success(function() {
              var sb = Oskari.getSandbox();
              gfiParamHandler(sb);
          });
  });
});<|endoftext|>