<commit_before>/*
  jquery.popline.js 0.0.1

  Version: 0.0.1

  jquery.popline.js is an open source project, contribute at GitHub:
  https://github.com/kenshin54/popline.js

  (c) 2013 by kenshin54
*/

;(function($) {

  var LEFT = -2, UP = -1, RIGHT = 2, DOWN = 1, NONE = 0, ENTER = 13;

  var toggleBox = function(event) {
    if ($.popline.utils.isNull($.popline.current)) {
      return;
    }
    var isTargetOrChild = $.contains($.popline.current.target.get(0), event.target) || $.popline.current.target.get(0) === event.target;
    var isBarOrChild = $.contains($.popline.current.bar.get(0), event.target) || $.popline.current.bar.get(0) === event.target;
    if ((isTargetOrChild || isBarOrChild) && window.getSelection().toString().length > 0) {
      var target= $.popline.current.target, bar = $.popline.current.bar;
      if (bar.is(":hidden") || bar.is(":animated")) {
        bar.stop(true, true);
        var left = null, top = null;
        var rect = window.getSelection().getRangeAt(0).getBoundingClientRect();
        left = event.pageX - bar.width() / 2;
        top = event.pageY - bar.outerHeight() - parseInt(target.css('font-size')) / 2;
        $.popline.current.show({left: left, top: top});
      }
    }else {
      $.popline.hideAllBar();
    }
  };

  var targetEvent = {
    mousedown: function(event) {
      $.popline.current = $(this).data("popline");
      $.popline.hideAllBar();
    },
    keyup: function(event) {
      var popline = $(this).data("popline"), bar = popline.bar;
      if (window.getSelection().toString().length > 0) {
        var left = null, top = null;
        var rect = $.popline.getRect(), keyMoved = isKeyMove(popline.target);
        if (keyMoved === DOWN || keyMoved === RIGHT) {
          left = rect.right - bar.width() / 2;
          top = $(document).scrollTop() + rect.bottom - bar.outerHeight() - parseInt($(this).css("font-size"));
        }else if (keyMoved === UP || keyMoved === LEFT) {
          left = rect.left - bar.width() / 2;
          top = $(document).scrollTop() + rect.top - bar.outerHeight();
        }
        $.popline.current.show({left: left, top: top});
      }else {
        $.popline.current.hide();
      }
    },
    keydown: function(event) {
      var rects = window.getSelection().getRangeAt(0).getClientRects();
      if (rects.length > 0) {
        $(this).data('lastKeyPos', $.popline.boundingRect());
      }
    }
  }

  var isKeyMove = function(target) {
    var lastKeyPos = target.data('lastKeyPos');
    currentRect = $.popline.boundingRect();
    if ($.popline.utils.isNull(lastKeyPos)) {
      return null;
    }
    if (currentRect.top === lastKeyPos.top && currentRect.bottom !== lastKeyPos.bottom) {
      return DOWN;
    }
    if (currentRect.bottom === lastKeyPos.bottom && currentRect.top !== lastKeyPos.top) {
      return UP;
    }
    if (currentRect.right !== lastKeyPos.right) {
      return RIGHT;
    }
    if (currentRect.left !== lastKeyPos.left) {
      return LEFT;
    }
    return NONE;
  };

  $.fn.popline = function(options) {

    if ($.popline.utils.browser.ie) {
      return;
    }

    this.each(function() {
      if (typeof(options) === "string" && $(this).data("popline")) {
        $(this).data("popline")[options]();
      }else if (!$(this).data("popline")) {
        var popline = new $.popline(options, this);
      }
    });

    if (!$(document).data("popline-global-binded")) {
      $(document).mouseup(function(event){
        var _this = this;
        setTimeout((function(){
          toggleBox.call(_this, event);
        }), 1);
      });
      $(document).data("popline-global-binded", true);
    }
  };

  $.popline = function(options, target) {
    this.settings = $.extend(true, {}, $.popline.defaults, options);
    this.target = $(target);
    this.init();
    $.popline.addInstance(this);
  };

  $.extend($.popline, {

    defaults: {
      zIndex: 9999,
      mode: "edit"
    },

    instances: [],

    current: null,

    prototype: {
      init: function() {
        this.bar = $("

").appendTo("body");
        this.bar.data("popline", this);
        this.target.data("popline", this);
        var me = this;

        var makeButtons = function(parent, buttons) {
          for (var name in buttons) {
            var button = buttons[name];
            var mode = $.popline.utils.isNull(button.mode) ? $.popline.defaults.mode : button.mode;
            if (mode !== me.settings.mode) {
              continue;
            }
            var $button = $("

");

            $button.addClass("popline-button popline-" + name + "-button")

            if (button.iconClass) {
              $button.children(".btn").append("");
            }

            if (button.text) {
              $button.children(".btn").append("" + button.text + "");
            }

            if ($.popline.utils.isFunction(button.beforeShow)) {
              this.beforeShowCallbacks.push({name: name, callback: button.beforeShow});
            }

            if ($.popline.utils.isFunction(button.afterHide)) {
              this.afterHideCallbacks.push({name: name, callback: button.afterHide});
            }

            $button.appendTo(parent);

            if (button.buttons) {
              $subbar = $("

");
              $button.append($subbar);
              makeButtons.call(this, $subbar, button.buttons);
              $button.click(function(event) {
                var _this = this;
                if (!$(this).hasClass("boxed")) {
                  me.switchBar($(this), function() {
                    $(_this).siblings("li").hide().end()
                         .children(".btn").hide().end()
                         .children("ul").show().end()
                  });
                  event.stopPropagation();
                }
              });
            }else if($.popline.utils.isFunction(button.action)) {
              $button.click((function(button) {
                  return function(event) {
                    button.action.call(this, event, me);
                  }
                })(button)
              );
            }
            $button.mousedown(function(event) {
              if (!$(event.target).is("input")) {
                event.preventDefault();
              }
            });
            $button.mouseup(function(event) {
              event.stopPropagation();
            });
          }
        }

        makeButtons.call(this, this.bar, $.popline.buttons);

        this.target.bind(targetEvent);

        this.bar.on("mouseenter", "li", function() {
          if (!($(this).hasClass("boxed"))) {
            $(this).addClass("hover");
          }
        });
        this.bar.on("mouseleave", "li", function() {
          if (!($(this).hasClass("boxed"))) {
            $(this).removeClass("hover");
          }
        });
      },

      show: function(options) {
        for (var i = 0, l = this.beforeShowCallbacks.length; i < l; i++) {
          var obj = this.beforeShowCallbacks[i];
          var $button = this.bar.find("li.popline-" + obj.name + "-button");
          obj.callback.call($button, this);
        }
        this.bar.css('top', options.top + "px").css('left', options.left + "px").stop(true, true).fadeIn();
      },

      hide: function() {
        var _this = this;
        if (this.bar.is(":visible") && !this.bar.is(":animated")) {
          this.bar.fadeOut(function(){
            _this.bar.find("li").removeClass("boxed").show();
            _this.bar.find(".subbar").hide();
            _this.bar.find(".textfield").hide();
            _this.bar.find(".btn").show();
            for (var i = 0, l = _this.afterHideCallbacks.length; i < l; i++) {
              var obj = _this.afterHideCallbacks[i];
              var $button = _this.bar.find("li.popline-" + obj.name + "-button");
              obj.callback.call($button, _this);
            }
          });
        }
      },

      destroy: function() {
        this.target.unbind(targetEvent);
        this.target.removeData("popline");
        this.target.removeData("lastKeyPos");
        this.bar.remove();
      },

      switchBar: function(button, hideFunc, showFunc) {
        if (typeof(hideFunc) === "function") {
          var _this = this;
          var position = parseInt(_this.bar.css('left')) + _this.bar.width() / 2;
          _this.bar.animate({ opacity: 0, marginTop: -_this.bar.height() + 'px' }, function() {
            hideFunc.call(this);
            button.removeClass('hover').addClass('boxed').show();
            _this.bar.css("margin-top", _this.bar.height() + "px")
            _this.bar.css("left", position - _this.bar.width() / 2 + "px");
            if (typeof(showFunc) === "function") {
              _this.bar.animate({ opacity: 1, marginTop: 0 }, showFunc)
            }else {
              _this.bar.animate({ opacity: 1, marginTop: 0 })
            }
          });
        }
      },

      beforeShowCallbacks: [],

      afterHideCallbacks: []

    },

    hideAllBar: function() {
      for (var i = 0, l = $.popline.instances.length; i < l; i++) {
        $.popline.instances[i].hide();
      }
    },

    addInstance: function(popline){
      $.popline.instances.push(popline);
    },

    boundingRect: function(rects) {
      if ($.popline.utils.isNull(rects)) {
        rects = window.getSelection().getRangeAt(0).getClientRects();
      }
      return {
        top: parseInt(rects[0].top),
        left: parseInt(rects[0].left),
        right: parseInt(rects[rects.length -1].right),
        bottom: parseInt(rects[rects.length - 1].bottom)
      }
    },

    webkitBoundingRect: function() {
      var rects = window.getSelection().getRangeAt(0).getClientRects();
      var wbRects = [];
      for (var i = 0, l = rects.length; i < l; i++) {
        var rect = rects[i];
        if (rect.width === 0) {
          continue;
        }else if ((i === 0 || i === rects.length - 1) && rect.width === 1) {
          continue;
        }else {
          wbRects.push(rect);
        }
      }
      return $.popline.boundingRect(wbRects);
    },

    getRect: function() {
      if ($.popline.utils.browser.firefox || $.popline.utils.browser.opera) {
        return $.popline.boundingRect();
      }else if ($.popline.utils.browser.chrome || $.popline.utils.browser.safari) {
        return $.popline.webkitBoundingRect();
      }
    },

    utils: {
      isNull: function(data) {
        if (typeof(data) === "undefined" || data === null) {
          return true;
        }
        return false;
      },
      isFunction: function(func) {
        return typeof(func) === "function";
      },
      randomNumber: function() {
        return Math.floor((Math.random() * 10000000) + 1);
      },
      trim: function(string) {
        return string.replace(/^\s+|\s+$/g, '');
      },
      browser: {
        chrome: navigator.userAgent.match(/chrome/i) ? true : false,
        safari: navigator.userAgent.match(/safari/i) && !navigator.userAgent.match(/chrome/i) ? true : false,
        firefox: navigator.userAgent.match(/firefox/i) ? true : false,
        opera: navigator.userAgent.match(/opera/i) ? true : false,
        ie: navigator.userAgent.match(/msie/i) ? true : false,
        webkit: navigator.userAgent.match(/webkit/i) ? true : false
      }
    },

    addButton: function(button) {
      $.extend($.popline.buttons, button);
    },

    buttons: {}

  });
})(jQuery);
<commit_msg>Add findNodeWithTags util method.<commit_after>/*
  jquery.popline.js 0.0.1

  Version: 0.0.1

  jquery.popline.js is an open source project, contribute at GitHub:
  https://github.com/kenshin54/popline.js

  (c) 2013 by kenshin54
*/

;(function($) {

  var LEFT = -2, UP = -1, RIGHT = 2, DOWN = 1, NONE = 0, ENTER = 13;

  var toggleBox = function(event) {
    if ($.popline.utils.isNull($.popline.current)) {
      return;
    }
    var isTargetOrChild = $.contains($.popline.current.target.get(0), event.target) || $.popline.current.target.get(0) === event.target;
    var isBarOrChild = $.contains($.popline.current.bar.get(0), event.target) || $.popline.current.bar.get(0) === event.target;
    if ((isTargetOrChild || isBarOrChild) && window.getSelection().toString().length > 0) {
      var target= $.popline.current.target, bar = $.popline.current.bar;
      if (bar.is(":hidden") || bar.is(":animated")) {
        bar.stop(true, true);
        var left = null, top = null;
        var rect = window.getSelection().getRangeAt(0).getBoundingClientRect();
        left = event.pageX - bar.width() / 2;
        top = event.pageY - bar.outerHeight() - parseInt(target.css('font-size')) / 2;
        $.popline.current.show({left: left, top: top});
      }
    }else {
      $.popline.hideAllBar();
    }
  };

  var targetEvent = {
    mousedown: function(event) {
      $.popline.current = $(this).data("popline");
      $.popline.hideAllBar();
    },
    keyup: function(event) {
      var popline = $(this).data("popline"), bar = popline.bar;
      if (window.getSelection().toString().length > 0) {
        var left = null, top = null;
        var rect = $.popline.getRect(), keyMoved = isKeyMove(popline.target);
        if (keyMoved === DOWN || keyMoved === RIGHT) {
          left = rect.right - bar.width() / 2;
          top = $(document).scrollTop() + rect.bottom - bar.outerHeight() - parseInt($(this).css("font-size"));
        }else if (keyMoved === UP || keyMoved === LEFT) {
          left = rect.left - bar.width() / 2;
          top = $(document).scrollTop() + rect.top - bar.outerHeight();
        }
        $.popline.current.show({left: left, top: top});
      }else {
        $.popline.current.hide();
      }
    },
    keydown: function(event) {
      var rects = window.getSelection().getRangeAt(0).getClientRects();
      if (rects.length > 0) {
        $(this).data('lastKeyPos', $.popline.boundingRect());
      }
    }
  }

  var isKeyMove = function(target) {
    var lastKeyPos = target.data('lastKeyPos');
    currentRect = $.popline.boundingRect();
    if ($.popline.utils.isNull(lastKeyPos)) {
      return null;
    }
    if (currentRect.top === lastKeyPos.top && currentRect.bottom !== lastKeyPos.bottom) {
      return DOWN;
    }
    if (currentRect.bottom === lastKeyPos.bottom && currentRect.top !== lastKeyPos.top) {
      return UP;
    }
    if (currentRect.right !== lastKeyPos.right) {
      return RIGHT;
    }
    if (currentRect.left !== lastKeyPos.left) {
      return LEFT;
    }
    return NONE;
  };

  $.fn.popline = function(options) {

    if ($.popline.utils.browser.ie) {
      return;
    }

    this.each(function() {
      if (typeof(options) === "string" && $(this).data("popline")) {
        $(this).data("popline")[options]();
      }else if (!$(this).data("popline")) {
        var popline = new $.popline(options, this);
      }
    });

    if (!$(document).data("popline-global-binded")) {
      $(document).mouseup(function(event){
        var _this = this;
        setTimeout((function(){
          toggleBox.call(_this, event);
        }), 1);
      });
      $(document).data("popline-global-binded", true);
    }
  };

  $.popline = function(options, target) {
    this.settings = $.extend(true, {}, $.popline.defaults, options);
    this.target = $(target);
    this.init();
    $.popline.addInstance(this);
  };

  $.extend($.popline, {

    defaults: {
      zIndex: 9999,
      mode: "edit"
    },

    instances: [],

    current: null,

    prototype: {
      init: function() {
        this.bar = $("

").appendTo("body");
        this.bar.data("popline", this);
        this.target.data("popline", this);
        var me = this;

        var makeButtons = function(parent, buttons) {
          for (var name in buttons) {
            var button = buttons[name];
            var mode = $.popline.utils.isNull(button.mode) ? $.popline.defaults.mode : button.mode;
            if (mode !== me.settings.mode) {
              continue;
            }
            var $button = $("

");

            $button.addClass("popline-button popline-" + name + "-button")

            if (button.iconClass) {
              $button.children(".btn").append("");
            }

            if (button.text) {
              $button.children(".btn").append("" + button.text + "");
            }

            if ($.popline.utils.isFunction(button.beforeShow)) {
              this.beforeShowCallbacks.push({name: name, callback: button.beforeShow});
            }

            if ($.popline.utils.isFunction(button.afterHide)) {
              this.afterHideCallbacks.push({name: name, callback: button.afterHide});
            }

            $button.appendTo(parent);

            if (button.buttons) {
              $subbar = $("

");
              $button.append($subbar);
              makeButtons.call(this, $subbar, button.buttons);
              $button.click(function(event) {
                var _this = this;
                if (!$(this).hasClass("boxed")) {
                  me.switchBar($(this), function() {
                    $(_this).siblings("li").hide().end()
                         .children(".btn").hide().end()
                         .children("ul").show().end()
                  });
                  event.stopPropagation();
                }
              });
            }else if($.popline.utils.isFunction(button.action)) {
              $button.click((function(button) {
                  return function(event) {
                    button.action.call(this, event, me);
                  }
                })(button)
              );
            }
            $button.mousedown(function(event) {
              if (!$(event.target).is("input")) {
                event.preventDefault();
              }
            });
            $button.mouseup(function(event) {
              event.stopPropagation();
            });
          }
        }

        makeButtons.call(this, this.bar, $.popline.buttons);

        this.target.bind(targetEvent);

        this.bar.on("mouseenter", "li", function() {
          if (!($(this).hasClass("boxed"))) {
            $(this).addClass("hover");
          }
        });
        this.bar.on("mouseleave", "li", function() {
          if (!($(this).hasClass("boxed"))) {
            $(this).removeClass("hover");
          }
        });
      },

      show: function(options) {
        for (var i = 0, l = this.beforeShowCallbacks.length; i < l; i++) {
          var obj = this.beforeShowCallbacks[i];
          var $button = this.bar.find("li.popline-" + obj.name + "-button");
          obj.callback.call($button, this);
        }
        this.bar.css('top', options.top + "px").css('left', options.left + "px").stop(true, true).fadeIn();
      },

      hide: function() {
        var _this = this;
        if (this.bar.is(":visible") && !this.bar.is(":animated")) {
          this.bar.fadeOut(function(){
            _this.bar.find("li").removeClass("boxed").show();
            _this.bar.find(".subbar").hide();
            _this.bar.find(".textfield").hide();
            _this.bar.find(".btn").show();
            for (var i = 0, l = _this.afterHideCallbacks.length; i < l; i++) {
              var obj = _this.afterHideCallbacks[i];
              var $button = _this.bar.find("li.popline-" + obj.name + "-button");
              obj.callback.call($button, _this);
            }
          });
        }
      },

      destroy: function() {
        this.target.unbind(targetEvent);
        this.target.removeData("popline");
        this.target.removeData("lastKeyPos");
        this.bar.remove();
      },

      switchBar: function(button, hideFunc, showFunc) {
        if (typeof(hideFunc) === "function") {
          var _this = this;
          var position = parseInt(_this.bar.css('left')) + _this.bar.width() / 2;
          _this.bar.animate({ opacity: 0, marginTop: -_this.bar.height() + 'px' }, function() {
            hideFunc.call(this);
            button.removeClass('hover').addClass('boxed').show();
            _this.bar.css("margin-top", _this.bar.height() + "px")
            _this.bar.css("left", position - _this.bar.width() / 2 + "px");
            if (typeof(showFunc) === "function") {
              _this.bar.animate({ opacity: 1, marginTop: 0 }, showFunc)
            }else {
              _this.bar.animate({ opacity: 1, marginTop: 0 })
            }
          });
        }
      },

      beforeShowCallbacks: [],

      afterHideCallbacks: []

    },

    hideAllBar: function() {
      for (var i = 0, l = $.popline.instances.length; i < l; i++) {
        $.popline.instances[i].hide();
      }
    },

    addInstance: function(popline){
      $.popline.instances.push(popline);
    },

    boundingRect: function(rects) {
      if ($.popline.utils.isNull(rects)) {
        rects = window.getSelection().getRangeAt(0).getClientRects();
      }
      return {
        top: parseInt(rects[0].top),
        left: parseInt(rects[0].left),
        right: parseInt(rects[rects.length -1].right),
        bottom: parseInt(rects[rects.length - 1].bottom)
      }
    },

    webkitBoundingRect: function() {
      var rects = window.getSelection().getRangeAt(0).getClientRects();
      var wbRects = [];
      for (var i = 0, l = rects.length; i < l; i++) {
        var rect = rects[i];
        if (rect.width === 0) {
          continue;
        }else if ((i === 0 || i === rects.length - 1) && rect.width === 1) {
          continue;
        }else {
          wbRects.push(rect);
        }
      }
      return $.popline.boundingRect(wbRects);
    },

    getRect: function() {
      if ($.popline.utils.browser.firefox || $.popline.utils.browser.opera) {
        return $.popline.boundingRect();
      }else if ($.popline.utils.browser.chrome || $.popline.utils.browser.safari) {
        return $.popline.webkitBoundingRect();
      }
    },

    utils: {
      isNull: function(data) {
        if (typeof(data) === "undefined" || data === null) {
          return true;
        }
        return false;
      },
      isFunction: function(func) {
        return typeof(func) === "function";
      },
      randomNumber: function() {
        return Math.floor((Math.random() * 10000000) + 1);
      },
      trim: function(string) {
        return string.replace(/^\s+|\s+$/g, '');
      },
      browser: {
        chrome: navigator.userAgent.match(/chrome/i) ? true : false,
        safari: navigator.userAgent.match(/safari/i) && !navigator.userAgent.match(/chrome/i) ? true : false,
        firefox: navigator.userAgent.match(/firefox/i) ? true : false,
        opera: navigator.userAgent.match(/opera/i) ? true : false,
        ie: navigator.userAgent.match(/msie/i) ? true : false,
        webkit: navigator.userAgent.match(/webkit/i) ? true : false
      },
      findNodeWithTags: function(node, tags) {
        if (!$.isArray(tags)) {
          tags = [tags];
        }
        while (node) {
          if (node.nodeType !== 3) {
            var index = tags.indexOf(node.tagName);
            if (index !== -1) {
              return node;
            }
          }
          node = node.parentNode;
        }
        return null;
      }
    },

    addButton: function(button) {
      $.extend($.popline.buttons, button);
    },

    buttons: {}

  });
})(jQuery);
<|endoftext|>


<commit_before>define({
  /* Here you define static configuration pameters of your extension. */
  greeting: 'Hi there!',
  showTime: 2000 /* ms */
});
<commit_msg>Fixed typo.<commit_after>define({
  /* Here you define static configuration parameters of your extension. */
  greeting: 'Hi there!',
  showTime: 2000 /* ms */
});
<|endoftext|>


<commit_before>var config = {}

/*********************************************************
 COMMON CONFIGURATION
 It's used by Nuve, ErizoController, ErizoAgent and ErizoJS
**********************************************************/
config.rabbit = {};
config.rabbit.host = 'localhost'; //default value: 'localhost'
config.rabbit.port = 5672; //default value: 5672
// Sets the AQMP heartbeat timeout to detect dead TCP Connections
config.rabbit.heartbeat = 8; //default value: 8 seconds, 0 to disable
config.logger = {};
config.logger.configFile = '../log4js_configuration.json'; //default value: "../log4js_configuration.json"

/*********************************************************
 CLOUD PROVIDER CONFIGURATION
 It's used by Nuve and ErizoController
**********************************************************/
config.cloudProvider = {};
config.cloudProvider.name = '';

/*********************************************************
 NUVE CONFIGURATION
**********************************************************/
config.nuve = {};
config.nuve.dataBaseURL = "localhost/nuvedb"; // default value: 'localhost/nuvedb'
config.nuve.superserviceID = '_auto_generated_ID_'; // default value: ''
config.nuve.superserviceKey = '_auto_generated_KEY_'; // default value: ''
config.nuve.testErizoController = 'localhost:8080'; // default value: 'localhost:8080'
// Nuve Cloud Handler policies are in nuve/nuveAPI/ch_policies/ folder
config.nuve.cloudHandlerPolicy = 'default_policy.js'; // default value: 'default_policy.js'
config.nuve.port = 3000; // default value: 3000

/*********************************************************
 ERIZO CONTROLLER CONFIGURATION
**********************************************************/
config.erizoController = {};

// Use undefined to run clients without Ice Servers
//
// Stun servers format
// {
//     "url": url
// }
//
// Turn servers format
// {
//     "username": username,
//     "credential": password,
//     "url": url
// }
config.erizoController.iceServers = [{'url': 'stun:stun.l.google.com:19302'}]; // default value: [{'url': 'stun:stun.l.google.com:19302'}]

// Default and max video bandwidth parameters to be used by clients for both published and subscribed streams
config.erizoController.defaultVideoBW = 300; //default value: 300
config.erizoController.maxVideoBW = 300; //default value: 300

// Public erizoController IP for websockets (useful when behind NATs)
// Use '' to automatically get IP from the interface
config.erizoController.publicIP = ''; //default value: ''
config.erizoController.networkinterface = ''; //default value: ''

// This configuration is used by the clients to reach erizoController
// Use '' to use the public IP address instead of a hostname
config.erizoController.hostname = ''; //default value: ''
config.erizoController.port = 8080; //default value: 8080
// Use true if clients communicate with erizoController over SSL
config.erizoController.ssl = false; //default value: false

// This configuration is used by erizoController server to listen for connections
// Use true if erizoController listens in HTTPS.
config.erizoController.listen_ssl = false; //default value: false
config.erizoController.listen_port = 8080; //default value: 8080

// Custom location for SSL certificates. Default located in /cert
//config.erizoController.ssl_key = '/full/path/to/ssl.key';
//config.erizoController.ssl_cert = '/full/path/to/ssl.crt';
//config.erizoController.sslCaCerts = ['/full/path/to/ca_cert1.crt', '/full/path/to/ca_cert2.crt'];

// Use the name of the inferface you want to bind to for websockets
// config.erizoController.networkInterface = 'eth1' // default value: undefined

config.erizoController.exitOnNuveCheckFail = false;  // default value: false
config.erizoController.allowSinglePC = false;  // default value: false
config.erizoController.maxErizosUsedByRoom = 100;  // default value: 100

config.erizoController.warning_n_rooms = 15; // default value: 15
config.erizoController.limit_n_rooms = 20; // default value: 20
config.erizoController.interval_time_keepAlive = 1000; // default value: 1000

// Roles to be used by services
config.erizoController.roles =
{"presenter": {"publish": true, "subscribe": true, "record": true, "stats": true, "controlhandlers": true},
    "viewer": {"subscribe": true},
    "viewerWithData": {"subscribe": true, "publish": {"audio": false, "video": false, "screen": false, "data": true}}}; // default value: {"presenter":{"publish": true, "subscribe":true, "record":true}, "viewer":{"subscribe":true}, "viewerWithData":{"subscribe":true, "publish":{"audio":false,"video":false,"screen":false,"data":true}}}

// If true, erizoController sends report to rabbitMQ queue "report_handler"
config.erizoController.report = {
    session_events: false, 		// Session level events -- default value: false
    connection_events: false, 	// Connection (ICE) level events -- default value: false
    rtcp_stats: false				// RTCP stats -- default value: false
};

// Subscriptions to rtcp_stats via AMQP
config.erizoController.reportSubscriptions = {
	maxSubscriptions: 10,	// per ErizoJS -- set 0 to disable subscriptions -- default 10
	minInterval: 1, 		// in seconds -- default 1
	maxTimeout: 60			// in seconds -- default 60
};

// If undefined, the path will be /tmp/
config.erizoController.recording_path = undefined; // default value: undefined

// Erizo Controller Cloud Handler policies are in erizo_controller/erizoController/ch_policies/ folder
config.erizoController.cloudHandlerPolicy = 'default_policy.js'; // default value: 'default_policy.js'

/*********************************************************
 ERIZO AGENT CONFIGURATION
**********************************************************/
config.erizoAgent = {};

// Max processes that ErizoAgent can run
config.erizoAgent.maxProcesses 	  = 1; // default value: 1
// Number of precesses that ErizoAgent runs when it starts. Always lower than or equals to maxProcesses.
config.erizoAgent.prerunProcesses = 1; // default value: 1

// Public erizoAgent IP for ICE candidates (useful when behind NATs)
// Use '' to automatically get IP from the interface
config.erizoAgent.publicIP = ''; //default value: ''
config.erizoAgent.networkinterface = ''; //default value: ''

// Use the name of the inferface you want to bind for ICE candidates
// config.erizoAgent.networkInterface = 'eth1' // default value: undefined

//Use individual log files for each of the started erizoJS processes
//This files will be named erizo-ERIZO_ID_HASH.log
config.erizoAgent.useIndividualLogFiles = false;

// If true this Agent will launch Debug versions of ErizoJS
config.erizoAgent.launchDebugErizoJS = false;

// Custom log directory for agent instance log files.
// If useIndividualLogFiles is enabled, files will go here
// Default is [licode_path]/erizo_controller/erizoAgent
// config.erizoAgent.instanceLogDir = '/path/to/dir';

/*********************************************************
 ERIZO JS CONFIGURATION
**********************************************************/
config.erizo = {};

//Erizo Logs are piped through erizoAgent by default
//you can control log levels in [licode_path]/erizo_controller/erizoAgent/log4cxx.properties

// Number of workers that will be used to handle WebRtcConnections
config.erizo.numWorkers = 24;

// Number of workers what will be used for IO (including ICE logic)
config.erizo.numIOWorkers = 1;

// the max amount of time in days a process is allowed to be up after the first publisher is added
config.erizo.activeUptimeLimit = 7;
// the max time in hours since last publish or subscribe operation where a erizoJS process can be killed
config.erizo.maxTimeSinceLastOperation = 3;
// Interval to check uptime in seconds
config.erizo.checkUptimeInterval = 1800;

//STUN server IP address and port to be used by the server.
//if '' is used, the address is discovered locally
//Please note this is only needed if your server does not have a public IP
config.erizo.stunserver = ''; // default value: ''
config.erizo.stunport = 0; // default value: 0

//TURN server IP address and port to be used by the server.
//Please note this is not needed in most cases, setting TURN in erizoController for the clients
//is the recommended configuration
//if '' is used, no relay for the server is used
config.erizo.turnserver = ''; // default value: ''
config.erizo.turnport = 0; // default value: 0
config.erizo.turnusername = '';
config.erizo.turnpass = '';
config.erizo.networkinterface = ''; //default value: ''

//note, this won't work with all versions of libnice. With 0 all the available ports are used
config.erizo.minport = 0; // default value: 0
config.erizo.maxport = 0; // default value: 0

//Use of internal nICEr library instead of libNice.
config.erizo.useNicer = true;  // default value: true
config.erizo.useConnectionQualityCheck = true; // default value: false

config.erizo.disabledHandlers = []; // there are no handlers disabled by default

/*********************************************************
 ROV CONFIGURATION
**********************************************************/
config.rov = {};
// The stats gathering period in ms
config.rov.statsPeriod = 20000;
// The port to expose the stats to prometheus
config.rov.serverPort = 3005;
// A prefix for the prometheus stats
config.rov.statsPrefix = "licode_";

/*********************************************************
 BASIC EXAMPLE CONFIGURATION
**********************************************************/
config.basicExample = {};
config.basicExample.port = 3001;  // default value: 3001
config.basicExample.tlsPort = 3004; // default value: 3004

/***** END *****/
// Following lines are always needed.
var module = module || {};
module.exports = config;
<commit_msg>UseNicer is no longer needed (#1508)<commit_after>var config = {}

/*********************************************************
 COMMON CONFIGURATION
 It's used by Nuve, ErizoController, ErizoAgent and ErizoJS
**********************************************************/
config.rabbit = {};
config.rabbit.host = 'localhost'; //default value: 'localhost'
config.rabbit.port = 5672; //default value: 5672
// Sets the AQMP heartbeat timeout to detect dead TCP Connections
config.rabbit.heartbeat = 8; //default value: 8 seconds, 0 to disable
config.logger = {};
config.logger.configFile = '../log4js_configuration.json'; //default value: "../log4js_configuration.json"

/*********************************************************
 CLOUD PROVIDER CONFIGURATION
 It's used by Nuve and ErizoController
**********************************************************/
config.cloudProvider = {};
config.cloudProvider.name = '';

/*********************************************************
 NUVE CONFIGURATION
**********************************************************/
config.nuve = {};
config.nuve.dataBaseURL = "localhost/nuvedb"; // default value: 'localhost/nuvedb'
config.nuve.superserviceID = '_auto_generated_ID_'; // default value: ''
config.nuve.superserviceKey = '_auto_generated_KEY_'; // default value: ''
config.nuve.testErizoController = 'localhost:8080'; // default value: 'localhost:8080'
// Nuve Cloud Handler policies are in nuve/nuveAPI/ch_policies/ folder
config.nuve.cloudHandlerPolicy = 'default_policy.js'; // default value: 'default_policy.js'
config.nuve.port = 3000; // default value: 3000

/*********************************************************
 ERIZO CONTROLLER CONFIGURATION
**********************************************************/
config.erizoController = {};

// Use undefined to run clients without Ice Servers
//
// Stun servers format
// {
//     "url": url
// }
//
// Turn servers format
// {
//     "username": username,
//     "credential": password,
//     "url": url
// }
config.erizoController.iceServers = [{'url': 'stun:stun.l.google.com:19302'}]; // default value: [{'url': 'stun:stun.l.google.com:19302'}]

// Default and max video bandwidth parameters to be used by clients for both published and subscribed streams
config.erizoController.defaultVideoBW = 300; //default value: 300
config.erizoController.maxVideoBW = 300; //default value: 300

// Public erizoController IP for websockets (useful when behind NATs)
// Use '' to automatically get IP from the interface
config.erizoController.publicIP = ''; //default value: ''
config.erizoController.networkinterface = ''; //default value: ''

// This configuration is used by the clients to reach erizoController
// Use '' to use the public IP address instead of a hostname
config.erizoController.hostname = ''; //default value: ''
config.erizoController.port = 8080; //default value: 8080
// Use true if clients communicate with erizoController over SSL
config.erizoController.ssl = false; //default value: false

// This configuration is used by erizoController server to listen for connections
// Use true if erizoController listens in HTTPS.
config.erizoController.listen_ssl = false; //default value: false
config.erizoController.listen_port = 8080; //default value: 8080

// Custom location for SSL certificates. Default located in /cert
//config.erizoController.ssl_key = '/full/path/to/ssl.key';
//config.erizoController.ssl_cert = '/full/path/to/ssl.crt';
//config.erizoController.sslCaCerts = ['/full/path/to/ca_cert1.crt', '/full/path/to/ca_cert2.crt'];

// Use the name of the inferface you want to bind to for websockets
// config.erizoController.networkInterface = 'eth1' // default value: undefined

config.erizoController.exitOnNuveCheckFail = false;  // default value: false
config.erizoController.allowSinglePC = false;  // default value: false
config.erizoController.maxErizosUsedByRoom = 100;  // default value: 100

config.erizoController.warning_n_rooms = 15; // default value: 15
config.erizoController.limit_n_rooms = 20; // default value: 20
config.erizoController.interval_time_keepAlive = 1000; // default value: 1000

// Roles to be used by services
config.erizoController.roles =
{"presenter": {"publish": true, "subscribe": true, "record": true, "stats": true, "controlhandlers": true},
    "viewer": {"subscribe": true},
    "viewerWithData": {"subscribe": true, "publish": {"audio": false, "video": false, "screen": false, "data": true}}}; // default value: {"presenter":{"publish": true, "subscribe":true, "record":true}, "viewer":{"subscribe":true}, "viewerWithData":{"subscribe":true, "publish":{"audio":false,"video":false,"screen":false,"data":true}}}

// If true, erizoController sends report to rabbitMQ queue "report_handler"
config.erizoController.report = {
    session_events: false, 		// Session level events -- default value: false
    connection_events: false, 	// Connection (ICE) level events -- default value: false
    rtcp_stats: false				// RTCP stats -- default value: false
};

// Subscriptions to rtcp_stats via AMQP
config.erizoController.reportSubscriptions = {
	maxSubscriptions: 10,	// per ErizoJS -- set 0 to disable subscriptions -- default 10
	minInterval: 1, 		// in seconds -- default 1
	maxTimeout: 60			// in seconds -- default 60
};

// If undefined, the path will be /tmp/
config.erizoController.recording_path = undefined; // default value: undefined

// Erizo Controller Cloud Handler policies are in erizo_controller/erizoController/ch_policies/ folder
config.erizoController.cloudHandlerPolicy = 'default_policy.js'; // default value: 'default_policy.js'

/*********************************************************
 ERIZO AGENT CONFIGURATION
**********************************************************/
config.erizoAgent = {};

// Max processes that ErizoAgent can run
config.erizoAgent.maxProcesses 	  = 1; // default value: 1
// Number of precesses that ErizoAgent runs when it starts. Always lower than or equals to maxProcesses.
config.erizoAgent.prerunProcesses = 1; // default value: 1

// Public erizoAgent IP for ICE candidates (useful when behind NATs)
// Use '' to automatically get IP from the interface
config.erizoAgent.publicIP = ''; //default value: ''
config.erizoAgent.networkinterface = ''; //default value: ''

// Use the name of the inferface you want to bind for ICE candidates
// config.erizoAgent.networkInterface = 'eth1' // default value: undefined

//Use individual log files for each of the started erizoJS processes
//This files will be named erizo-ERIZO_ID_HASH.log
config.erizoAgent.useIndividualLogFiles = false;

// If true this Agent will launch Debug versions of ErizoJS
config.erizoAgent.launchDebugErizoJS = false;

// Custom log directory for agent instance log files.
// If useIndividualLogFiles is enabled, files will go here
// Default is [licode_path]/erizo_controller/erizoAgent
// config.erizoAgent.instanceLogDir = '/path/to/dir';

/*********************************************************
 ERIZO JS CONFIGURATION
**********************************************************/
config.erizo = {};

//Erizo Logs are piped through erizoAgent by default
//you can control log levels in [licode_path]/erizo_controller/erizoAgent/log4cxx.properties

// Number of workers that will be used to handle WebRtcConnections
config.erizo.numWorkers = 24;

// Number of workers what will be used for IO (including ICE logic)
config.erizo.numIOWorkers = 1;

// the max amount of time in days a process is allowed to be up after the first publisher is added
config.erizo.activeUptimeLimit = 7;
// the max time in hours since last publish or subscribe operation where a erizoJS process can be killed
config.erizo.maxTimeSinceLastOperation = 3;
// Interval to check uptime in seconds
config.erizo.checkUptimeInterval = 1800;

//STUN server IP address and port to be used by the server.
//if '' is used, the address is discovered locally
//Please note this is only needed if your server does not have a public IP
config.erizo.stunserver = ''; // default value: ''
config.erizo.stunport = 0; // default value: 0

//TURN server IP address and port to be used by the server.
//Please note this is not needed in most cases, setting TURN in erizoController for the clients
//is the recommended configuration
//if '' is used, no relay for the server is used
config.erizo.turnserver = ''; // default value: ''
config.erizo.turnport = 0; // default value: 0
config.erizo.turnusername = '';
config.erizo.turnpass = '';
config.erizo.networkinterface = ''; //default value: ''

//note, this won't work with all versions of libnice. With 0 all the available ports are used
config.erizo.minport = 0; // default value: 0
config.erizo.maxport = 0; // default value: 0

config.erizo.useConnectionQualityCheck = true; // default value: false

config.erizo.disabledHandlers = []; // there are no handlers disabled by default

/*********************************************************
 ROV CONFIGURATION
**********************************************************/
config.rov = {};
// The stats gathering period in ms
config.rov.statsPeriod = 20000;
// The port to expose the stats to prometheus
config.rov.serverPort = 3005;
// A prefix for the prometheus stats
config.rov.statsPrefix = "licode_";

/*********************************************************
 BASIC EXAMPLE CONFIGURATION
**********************************************************/
config.basicExample = {};
config.basicExample.port = 3001;  // default value: 3001
config.basicExample.tlsPort = 3004; // default value: 3004

/***** END *****/
// Following lines are always needed.
var module = module || {};
module.exports = config;
<|endoftext|>


<commit_before>

window.regionScoreboard = function() {
  // TODO: rather than just load the region scores for the center of the map, display a list of regions in the current view
  // and let the user select one (with automatic selection when just one region, and limited to close enough zooms so list size is reasonable)
  var latLng = map.getCenter();

  var latE6 = Math.round(latLng.lat*1E6);
  var lngE6 = Math.round(latLng.lng*1E6);

  var dlg = dialog({title:'Region scores',html:'Loading regional scores...',width:450,minHeight:370});

  window.postAjax('getRegionScoreDetails', {latE6:latE6,lngE6:lngE6}, function(res){regionScoreboardSuccess(res,dlg);}, function(){regionScoreboardFailure(dlg);});
}

function regionScoreboardFailure(dlg) {
  dlg.html('Failed to load region scores - try again');
}

function regionScoreboardScoreHistoryChart(result) {
  // svg area 400x130. graph area 350x100, offset to 40,10

  var max = Math.max(result.gameScore[0],result.gameScore[1],10); //NOTE: ensure a min of 10 for the graph
  var items = []; //we'll copy the items to an array indexed by checkpoint number - easier to access!
  for (var i=0; i<result.scoreHistory.length; i++) {
    max = Math.max(max, result.scoreHistory[i][1], result.scoreHistory[i][2]); //note: index 0 is the checkpoint number here
    items[result.scoreHistory[i][0]] = [result.scoreHistory[i][1], result.scoreHistory[i][2]];
  }

  // scale up maximum a little, so graph isn't squashed right against upper edge
  max *= 1.09;

  var scale = function(y) { return 110-y/max*100; };

  var teamPaths = [[],[]];
  var otherSvg = [];

  for (var i=0; i<items.length; i++) {
    var x=i*10+40;
    if (items[i] !== undefined) {
      // paths
      if (i>0 && items[i-1] !== undefined) {
        for (var t=0; t<2; t++) {
          teamPaths[t].push('M'+(x-10)+','+scale(items[i-1][t])+' L'+x+','+scale(items[i][t]));
        }
      }
      // markers
      otherSvg.push('<g title="test" class="checkpoint" data-cp="'+i+'" data-enl="'+items[i][0]+'" data-res="'+items[i][1]+'">');
      otherSvg.push('<rect x="'+(i*10+35)+'" y="10" width="10" height="100" fill="black" fill-opacity="0" />');
      for (var t=0; t<2; t++) {
        var col = t==0 ? COLORS[TEAM_ENL] : COLORS[TEAM_RES];
        otherSvg.push('<circle cx="'+x+'" cy="'+scale(items[i][t])+'" r="3" stroke-width="1" stroke="'+col+'" fill="'+col+'" fill-opacity="0.5" />');
      }
      otherSvg.push('</g>');
    }
  }

  var paths = '<path d="M40,110 L40,10 M40,110 L390,110" stroke="#fff" />';

  // graph tickmarks - horizontal
  var ticks = [];
  for (var i=5; i<=35; i+=5) {
    var x=i*10+40;
    ticks.push('M'+x+',10 L'+x+',110');
    otherSvg.push('<text x="'+x+'" y="125" font-size="12" font-family="Roboto, Helvetica, sans-serif" text-anchor="middle" fill="#fff">'+i+'</text>');
  }

  // vertical
  // first we calculate the power of 10 that is smaller than the max limit
  var vtickStep = Math.pow(10,Math.floor(Math.log(max)/Math.log(10)));
  // this could be between 1 and 10 grid lines - so we adjust to give nicer spacings
  if (vtickStep < (max/5)) {
    vtickStep *= 2;
  } else if (vtickStep > (max/2)) {
    vtickStep /= 2;
  }
  for (var i=vtickStep; i<=max; i+=vtickStep) {
    var y = scale(i);

    ticks.push('M40,'+y+' L390,'+y);

    var istr = i>=1000000000 ? i/1000000000+'B' : i>=1000000 ? i/1000000+'M' : i>=1000 ? i/1000+'k' : i;
    otherSvg.push('<text x="35" y="'+y+'" font-size="12" font-family="Roboto, Helvetica, sans-serif" text-anchor="end" fill="#fff">'+istr+'</text>');
  }

  paths += '<path d="'+ticks.join(' ')+'" stroke="#fff" opacity="0.3" />;'

  for (var t=0; t<2; t++) {
    var col = t==0 ? COLORS[TEAM_ENL] : COLORS[TEAM_RES];
    if (teamPaths[t].length > 0) {
      paths += '<path d="'+teamPaths[t].join(' ')+'" stroke="'+col+'" />';
    }

    var y = scale(result.gameScore[t]);
    paths += '<path d="M40,'+y+' L390,'+y+'" stroke="'+col+'" stroke-dasharray="3,2" opacity="0.8" />';
  }

  var svg = '
<svg width="400" height="130">'
           +'<rect x="0" y="0" width="400" height="130" stroke="#FFCE00" fill="#08304E" />'
           +paths
           +otherSvg.join('')
           +'</svg>
';

  return svg;
}

function regionScoreboardSuccess(data,dlg) {
  if (data.result === undefined) {
    return regionScoreboardFailure(dlg);
  }

  var agentTable = '

#	Agent
';
  for (var i=0; i<data.result.topAgents.length; i++) {
    var agent = data.result.topAgents[i];
    agentTable += '
'+(i+1)+'	'+agent.nick+'
';
  }
  if (data.result.topAgents.length==0) {
    agentTable += '
no top agents
';
  }
  agentTable += '
';

  var maxAverage = Math.max(data.result.gameScore[0], data.result.gameScore[1], 1);
  var teamRow = [];
  for (var t=0; t<2; t++) {
    var team = t==0 ? 'Enlightened' : 'Resistance';
    var teamClass = t==0 ? 'enl' : 'res';
    var teamCol = t==0 ? COLORS[TEAM_ENL] : COLORS[TEAM_RES];
    var barSize = Math.round(data.result.gameScore[t]/maxAverage*200);
    teamRow[t] = '
'+team+'	'+digits(data.result.gameScore[t])+'

';

  }

  var first = PLAYER.team == 'RESISTANCE' ? 1 : 0;

  dlg.html('Region scores for '+data.result.regionName+''
         +'
'+teamRow[first]+teamRow[1-first]+'
'
         +'Checkpoint history'
         +regionScoreboardScoreHistoryChart(data.result)
         +'Top agents'
         +agentTable);

  $('g.checkpoint', dlg).each(function(i, elem) {
    elem = $(elem);
    console.log(elem.children()[0]);

    var tooltip = 'CP:\t'+elem.attr('data-cp')
      + '\nEnl:\t' + digits(elem.attr('data-enl'))
      + '\nRes:\t' + digits(elem.attr('data-res'));
    console.log(tooltip);
    elem.tooltip({
      content: convertTextToTableMagic(tooltip),
      position: {my: "center bottom", at: "center top-10"}
    });
  });
}
<commit_msg>Clickable names in Top 3<commit_after>

window.regionScoreboard = function() {
  // TODO: rather than just load the region scores for the center of the map, display a list of regions in the current view
  // and let the user select one (with automatic selection when just one region, and limited to close enough zooms so list size is reasonable)
  var latLng = map.getCenter();

  var latE6 = Math.round(latLng.lat*1E6);
  var lngE6 = Math.round(latLng.lng*1E6);

  var dlg = dialog({title:'Region scores',html:'Loading regional scores...',width:450,minHeight:370});

  window.postAjax('getRegionScoreDetails', {latE6:latE6,lngE6:lngE6}, function(res){regionScoreboardSuccess(res,dlg);}, function(){regionScoreboardFailure(dlg);});
}

function regionScoreboardFailure(dlg) {
  dlg.html('Failed to load region scores - try again');
}

function regionScoreboardScoreHistoryChart(result) {
  // svg area 400x130. graph area 350x100, offset to 40,10

  var max = Math.max(result.gameScore[0],result.gameScore[1],10); //NOTE: ensure a min of 10 for the graph
  var items = []; //we'll copy the items to an array indexed by checkpoint number - easier to access!
  for (var i=0; i<result.scoreHistory.length; i++) {
    max = Math.max(max, result.scoreHistory[i][1], result.scoreHistory[i][2]); //note: index 0 is the checkpoint number here
    items[result.scoreHistory[i][0]] = [result.scoreHistory[i][1], result.scoreHistory[i][2]];
  }

  // scale up maximum a little, so graph isn't squashed right against upper edge
  max *= 1.09;

  var scale = function(y) { return 110-y/max*100; };

  var teamPaths = [[],[]];
  var otherSvg = [];

  for (var i=0; i<items.length; i++) {
    var x=i*10+40;
    if (items[i] !== undefined) {
      // paths
      if (i>0 && items[i-1] !== undefined) {
        for (var t=0; t<2; t++) {
          teamPaths[t].push('M'+(x-10)+','+scale(items[i-1][t])+' L'+x+','+scale(items[i][t]));
        }
      }
      // markers
      otherSvg.push('<g title="test" class="checkpoint" data-cp="'+i+'" data-enl="'+items[i][0]+'" data-res="'+items[i][1]+'">');
      otherSvg.push('<rect x="'+(i*10+35)+'" y="10" width="10" height="100" fill="black" fill-opacity="0" />');
      for (var t=0; t<2; t++) {
        var col = t==0 ? COLORS[TEAM_ENL] : COLORS[TEAM_RES];
        otherSvg.push('<circle cx="'+x+'" cy="'+scale(items[i][t])+'" r="3" stroke-width="1" stroke="'+col+'" fill="'+col+'" fill-opacity="0.5" />');
      }
      otherSvg.push('</g>');
    }
  }

  var paths = '<path d="M40,110 L40,10 M40,110 L390,110" stroke="#fff" />';

  // graph tickmarks - horizontal
  var ticks = [];
  for (var i=5; i<=35; i+=5) {
    var x=i*10+40;
    ticks.push('M'+x+',10 L'+x+',110');
    otherSvg.push('<text x="'+x+'" y="125" font-size="12" font-family="Roboto, Helvetica, sans-serif" text-anchor="middle" fill="#fff">'+i+'</text>');
  }

  // vertical
  // first we calculate the power of 10 that is smaller than the max limit
  var vtickStep = Math.pow(10,Math.floor(Math.log(max)/Math.log(10)));
  // this could be between 1 and 10 grid lines - so we adjust to give nicer spacings
  if (vtickStep < (max/5)) {
    vtickStep *= 2;
  } else if (vtickStep > (max/2)) {
    vtickStep /= 2;
  }
  for (var i=vtickStep; i<=max; i+=vtickStep) {
    var y = scale(i);

    ticks.push('M40,'+y+' L390,'+y);

    var istr = i>=1000000000 ? i/1000000000+'B' : i>=1000000 ? i/1000000+'M' : i>=1000 ? i/1000+'k' : i;
    otherSvg.push('<text x="35" y="'+y+'" font-size="12" font-family="Roboto, Helvetica, sans-serif" text-anchor="end" fill="#fff">'+istr+'</text>');
  }

  paths += '<path d="'+ticks.join(' ')+'" stroke="#fff" opacity="0.3" />;'

  for (var t=0; t<2; t++) {
    var col = t==0 ? COLORS[TEAM_ENL] : COLORS[TEAM_RES];
    if (teamPaths[t].length > 0) {
      paths += '<path d="'+teamPaths[t].join(' ')+'" stroke="'+col+'" />';
    }

    var y = scale(result.gameScore[t]);
    paths += '<path d="M40,'+y+' L390,'+y+'" stroke="'+col+'" stroke-dasharray="3,2" opacity="0.8" />';
  }

  var svg = '
<svg width="400" height="130">'
           +'<rect x="0" y="0" width="400" height="130" stroke="#FFCE00" fill="#08304E" />'
           +paths
           +otherSvg.join('')
           +'</svg>
';

  return svg;
}

function regionScoreboardSuccess(data,dlg) {
  if (data.result === undefined) {
    return regionScoreboardFailure(dlg);
  }

  var agentTable = '

#	Agent
';
  for (var i=0; i<data.result.topAgents.length; i++) {
    var agent = data.result.topAgents[i];
    agentTable += '
'+(i+1)+'	'+agent.nick+'
';
  }
  if (data.result.topAgents.length==0) {
    agentTable += '
no top agents
';
  }
  agentTable += '
';

  var maxAverage = Math.max(data.result.gameScore[0], data.result.gameScore[1], 1);
  var teamRow = [];
  for (var t=0; t<2; t++) {
    var team = t==0 ? 'Enlightened' : 'Resistance';
    var teamClass = t==0 ? 'enl' : 'res';
    var teamCol = t==0 ? COLORS[TEAM_ENL] : COLORS[TEAM_RES];
    var barSize = Math.round(data.result.gameScore[t]/maxAverage*200);
    teamRow[t] = '
'+team+'	'+digits(data.result.gameScore[t])+'

';

  }

  var first = PLAYER.team == 'RESISTANCE' ? 1 : 0;

  dlg.html('Region scores for '+data.result.regionName+''
         +'
'+teamRow[first]+teamRow[1-first]+'
'
         +'Checkpoint history'
         +regionScoreboardScoreHistoryChart(data.result)
         +'Top agents'
         +agentTable);

  $('g.checkpoint', dlg).each(function(i, elem) {
    elem = $(elem);
    console.log(elem.children()[0]);

    var tooltip = 'CP:\t'+elem.attr('data-cp')
      + '\nEnl:\t' + digits(elem.attr('data-enl'))
      + '\nRes:\t' + digits(elem.attr('data-res'));
    console.log(tooltip);
    elem.tooltip({
      content: convertTextToTableMagic(tooltip),
      position: {my: "center bottom", at: "center top-10"}
    });
  });
}
<|endoftext|>


<commit_before>/* global LocusZoom,Q */
/* eslint-env browser */
/* eslint-disable no-console */

"use strict";

LocusZoom.Data = LocusZoom.Data ||  {};

LocusZoom.Data.Requester = function(sources) {

    function split_requests(fields) {
        var requests = {};
        fields.forEach(function(field) {
            var parts = field.split(/\:(.*)/);
            if (parts.length==1) {
                if (typeof requests["base"] == "undefined") {
                    requests.base = {names:[], fields:[]};
                }
                requests.base.names.push(field);
                requests.base.fields.push(field);
            } else {
                if (typeof requests[parts[0]] =="undefined") {
                    requests[parts[0]] = {names:[], fields:[]};
                }
                requests[parts[0]].names.push(field);
                requests[parts[0]].fields.push(parts[1]);
            }
        });
        return requests;
    }

    this.getData = function(state, fields) {
        var requests = split_requests(fields);
        var promises = Object.keys(requests).map(function(key) {
            return sources[key].getData(state, requests[key].fields, requests[key].names);
        });
        //assume the are requested in dependent order
        //TODO: better manage dependencies
        var ret = Q.when({});
        for(var i=0; i < promises.length; i++) {
            ret = ret.then(promises[i]);
        }
        return ret;
    };
};

LocusZoom.Data.AssociationSource = function(url) {
    this.url = url;

    this.getData = function(state, fields, outnames) {
        ["id","position"].forEach(function(x) {
            if (fields.indexOf(x)==-1) {
                fields.unshift(x);
                outnames.unshift(x);
            }
        });
        return function (chain) {
            var requrl = url + "results/?filter=analysis in 1 " +
                "and chromosome in  '" + state.chr + "'" +
                " and position ge " + state.start +
                " and position le " + state.end;
            return LocusZoom.createCORSPromise("GET",requrl).then(function(x) {
                var records = [];
                fields.forEach(function(f) {
                    if (!(f in x)) {throw "field " + f + " not found in response";}
                });
                for(var i = 0; i < x.position.length; i++) {
                    var record = {};
                    for(var j=0; j<fields.length; j++) {
                        record[outnames[j]] = x[fields[j]][i];
                    }
                    records.push(record);
                }
                var res = {header: chain.header || {}, body: records};
                return res;
            });
        };
    };
};

LocusZoom.Data.LDSource = function(url) {
    this.url = url;

    var findSmallestPvalue = function(x, pval) {
        pval = pval || "pvalue";
        var smVal = x[0][pval], smIdx=0;
        for(var i=1; i<x.length; i++) {
            if (x[i][pval] < smVal) {
                smVal = x[i][pval];
                smIdx = i;
            }
        }
        return smIdx;
    };

    var leftJoin  = function(left, right, lfield, rfield) {
        var i=0, j=0;
        while (i < left.length && j < right.position2.length) {
            if (left[i].position == right.position2[j]) {
                left[i][lfield] = right[rfield][j];
                i++;
                j++;
            } else if (left[i].position < right.position2[j]) {
                i++;
            } else {
                j++;
            }
        }
    };

    this.getData = function(state, fields, outnames) {
        if (fields.length>1) {
            throw("LD currently only supports one field");
        }
        return function (chain) {
            var refVar = state.ldrefvar || chain.header.ldrefvar;
            if (!refVar) {
                refVar = chain.body[findSmallestPvalue(chain.body)].id;
            }
            var requrl = url + "results/?filter=reference eq 2" +
                " and chromosome2 eq '" + state.chr + "'" +
                " and position2 ge " + state.start +
                " and position2 le " + state.end +
                " and variant1 eq '" + refVar + "'" +
                "&fields=chr,pos,rsquare";
            return LocusZoom.createCORSPromise("GET",requrl).then(function(x) {
                chain.header.ldrefvar = refVar;
                leftJoin(chain.body, x, outnames[0], "rsquare");
                return chain;
            });
        };
    };
};

LocusZoom.Data.GeneSource = function(url) {
    this.url = url;

    this.getData = function(state, fields, outnames) {
        return function (chain) {
            var requrl = url + "?filter=source in 1" +
                " and chrom eq '" + state.chr + "'" +
                " and start ge " + state.start +
                " and end le " + state.end;
            return LocusZoom.createCORSPromise("GET",requrl).then(function(x) {
                return {header: chain.header, body: x.data};
            }, function(err) {
                console.log(err);
            });
        };
    };
};

LocusZoom.createResolvedPromise = function() {
    var response = Q.defer();
    response.resolve(Array.prototype.slice.call(arguments));
    return response.promise;
};

LocusZoom.DefaultDataSources = {
    base: new LocusZoom.Data.AssociationSource("/api/v1/single/"),
    ld: new LocusZoom.Data.LDSource("/api/v1/pair/LD/"),
    gene: new LocusZoom.Data.GeneSource("/api/v1/annotation/genes/")
};

var lzd = new LocusZoom.Data.Requester(LocusZoom.DefaultDataSources);
<commit_msg>Allow for different LD ref snp via field<commit_after>/* global LocusZoom,Q */
/* eslint-env browser */
/* eslint-disable no-console */

"use strict";

LocusZoom.Data = LocusZoom.Data ||  {};

LocusZoom.Data.Requester = function(sources) {

    function split_requests(fields) {
        var requests = {};
        fields.forEach(function(field) {
            var parts = field.split(/\:(.*)/);
            if (parts.length==1) {
                if (typeof requests["base"] == "undefined") {
                    requests.base = {names:[], fields:[]};
                }
                requests.base.names.push(field);
                requests.base.fields.push(field);
            } else {
                if (typeof requests[parts[0]] =="undefined") {
                    requests[parts[0]] = {names:[], fields:[]};
                }
                requests[parts[0]].names.push(field);
                requests[parts[0]].fields.push(parts[1]);
            }
        });
        return requests;
    }

    this.getData = function(state, fields) {
        var requests = split_requests(fields);
        var promises = Object.keys(requests).map(function(key) {
            return sources[key].getData(state, requests[key].fields, requests[key].names);
        });
        //assume the are requested in dependent order
        //TODO: better manage dependencies
        var ret = Q.when({});
        for(var i=0; i < promises.length; i++) {
            ret = ret.then(promises[i]);
        }
        return ret;
    };
};

LocusZoom.Data.AssociationSource = function(url) {
    this.url = url;

    this.getData = function(state, fields, outnames) {
        ["id","position"].forEach(function(x) {
            if (fields.indexOf(x)==-1) {
                fields.unshift(x);
                outnames.unshift(x);
            }
        });
        return function (chain) {
            var requrl = url + "results/?filter=analysis in 1 " +
                "and chromosome in  '" + state.chr + "'" +
                " and position ge " + state.start +
                " and position le " + state.end;
            return LocusZoom.createCORSPromise("GET",requrl).then(function(x) {
                var records = [];
                fields.forEach(function(f) {
                    if (!(f in x)) {throw "field " + f + " not found in response";}
                });
                for(var i = 0; i < x.position.length; i++) {
                    var record = {};
                    for(var j=0; j<fields.length; j++) {
                        record[outnames[j]] = x[fields[j]][i];
                    }
                    records.push(record);
                }
                var res = {header: chain.header || {}, body: records};
                return res;
            });
        };
    };
};

LocusZoom.Data.LDSource = function(url) {
    this.url = url;

    var findSmallestPvalue = function(x, pval) {
        pval = pval || "pvalue";
        var smVal = x[0][pval], smIdx=0;
        for(var i=1; i<x.length; i++) {
            if (x[i][pval] < smVal) {
                smVal = x[i][pval];
                smIdx = i;
            }
        }
        return smIdx;
    };

    var leftJoin  = function(left, right, lfield, rfield) {
        var i=0, j=0;
        while (i < left.length && j < right.position2.length) {
            if (left[i].position == right.position2[j]) {
                left[i][lfield] = right[rfield][j];
                i++;
                j++;
            } else if (left[i].position < right.position2[j]) {
                i++;
            } else {
                j++;
            }
        }
    };

    this.getData = function(state, fields, outnames) {
        if (fields.length>1) {
            throw("LD currently only supports one field");
        }
        return function (chain) {
            var refVar = fields[0];
            if (refVar == "state") {
                refVar = state.ldrefvar || chain.header.ldrefvar || "best";
            }
            if ( refVar=="best") {
                refVar = chain.body[findSmallestPvalue(chain.body)].id;
            }
            var requrl = url + "results/?filter=reference eq 2" +
                " and chromosome2 eq '" + state.chr + "'" +
                " and position2 ge " + state.start +
                " and position2 le " + state.end +
                " and variant1 eq '" + refVar + "'" +
                "&fields=chr,pos,rsquare";
            return LocusZoom.createCORSPromise("GET",requrl).then(function(x) {
                chain.header.ldrefvar = refVar;
                leftJoin(chain.body, x, outnames[0], "rsquare");
                return chain;
            });
        };
    };
};

LocusZoom.Data.GeneSource = function(url) {
    this.url = url;

    this.getData = function(state, fields, outnames) {
        return function (chain) {
            var requrl = url + "?filter=source in 1" +
                " and chrom eq '" + state.chr + "'" +
                " and start ge " + state.start +
                " and end le " + state.end;
            return LocusZoom.createCORSPromise("GET",requrl).then(function(x) {
                return {header: chain.header, body: x.data};
            }, function(err) {
                console.log(err);
            });
        };
    };
};

LocusZoom.createResolvedPromise = function() {
    var response = Q.defer();
    response.resolve(Array.prototype.slice.call(arguments));
    return response.promise;
};

LocusZoom.DefaultDataSources = {
    base: new LocusZoom.Data.AssociationSource("/api/v1/single/"),
    ld: new LocusZoom.Data.LDSource("/api/v1/pair/LD/"),
    gene: new LocusZoom.Data.GeneSource("/api/v1/annotation/genes/")
};

var lzd = new LocusZoom.Data.Requester(LocusZoom.DefaultDataSources);
<|endoftext|>


<commit_before>/**
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * 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.
 */
'use strict';

// A Sticky Notes app.
class StickyNotesApp {

  // Initializes the Sticky Notes app.
  constructor() {
    // Shortcuts to DOM Elements.
    this.notesContainer = document.getElementById('notes-container');
    this.noteMessageInput = document.getElementById('message');
    this.addNoteButton = document.getElementById('save');
    this.notesSectionTitle = document.getElementById('notes-section-title');

    // Saves notes on button click.
    this.addNoteButton.addEventListener('click', () => this.saveNote());

    // Toggle for the button.
    this.noteMessageInput.addEventListener('keyup', () => this.toggleButton());

    // Loads all the notes.
    for (let key in localStorage) {
      this.displayNote(key, localStorage[key]);
    }
    // Listen for updates to notes from other windows.
    window.addEventListener('storage', e => this.displayNote(e.key, e.newValue));
  }

  // Saves a new sticky note on localStorage.
  saveNote() {
    if (this.noteMessageInput.value) {
      let key = Date.now().toString();
      localStorage.setItem(key, this.noteMessageInput.value);
      this.displayNote(key, this.noteMessageInput.value);
      StickyNotesApp.resetMaterialTextfield(this.noteMessageInput);
      this.toggleButton();
    }
  }

  // Resets the given MaterialTextField.
  static resetMaterialTextfield(element) {
    element.value = '';
    element.parentNode.MaterialTextfield.boundUpdateClassesHandler();
    element.blur();
  }

  // Creates/updates/deletes a note in the UI.
  displayNote(key, message) {
    let note = document.getElementById(key);
    // If no element with the given key exists we create a new note.
    if (!note) {
      note = document.createElement('sticky-note');
      note.id = key;
      this.notesContainer.insertBefore(note, this.notesSectionTitle.nextSibling);
    }
    // If the message is null we delete the note.
    if (!message) {
      return note.deleteNote();
    }
    note.setMessage(message);
  }

  // Enables or disables the submit button depending on the values of the input field.
  toggleButton() {
    if (this.noteMessageInput.value) {
      this.addNoteButton.removeAttribute('disabled');
    } else {
      this.addNoteButton.setAttribute('disabled', 'true');
    }
  }
}

// On load start the app.
window.addEventListener('load', () => new StickyNotesApp());

// This is a Sticky Note custom element.
class StickyNote extends HTMLElement {

  // Fires when an instance of the element is created.
  createdCallback() {
    this.classList.add(...StickyNote.CLASSES);
    this.innerHTML = StickyNote.TEMPLATE;
    this.messageElement = this.querySelector('.message');
    this.dateElement = this.querySelector('.date');
    this.deleteButton = this.querySelector('.delete');
    this.deleteButton.addEventListener('click', () => this.deleteNote());
  }

  // Fires when an attribute of the element is added/deleted/modified.
  attributeChangedCallback(attributeName) {
    // We display/update the created date message if the id changes.
    if (attributeName == 'id') {
      let date;
      if (this.id) {
        date = new Date(parseInt(this.id));
      } else {
        date = new Date();
      }

      // Format the date
      let dateFormatterOptions = {day: 'numeric', month: 'short'};
      let shortDate = new Intl.DateTimeFormat("en-US", dateFormatterOptions).format(date);

      this.dateElement.textContent = `Created on ${shortDate}`;
    }
  }

  // Sets the message of the note.
  setMessage(message) {
    this.messageElement.textContent = message;
    // Replace all line breaks by
.
    this.messageElement.innerHTML = this.messageElement.innerHTML.replace(/\n/g, '
');
  }

  // Deletes the note by removing the element from the DOM and the data from localStorage.
  deleteNote() {
    localStorage.removeItem(this.id);
    this.parentNode.removeChild(this);
  }
}

// Initial content of the element.
StickyNote.TEMPLATE = `

    <button class="delete mdl-button mdl-js-button mdl-js-ripple-effect">
      Delete
    </button>`;

// StickyNote elements top level style classes.
StickyNote.CLASSES = ['mdl-cell--4-col-desktop', 'mdl-card__supporting-text', 'mdl-cell--12-col',
  'mdl-shadow--2dp', 'mdl-cell--4-col-tablet', 'mdl-card', 'mdl-cell', 'sticky-note'];

document.registerElement('sticky-note', StickyNote);
<commit_msg>Updating deprecated registerElement function<commit_after>/**
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * 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.
 */
'use strict';

// A Sticky Notes app.
class StickyNotesApp {

  // Initializes the Sticky Notes app.
  constructor() {
    // Shortcuts to DOM Elements.
    this.notesContainer = document.getElementById('notes-container');
    this.noteMessageInput = document.getElementById('message');
    this.addNoteButton = document.getElementById('save');
    this.notesSectionTitle = document.getElementById('notes-section-title');

    // Saves notes on button click.
    this.addNoteButton.addEventListener('click', () => this.saveNote());

    // Toggle for the button.
    this.noteMessageInput.addEventListener('keyup', () => this.toggleButton());

    // Loads all the notes.
    for (let key in localStorage) {
      this.displayNote(key, localStorage[key]);
    }
    // Listen for updates to notes from other windows.
    window.addEventListener('storage', e => this.displayNote(e.key, e.newValue));
  }

  // Saves a new sticky note on localStorage.
  saveNote() {
    if (this.noteMessageInput.value) {
      let key = Date.now().toString();
      localStorage.setItem(key, this.noteMessageInput.value);
      this.displayNote(key, this.noteMessageInput.value);
      StickyNotesApp.resetMaterialTextfield(this.noteMessageInput);
      this.toggleButton();
    }
  }

  // Resets the given MaterialTextField.
  static resetMaterialTextfield(element) {
    element.value = '';
    element.parentNode.MaterialTextfield.boundUpdateClassesHandler();
    element.blur();
  }

  // Creates/updates/deletes a note in the UI.
  displayNote(key, message) {
    let note = document.getElementById(key);
    // If no element with the given key exists we create a new note.
    if (!note) {
      note = document.createElement('sticky-note');
      note.id = key;
      this.notesContainer.insertBefore(note, this.notesSectionTitle.nextSibling);
    }
    // If the message is null we delete the note.
    if (!message) {
      return note.deleteNote();
    }
    note.setMessage(message);
  }

  // Enables or disables the submit button depending on the values of the input field.
  toggleButton() {
    if (this.noteMessageInput.value) {
      this.addNoteButton.removeAttribute('disabled');
    } else {
      this.addNoteButton.setAttribute('disabled', 'true');
    }
  }
}

// On load start the app.
window.addEventListener('load', () => new StickyNotesApp());

// This is a Sticky Note custom element.
class StickyNote extends HTMLElement {

  // Fires when an instance of the element is created.
  createdCallback() {
    this.classList.add(...StickyNote.CLASSES);
    this.innerHTML = StickyNote.TEMPLATE;
    this.messageElement = this.querySelector('.message');
    this.dateElement = this.querySelector('.date');
    this.deleteButton = this.querySelector('.delete');
    this.deleteButton.addEventListener('click', () => this.deleteNote());
  }

  // Fires when an attribute of the element is added/deleted/modified.
  attributeChangedCallback(attributeName) {
    // We display/update the created date message if the id changes.
    if (attributeName == 'id') {
      let date;
      if (this.id) {
        date = new Date(parseInt(this.id));
      } else {
        date = new Date();
      }

      // Format the date
      let dateFormatterOptions = {day: 'numeric', month: 'short'};
      let shortDate = new Intl.DateTimeFormat("en-US", dateFormatterOptions).format(date);

      this.dateElement.textContent = `Created on ${shortDate}`;
    }
  }

  // Sets the message of the note.
  setMessage(message) {
    this.messageElement.textContent = message;
    // Replace all line breaks by
.
    this.messageElement.innerHTML = this.messageElement.innerHTML.replace(/\n/g, '
');
  }

  // Deletes the note by removing the element from the DOM and the data from localStorage.
  deleteNote() {
    localStorage.removeItem(this.id);
    this.parentNode.removeChild(this);
  }
}

// Initial content of the element.
StickyNote.TEMPLATE = `

    <button class="delete mdl-button mdl-js-button mdl-js-ripple-effect">
      Delete
    </button>`;

// StickyNote elements top level style classes.
StickyNote.CLASSES = ['mdl-cell--4-col-desktop', 'mdl-card__supporting-text', 'mdl-cell--12-col',
  'mdl-shadow--2dp', 'mdl-cell--4-col-tablet', 'mdl-card', 'mdl-cell', 'sticky-note'];

customElements.define('sticky-note', StickyNote);
<|endoftext|>


<commit_before>/**
 * Provide actual flu data with lags
 * Use delphi-epidata API (https://github.com/cmu-delphi/delphi-epidata)
 */

const delphiAPI = require('../assets/delphi_epidata.min')
const metadata = require('./metadata')
const mmwr = require('mmwr-week')
const ProgressBar = require('progress')
const fs = require('fs')

const regionIdentifiers = metadata.regions.map(x => x.id)

/**
 * Function returning complete week array for a season
 * @param {string} season representing season
 * @returns {array} container with week, data pairs
 */
const seasonWeeksData = season => {
  let first = parseInt(season.split('-')[0])
  let second = parseInt(season.split('-')[1])

  // Check the number of weeks in first year
  let firstMaxWeek = mmwr.MMWRWeeksInYear(first)

  let weeks = []
  // Weeks for first year
  for (let i = 30; i <= firstMaxWeek; i++) {
    weeks.push({
      week: parseInt(first + '' + i),
      data: []
    })
  }

  // Weeks for second year
  for (let i = 1; i < 30; i++) {
    let week

    if (i < 10) week = parseInt(second + '0' + i)
    else week = parseInt(second + '' + i)

    weeks.push({
      week: week,
      data: []
    })
  }
  return weeks
}

/**
 * Function mapping week number (201523) to season string
 * @param {number} week week number identifier
 * @returns {string} string season like 2014-2015
 */
const weekToSeason = week => {
  let weekNum = week % 100
  let year = parseInt(week / 100)
  if (weekNum > 29) {
    return [year, year + 1].join('-')
  } else {
    return [year - 1, year].join('-')
  }
}

/**
 * Get actual epidemic data for given seasons
 * @param {array} seasons array of string identifier strings
 * @param {string} cacheFile file pointing to actual cache data
 * @param {function} callback callback function
 */
const getActual = (seasons, cacheFile, callback) => {
  // Get min max epiweek range in seasons
  let firstYear = Math.min(...seasons.map(d => parseInt(d.split('-')[0])))
  let lastYear = Math.max(...seasons.map(d => parseInt(d.split('-')[1])))

  // Request range
  let start = parseInt(firstYear + '' + 30)
  let end = parseInt(lastYear + '' + 29)

  // Setup container
  let output = {}
  regionIdentifiers.forEach(id => {
    output[id] = {}
    seasons.map(season => {
      output[id][season] = seasonWeeksData(season)
    })
  })

  let progressBar = new ProgressBar(' :bar :current of :total lag values', {
    complete: '▇',
    incomplete: '-',
    total: 52
  })

  // Use range and lag value to identify data
  let cache = {}

  // Setup cache
  if (fs.existsSync(cacheFile)) {
    cache = JSON.parse(fs.readFileSync(cacheFile, 'utf8'))
  }

  let rangeIdentifer = start + '-' + end
  if (!(rangeIdentifer in cache)) {
    cache[rangeIdentifer] = {}
  }

  // Fetch data from delphi api for given lag
  const laggedRequest = lag => {
    const populateOutput = data => {
      data.forEach(d => {
        let sub = output[d.region][weekToSeason(d.epiweek)]
        let dataIndex
        for (let i = 0; i < sub.length; i++) {
          if (sub[i].week === d.epiweek) {
            dataIndex = i
            break
          }
        }

        output[d.region][weekToSeason(d.epiweek)][dataIndex].data.push({
          lag: lag,
          value: d.wili
        })
      })
    }

    const nextLagCall = (currentLag) => {
      progressBar.tick()
      if (currentLag === 0) {
        // Save cache
        fs.writeFileSync(cacheFile, JSON.stringify(cache))
        callback(output)
      } else {
        laggedRequest(currentLag - 1)
      }
    }

    if (lag in cache[rangeIdentifer]) {
      // Pulling in from cache
      populateOutput(cache[rangeIdentifer][lag])
      nextLagCall(lag)
    } else {
      // Request API
      delphiAPI.Epidata.fluview((res, message, data) => {
        if (data !== undefined) {
          populateOutput(data)
          cache[rangeIdentifer][lag] = data
        }

        nextLagCall(lag)
      }, regionIdentifiers, [delphiAPI.Epidata.range(start, end)], null, lag)
    }
  }

  // Look upto 51 weeks back
  laggedRequest(51)
}

exports.getActual = getActual
<commit_msg>Skip extra weeks, disable cache<commit_after>/**
 * Provide actual flu data with lags
 * Use delphi-epidata API (https://github.com/cmu-delphi/delphi-epidata)
 */

const delphiAPI = require('../assets/delphi_epidata.min')
const metadata = require('./metadata')
const mmwr = require('mmwr-week')
const ProgressBar = require('progress')
const fs = require('fs')

const regionIdentifiers = metadata.regions.map(x => x.id)

/**
 * Function returning complete week array for a season
 * @param {string} season representing season
 * @returns {array} container with week, data pairs
 */
const seasonWeeksData = season => {
  let first = parseInt(season.split('-')[0])
  let second = parseInt(season.split('-')[1])

  // Check the number of weeks in first year
  let firstMaxWeek = mmwr.MMWRWeeksInYear(first)

  let weeks = []
  // Weeks for first year
  for (let i = 30; i <= firstMaxWeek; i++) {
    weeks.push({
      week: parseInt(first + '' + i),
      data: []
    })
  }

  // Weeks for second year
  for (let i = 1; i < 30; i++) {
    let week

    if (i < 10) week = parseInt(second + '0' + i)
    else week = parseInt(second + '' + i)

    weeks.push({
      week: week,
      data: []
    })
  }
  return weeks
}

/**
 * Function mapping week number (201523) to season string
 * @param {number} week week number identifier
 * @returns {string} string season like 2014-2015
 */
const weekToSeason = week => {
  let weekNum = week % 100
  let year = parseInt(week / 100)
  if (weekNum > 29) {
    return [year, year + 1].join('-')
  } else {
    return [year - 1, year].join('-')
  }
}

/**
 * Get actual epidemic data for given seasons
 * @param {array} seasons array of string identifier strings
 * @param {string} cacheFile file pointing to actual cache data
 * @param {function} callback callback function
 */
const getActual = (seasons, cacheFile, callback) => {
  // Get min max epiweek range in seasons
  let firstYear = Math.min(...seasons.map(d => parseInt(d.split('-')[0])))
  let lastYear = Math.max(...seasons.map(d => parseInt(d.split('-')[1])))

  // Request range
  let start = parseInt(firstYear + '' + 30)

  // Current week
  let current = mmwr.DateToMMWRWeek().year * 100 + mmwr.DateToMMWRWeek().week
  let end = Math.min(current, parseInt(lastYear + '' + 29))

  // Setup container
  let output = {}
  regionIdentifiers.forEach(id => {
    output[id] = {}
    seasons.map(season => {
      output[id][season] = seasonWeeksData(season)
    })
  })

  let progressBar = new ProgressBar(' :bar :current of :total lag values', {
    complete: '▇',
    incomplete: '-',
    total: 52
  })

  // Use range and lag value to identify data
  let cache = {}

  // Setup cache
  if (fs.existsSync(cacheFile)) {
    cache = JSON.parse(fs.readFileSync(cacheFile, 'utf8'))
  } else {
    cache = {
      data: {},
      lastWeek: 0 // Used to identify cache staleness
    }
  }

  // let cacheStaleness = current - cache.lastWeek

  let rangeIdentifer = start + '-' + end
  if (!(rangeIdentifer in cache.data)) {
    cache.data[rangeIdentifer] = {}
  }

  // Fetch data from delphi api for given lag
  const laggedRequest = lag => {
    const populateOutput = data => {
      data.forEach(d => {
        let sub = output[d.region][weekToSeason(d.epiweek)]
        let dataIndex
        for (let i = 0; i < sub.length; i++) {
          if (sub[i].week === d.epiweek) {
            dataIndex = i
            break
          }
        }

        output[d.region][weekToSeason(d.epiweek)][dataIndex].data.push({
          lag: lag,
          value: d.wili
        })
      })
    }

    const nextLagCall = (currentLag) => {
      progressBar.tick()
      if (currentLag === 0) {
        // Save cache
        cache.lastWeek = current
        fs.writeFileSync(cacheFile, JSON.stringify(cache))
        callback(output)
      } else {
        laggedRequest(currentLag - 1)
      }
    }

    // if (lag in cache.data[rangeIdentifer]) {
    //   // Pulling in from cache
    //   populateOutput(cache.data[rangeIdentifer][lag])
    //   nextLagCall(lag)
    // } else {
    // Request API
    delphiAPI.Epidata.fluview((res, message, data) => {
      if (data !== undefined) {
        populateOutput(data)
        cache.data[rangeIdentifer][lag] = data
      }

      nextLagCall(lag)
    }, regionIdentifiers, [delphiAPI.Epidata.range(start, end)], null, lag)
    // }
  }

  // Look upto 51 weeks back
  laggedRequest(51)
}

exports.getActual = getActual
<|endoftext|>


<commit_before>kernel.fp.array = def(
  [
  ],

  function () {
    var equals = function (a1, a2) {
      if (a1.length !== a2.length)
        return false;
      for (var i = 0; i < a1.length; ++i)
        if (a1[i] !== a2[i])
          return false;
      return true;
    };

    var forall = function (a, f) {
      var fn = f || function (x) {
        return x === true;
      };
      for (var i = 0; i < a.length; ++i)
        if (fn(a[i]) !== true)
          return false;
      return true;
    };

    var map = function (a, f) {
      var r = [];
      for (var i = 0; i < a.length; ++i)
        r.push(f(a[i], i));
      return r;
    };

    var flatten = function (a) {
      var r = [];
      for (var i = 0; i < a.length; ++i)
        r = r.concat(a[i]);
      return r;
    };

    var flatmap = function (a, f) {
      return flatten(map(a, f));
    };

    var filter = function (a, f) {
      var r = [];
      for (var i = 0; i < a.length; ++i)
        if (f(a[i]))
          r.push(a[i]);
      return r;
    };

    var each = map;

    var contains = function (a, x) {
      return !forall(a, function (v) {
        return v !== x;
      });
    };

    var indexof = function (a, x) {
      for (var i = 0; i < a.length; ++i)
        if (a[i] === x)
          return i;
      return -1;
    };

    return {
      equals: equals,
      forall: forall,
      flatten: flatten,
      map: map,
      flatmap: flatmap,
      each: each,
      forall: forall,
      filter: filter,
      contains: contains,
      indexof: indexof
    };
  }
);
<commit_msg>Add a global modulator and tests.<commit_after>kernel.fp.array = def(
  [
  ],

  function () {
    var equals = function (a1, a2) {
      if (a1.length !== a2.length)
        return false;
      for (var i = 0; i < a1.length; ++i)
        if (a1[i] !== a2[i])
          return false;
      return true;
    };

    var forall = function (a, f) {
      var fn = f || function (x) {
        return x === true;
      };
      for (var i = 0; i < a.length; ++i)
        if (fn(a[i]) !== true)
          return false;
      return true;
    };

    var map = function (a, f) {
      var r = [];
      for (var i = 0; i < a.length; ++i)
        r.push(f(a[i], i));
      return r;
    };

    var flatten = function (a) {
      var r = [];
      for (var i = 0; i < a.length; ++i)
        r = r.concat(a[i]);
      return r;
    };

    var flatmap = function (a, f) {
      return flatten(map(a, f));
    };

    var filter = function (a, f) {
      var r = [];
      for (var i = 0; i < a.length; ++i)
        if (f(a[i]))
          r.push(a[i]);
      return r;
    };

    var each = map;

    var contains = function (a, x) {
      return !forall(a, function (v) {
        return v !== x;
      });
    };

    var indexof = function (a, x) {
      for (var i = 0; i < a.length; ++i)
        if (a[i] === x)
          return i;
      return -1;
    };

    return {
      equals: equals,
      forall: forall,
      map: map,
      flatten: flatten,
      flatmap: flatmap,
      filter: filter,
      each: each,
      contains: contains,
      indexof: indexof
    };
  }
);
<|endoftext|>