<commit_before>// Graph options we might want:
//  Grid off/on
//  Stacked off/on
//  Area off/on
//  Legend position
//  Short link
//  Graph title
//  Palette
//  Background
//  Enable tooltips
//  width/height
//  Axis options
//  Y-Range min/max
//  (X-Range min/max)
//  X-Axis format
//  Y-Axis format
//  Y-Axis title
//  X-Axis title
//  Log scale

var Prometheus = Prometheus || {};
var graphs = [];

Prometheus.Graph = function(element, options) {
  this.el = element;
  this.options = options;
  this.changeHandler = null;
  this.rickshawGraph = null;
  this.data = [];

  this.initialize();
};

Prometheus.Graph.timeFactors = {
  "y": 60 * 60 * 24 * 365,
  "w": 60 * 60 * 24 * 7,
  "d": 60 * 60 * 24,
  "h": 60 * 60,
  "m": 60,
  "s": 1
};

Prometheus.Graph.stepValues = [
  "1s", "10s", "1m", "5m", "15m", "30m", "1h", "2h", "6h", "12h", "1d", "2d",
  "1w", "2w", "4w", "8w", "1y", "2y"
];

Prometheus.Graph.numGraphs = 0;

Prometheus.Graph.prototype.initialize = function() {
  var self = this;
  self.id = Prometheus.Graph.numGraphs++;

  // Set default options.
  self.options['id'] = self.id;
  self.options['range_input'] = self.options['range_input'] || "1h";
  self.options['stacked_checked'] = self.options['stacked'] ? "checked" : "";

  // Draw graph controls and container from Handlebars template.
  var source = $("#graph_template").html();
  var template = Handlebars.compile(source);
  var graphHtml = template(self.options);
  self.el.append(graphHtml);

  // Get references to all the interesting elements in the graph container and
  // bind event handlers.
  var graphWrapper = self.el.find("#graph_wrapper" + self.id);
  self.queryForm = graphWrapper.find(".query_form");
  self.expr = graphWrapper.find("input[name=expr]");
  self.rangeInput = self.queryForm.find("input[name=range_input]");
  self.stacked = self.queryForm.find("input[name=stacked]");
  self.insertMetric = self.queryForm.find("select[name=insert_metric]");

  // Return moves focus back to expr instead of submitting.
  self.insertMetric.bind('keydown', 'return', function(e) {
    self.expr.focus();
    self.expr.val(self.expr.val());

    return e.preventDefault();
  })

  self.graph = graphWrapper.find(".graph");
  self.legend = graphWrapper.find(".legend");
  self.spinner = graphWrapper.find(".spinner");
  self.evalStats = graphWrapper.find(".eval_stats");

  self.endDate = graphWrapper.find("input[name=end_input]");
  if (self.options["end_input"]) {
    self.endDate.appendDtpicker({"current": self.options["end_input"]});
  } else {
    self.endDate.appendDtpicker();
    self.endDate.val("");
  }
  self.endDate.change(function() { self.submitQuery() });

  self.stacked.change(function() { self.updateGraph(); });
  self.queryForm.submit(function() { self.submitQuery(); return false; });
  self.spinner.hide();

  self.queryForm.find("input[name=inc_range]").click(function() { self.increaseRange(); });
  self.queryForm.find("input[name=dec_range]").click(function() { self.decreaseRange(); });

  self.queryForm.find("input[name=inc_end]").click(function() { self.increaseEnd(); });
  self.queryForm.find("input[name=dec_end]").click(function() { self.decreaseEnd(); });

  self.insertMetric.change(function() {
    self.expr.selection('replace', {text: self.insertMetric.val(), mode: 'before'})
    self.insertMetric.focus(); // refocusing
  });
  self.insertMetric.click(function() { self.expr.focus() })

  self.expr.focus(); // TODO: move to external Graph method.

  self.populateInsertableMetrics();

  if (self.expr.val()) {
    self.submitQuery();
  }
};

Prometheus.Graph.prototype.populateInsertableMetrics = function() {
  var self = this;
  $.ajax({
      method: "GET",
      url: "/api/metrics",
      dataType: "json",
      success: function(json, textStatus) {
        for (var i = 0; i < json.length; i++) {
          self.insertMetric[0].options.add(new Option(json[i], json[i]));
        }
      },
      error: function() {
        alert("Error loading available metrics!");
      },
  });
};

Prometheus.Graph.prototype.onChange = function(handler) {
  this.changeHandler = handler;
};

Prometheus.Graph.prototype.getOptions = function() {
  var self = this;
  var options = {};

  var optionInputs = [
    "expr",
    "range_input",
    "end_input",
    "step_input",
    "stacked"
  ];

  self.queryForm.find("input").each(function(index, element) {
      var name = element.name;
      if ($.inArray(name, optionInputs) >= 0) {
        if (name == "stacked") {
          options[name] = element.checked;
        } else {
          options[name] = element.value;
        }
      }
  });
  return options;
};

Prometheus.Graph.prototype.parseRange = function(rangeText) {
  var rangeRE = new RegExp("^([0-9]+)([ywdhms]+)$");
  var matches = rangeText.match(rangeRE);
  if (matches.length != 3) {
    return 60;
  }
  var value = parseInt(matches[1]);
  var unit = matches[2];
  return value * Prometheus.Graph.timeFactors[unit];
};

Prometheus.Graph.prototype.increaseRange = function() {
  var self = this;
  var rangeSeconds = self.parseRange(self.rangeInput.val());
  for (var i = 0; i < Prometheus.Graph.stepValues.length; i++) {
    if (rangeSeconds < self.parseRange(Prometheus.Graph.stepValues[i])) {
      self.rangeInput.val(Prometheus.Graph.stepValues[i]);
      if (self.expr.val()) {
        self.submitQuery();
      }
      return;
    }
  }
};

Prometheus.Graph.prototype.decreaseRange = function() {
  var self = this;
  var rangeSeconds = self.parseRange(self.rangeInput.val());
  for (var i = Prometheus.Graph.stepValues.length - 1; i >= 0; i--) {
    if (rangeSeconds > self.parseRange(Prometheus.Graph.stepValues[i])) {
      self.rangeInput.val(Prometheus.Graph.stepValues[i]);
      if (self.expr.val()) {
        self.submitQuery();
      }
      return;
    }
  }
};

Prometheus.Graph.prototype.getEndDate = function() {
  var self = this;
  if (!self.endDate || !self.endDate.val()) {
    return null;
  }
  return new Date(self.endDate.val()).getTime();
};

Prometheus.Graph.prototype.getOrSetEndDate = function() {
  var self = this;
  var date = self.getEndDate();
  if (date) {
    return date;
  }
  date = new Date();
  self.setEndDate(date);
  return date;
}

Prometheus.Graph.prototype.setEndDate = function(date) {
  var self = this;
  dateString = date.getFullYear() + '-' + (date.getMonth()+1) + '-' + date.getDate() + ' ' +
               date.getHours() + ':' + date.getMinutes();
  self.endDate.val("");
  self.endDate.appendDtpicker({"current": dateString});
};

Prometheus.Graph.prototype.increaseEnd = function() {
  var self = this;
  self.setEndDate(new Date(self.getOrSetEndDate() + self.parseRange(self.rangeInput.val()) * 1000/2 )) // increase by 1/2 range & convert ms in s
  self.submitQuery();
};

Prometheus.Graph.prototype.decreaseEnd = function() {
  var self = this;
  self.setEndDate(new Date(self.getOrSetEndDate() - self.parseRange(self.rangeInput.val()) * 1000/2 ))
  self.submitQuery();
};

Prometheus.Graph.prototype.submitQuery = function() {
  var self = this;

  self.spinner.show();
  self.evalStats.empty();

  var startTime = new Date().getTime();

  var rangeSeconds = self.parseRange(self.rangeInput.val());
  self.queryForm.find("input[name=range]").val(rangeSeconds);
  var resolution = self.queryForm.find("input[name=step_input]").val() || Math.max(Math.floor(rangeSeconds / 250), 1);
  self.queryForm.find("input[name=step]").val(resolution);
  var endDate = self.getEndDate() / 1000;
  self.queryForm.find("input[name=end]").val(endDate);

  $.ajax({
      method: self.queryForm.attr("method"),
      url: self.queryForm.attr("action"),
      dataType: "json",
      data: self.queryForm.serialize(),
      success: function(json, textStatus) {
        if (json.Type == "error") {
          alert(json.Value);
          return;
        }
        self.data = self.transformData(json);
        if (self.data.length == 0) {
          alert("No datapoints found.");
          return;
        }
        self.updateGraph(true);
      },
      error: function() {
        alert("Error executing query!");
      },
      complete: function() {
        var duration = new Date().getTime() - startTime;
        self.evalStats.html("Load time: " + duration + "ms, resolution: " + resolution + "s");
        self.spinner.hide();
      }
  });
};

Prometheus.Graph.prototype.metricToTsName = function(labels) {
  var tsName = labels["name"] + "{";
  var labelStrings = [];
  for (label in labels) {
    if (label != "name") {
      labelStrings.push(label + "='" + labels[label] + "'");
    }
  }
  tsName += labelStrings.join(",") + "}";
  return tsName;
};

Prometheus.Graph.prototype.parseValue = function(value) {
  if (value == "NaN" || value == "Inf" || value == "-Inf") {
    return 0; // TODO: what should we really do here?
  } else {
    return parseFloat(value);
  }
};

Prometheus.Graph.prototype.transformData = function(json) {
  self = this;
  var palette = new Rickshaw.Color.Palette();
  if (json.Type != "matrix") {
    alert("Result is not of matrix type! Please enter a correct expression.");
    return [];
  }
  var data = json.Value.map(function(ts) {
    return {
      name: self.metricToTsName(ts.Metric),
      data: ts.Values.map(function(value) {
        return {
          x: value.Timestamp,
          y: self.parseValue(value.Value)
        }
      }),
      color: palette.color()
    };
  });
  Rickshaw.Series.zeroFill(data);
  return data;
};

Prometheus.Graph.prototype.showGraph = function() {
  var self = this;
  self.rickshawGraph = new Rickshaw.Graph({
    element: self.graph[0],
    height: Math.max(self.graph.innerHeight(), 100),
    width: Math.max(self.graph.innerWidth(), 200),
    renderer: (self.stacked.is(":checked") ? "stack" : "line"),
    interpolation: "linear",
    series: self.data
  });

  var xAxis = new Rickshaw.Graph.Axis.Time({ graph: self.rickshawGraph });

  var yAxis = new Rickshaw.Graph.Axis.Y({
    graph: self.rickshawGraph,
    tickFormat: Rickshaw.Fixtures.Number.formatKMBT,
  });

  self.rickshawGraph.render();
};

Prometheus.Graph.prototype.updateGraph = function(reloadGraph) {
  var self = this;
  if (self.data.length == 0) { return; }
  self.legend.empty();
  if (self.rickshawGraph == null || reloadGraph) {
    self.graph.empty();
    self.showGraph();
  } else {
    self.rickshawGraph.configure({
      renderer: (self.stacked.is(":checked") ? "stack" : "line"),
      interpolation: "linear",
      series: self.data
    });
    self.rickshawGraph.render();
  }

  var hoverDetail = new Rickshaw.Graph.HoverDetail({
    graph: self.rickshawGraph
  });

  var legend = new Rickshaw.Graph.Legend({
    element: self.legend[0],
    graph: self.rickshawGraph
  });

  var shelving = new Rickshaw.Graph.Behavior.Series.Toggle({
    graph: self.rickshawGraph,
    legend: legend
  });

  self.changeHandler();
};
Prometheus.Graph.prototype.resizeGraph = function() {
  var self = this;
  self.rickshawGraph.configure({
    height: Math.max(self.graph.innerHeight(), 100),
    width: Math.max(self.graph.innerWidth(), 200),
  });
  self.rickshawGraph.render();
};

function parseGraphOptionsFromUrl() {
  var hashOptions = window.location.hash.slice(1);
  if (!hashOptions) {
    return [];
  }
  var optionsJSON = decodeURIComponent(window.location.hash.slice(1));
  return JSON.parse(optionsJSON);
}

function storeGraphOptionsInUrl(options) {
  var allGraphsOptions = [];
  for (var i = 0; i < graphs.length; i++) {
    allGraphsOptions.push(graphs[i].getOptions());
  }
  var optionsJSON = JSON.stringify(allGraphsOptions);
  window.location.hash = encodeURIComponent(optionsJSON);
}

function addGraph(options) {
  var graph = new Prometheus.Graph($("#graph_container"), options);
  graphs.push(graph);
  graph.onChange(function() {
    storeGraphOptionsInUrl();
  });
  $(window).resize(function() {
    graph.resizeGraph();
  });
}

function init() {
  jQuery.ajaxSetup({
    cache: false
  });

  var options = parseGraphOptionsFromUrl();
  if (options.length == 0) {
    options.push({});
  }
  for (var i = 0; i < options.length; i++) {
    addGraph(options[i]);
  }

  $("#add_graph").click(function() { addGraph({}); });
}

$(init);
<commit_msg>Add autocompletion for metrics.<commit_after>// Graph options we might want:
//  Grid off/on
//  Stacked off/on
//  Area off/on
//  Legend position
//  Short link
//  Graph title
//  Palette
//  Background
//  Enable tooltips
//  width/height
//  Axis options
//  Y-Range min/max
//  (X-Range min/max)
//  X-Axis format
//  Y-Axis format
//  Y-Axis title
//  X-Axis title
//  Log scale

var Prometheus = Prometheus || {};
var graphs = [];

Prometheus.Graph = function(element, options) {
  this.el = element;
  this.options = options;
  this.changeHandler = null;
  this.rickshawGraph = null;
  this.data = [];

  this.initialize();
};

Prometheus.Graph.timeFactors = {
  "y": 60 * 60 * 24 * 365,
  "w": 60 * 60 * 24 * 7,
  "d": 60 * 60 * 24,
  "h": 60 * 60,
  "m": 60,
  "s": 1
};

Prometheus.Graph.stepValues = [
  "1s", "10s", "1m", "5m", "15m", "30m", "1h", "2h", "6h", "12h", "1d", "2d",
  "1w", "2w", "4w", "8w", "1y", "2y"
];

Prometheus.Graph.numGraphs = 0;

Prometheus.Graph.prototype.initialize = function() {
  var self = this;
  self.id = Prometheus.Graph.numGraphs++;

  // Set default options.
  self.options['id'] = self.id;
  self.options['range_input'] = self.options['range_input'] || "1h";
  self.options['stacked_checked'] = self.options['stacked'] ? "checked" : "";

  // Draw graph controls and container from Handlebars template.
  var source = $("#graph_template").html();
  var template = Handlebars.compile(source);
  var graphHtml = template(self.options);
  self.el.append(graphHtml);

  // Get references to all the interesting elements in the graph container and
  // bind event handlers.
  var graphWrapper = self.el.find("#graph_wrapper" + self.id);
  self.queryForm = graphWrapper.find(".query_form");
  self.expr = graphWrapper.find("input[name=expr]");
  self.rangeInput = self.queryForm.find("input[name=range_input]");
  self.stacked = self.queryForm.find("input[name=stacked]");
  self.insertMetric = self.queryForm.find("select[name=insert_metric]");

  // Return moves focus back to expr instead of submitting.
  self.insertMetric.bind('keydown', 'return', function(e) {
    self.expr.focus();
    self.expr.val(self.expr.val());

    return e.preventDefault();
  })

  self.graph = graphWrapper.find(".graph");
  self.legend = graphWrapper.find(".legend");
  self.spinner = graphWrapper.find(".spinner");
  self.evalStats = graphWrapper.find(".eval_stats");

  self.endDate = graphWrapper.find("input[name=end_input]");
  if (self.options["end_input"]) {
    self.endDate.appendDtpicker({"current": self.options["end_input"]});
  } else {
    self.endDate.appendDtpicker();
    self.endDate.val("");
  }
  self.endDate.change(function() { self.submitQuery() });

  self.stacked.change(function() { self.updateGraph(); });
  self.queryForm.submit(function() { self.submitQuery(); return false; });
  self.spinner.hide();

  self.queryForm.find("input[name=inc_range]").click(function() { self.increaseRange(); });
  self.queryForm.find("input[name=dec_range]").click(function() { self.decreaseRange(); });

  self.queryForm.find("input[name=inc_end]").click(function() { self.increaseEnd(); });
  self.queryForm.find("input[name=dec_end]").click(function() { self.decreaseEnd(); });

  self.insertMetric.change(function() {
    self.expr.selection('replace', {text: self.insertMetric.val(), mode: 'before'})
    self.insertMetric.focus(); // refocusing
  });
  self.insertMetric.click(function() { self.expr.focus() })

  self.expr.focus(); // TODO: move to external Graph method.

  self.populateInsertableMetrics();

  if (self.expr.val()) {
    self.submitQuery();
  }
};

Prometheus.Graph.prototype.populateInsertableMetrics = function() {
  var self = this;
  $.ajax({
      method: "GET",
      url: "/api/metrics",
      dataType: "json",
      success: function(json, textStatus) {
        var availableMetrics = []
        for (var i = 0; i < json.length; i++) {
          self.insertMetric[0].options.add(new Option(json[i], json[i]));
          availableMetrics.push(json[i])
        }
        self.expr.autocomplete({source: availableMetrics})
      },
      error: function() {
        alert("Error loading available metrics!");
      },
  });
};

Prometheus.Graph.prototype.onChange = function(handler) {
  this.changeHandler = handler;
};

Prometheus.Graph.prototype.getOptions = function() {
  var self = this;
  var options = {};

  var optionInputs = [
    "expr",
    "range_input",
    "end_input",
    "step_input",
    "stacked"
  ];

  self.queryForm.find("input").each(function(index, element) {
      var name = element.name;
      if ($.inArray(name, optionInputs) >= 0) {
        if (name == "stacked") {
          options[name] = element.checked;
        } else {
          options[name] = element.value;
        }
      }
  });
  return options;
};

Prometheus.Graph.prototype.parseRange = function(rangeText) {
  var rangeRE = new RegExp("^([0-9]+)([ywdhms]+)$");
  var matches = rangeText.match(rangeRE);
  if (matches.length != 3) {
    return 60;
  }
  var value = parseInt(matches[1]);
  var unit = matches[2];
  return value * Prometheus.Graph.timeFactors[unit];
};

Prometheus.Graph.prototype.increaseRange = function() {
  var self = this;
  var rangeSeconds = self.parseRange(self.rangeInput.val());
  for (var i = 0; i < Prometheus.Graph.stepValues.length; i++) {
    if (rangeSeconds < self.parseRange(Prometheus.Graph.stepValues[i])) {
      self.rangeInput.val(Prometheus.Graph.stepValues[i]);
      if (self.expr.val()) {
        self.submitQuery();
      }
      return;
    }
  }
};

Prometheus.Graph.prototype.decreaseRange = function() {
  var self = this;
  var rangeSeconds = self.parseRange(self.rangeInput.val());
  for (var i = Prometheus.Graph.stepValues.length - 1; i >= 0; i--) {
    if (rangeSeconds > self.parseRange(Prometheus.Graph.stepValues[i])) {
      self.rangeInput.val(Prometheus.Graph.stepValues[i]);
      if (self.expr.val()) {
        self.submitQuery();
      }
      return;
    }
  }
};

Prometheus.Graph.prototype.getEndDate = function() {
  var self = this;
  if (!self.endDate || !self.endDate.val()) {
    return null;
  }
  return new Date(self.endDate.val()).getTime();
};

Prometheus.Graph.prototype.getOrSetEndDate = function() {
  var self = this;
  var date = self.getEndDate();
  if (date) {
    return date;
  }
  date = new Date();
  self.setEndDate(date);
  return date;
}

Prometheus.Graph.prototype.setEndDate = function(date) {
  var self = this;
  dateString = date.getFullYear() + '-' + (date.getMonth()+1) + '-' + date.getDate() + ' ' +
               date.getHours() + ':' + date.getMinutes();
  self.endDate.val("");
  self.endDate.appendDtpicker({"current": dateString});
};

Prometheus.Graph.prototype.increaseEnd = function() {
  var self = this;
  self.setEndDate(new Date(self.getOrSetEndDate() + self.parseRange(self.rangeInput.val()) * 1000/2 )) // increase by 1/2 range & convert ms in s
  self.submitQuery();
};

Prometheus.Graph.prototype.decreaseEnd = function() {
  var self = this;
  self.setEndDate(new Date(self.getOrSetEndDate() - self.parseRange(self.rangeInput.val()) * 1000/2 ))
  self.submitQuery();
};

Prometheus.Graph.prototype.submitQuery = function() {
  var self = this;

  self.spinner.show();
  self.evalStats.empty();

  var startTime = new Date().getTime();

  var rangeSeconds = self.parseRange(self.rangeInput.val());
  self.queryForm.find("input[name=range]").val(rangeSeconds);
  var resolution = self.queryForm.find("input[name=step_input]").val() || Math.max(Math.floor(rangeSeconds / 250), 1);
  self.queryForm.find("input[name=step]").val(resolution);
  var endDate = self.getEndDate() / 1000;
  self.queryForm.find("input[name=end]").val(endDate);

  $.ajax({
      method: self.queryForm.attr("method"),
      url: self.queryForm.attr("action"),
      dataType: "json",
      data: self.queryForm.serialize(),
      success: function(json, textStatus) {
        if (json.Type == "error") {
          alert(json.Value);
          return;
        }
        self.data = self.transformData(json);
        if (self.data.length == 0) {
          alert("No datapoints found.");
          return;
        }
        self.updateGraph(true);
      },
      error: function() {
        alert("Error executing query!");
      },
      complete: function() {
        var duration = new Date().getTime() - startTime;
        self.evalStats.html("Load time: " + duration + "ms, resolution: " + resolution + "s");
        self.spinner.hide();
      }
  });
};

Prometheus.Graph.prototype.metricToTsName = function(labels) {
  var tsName = labels["name"] + "{";
  var labelStrings = [];
  for (label in labels) {
    if (label != "name") {
      labelStrings.push(label + "='" + labels[label] + "'");
    }
  }
  tsName += labelStrings.join(",") + "}";
  return tsName;
};

Prometheus.Graph.prototype.parseValue = function(value) {
  if (value == "NaN" || value == "Inf" || value == "-Inf") {
    return 0; // TODO: what should we really do here?
  } else {
    return parseFloat(value);
  }
};

Prometheus.Graph.prototype.transformData = function(json) {
  self = this;
  var palette = new Rickshaw.Color.Palette();
  if (json.Type != "matrix") {
    alert("Result is not of matrix type! Please enter a correct expression.");
    return [];
  }
  var data = json.Value.map(function(ts) {
    return {
      name: self.metricToTsName(ts.Metric),
      data: ts.Values.map(function(value) {
        return {
          x: value.Timestamp,
          y: self.parseValue(value.Value)
        }
      }),
      color: palette.color()
    };
  });
  Rickshaw.Series.zeroFill(data);
  return data;
};

Prometheus.Graph.prototype.showGraph = function() {
  var self = this;
  self.rickshawGraph = new Rickshaw.Graph({
    element: self.graph[0],
    height: Math.max(self.graph.innerHeight(), 100),
    width: Math.max(self.graph.innerWidth(), 200),
    renderer: (self.stacked.is(":checked") ? "stack" : "line"),
    interpolation: "linear",
    series: self.data
  });

  var xAxis = new Rickshaw.Graph.Axis.Time({ graph: self.rickshawGraph });

  var yAxis = new Rickshaw.Graph.Axis.Y({
    graph: self.rickshawGraph,
    tickFormat: Rickshaw.Fixtures.Number.formatKMBT,
  });

  self.rickshawGraph.render();
};

Prometheus.Graph.prototype.updateGraph = function(reloadGraph) {
  var self = this;
  if (self.data.length == 0) { return; }
  self.legend.empty();
  if (self.rickshawGraph == null || reloadGraph) {
    self.graph.empty();
    self.showGraph();
  } else {
    self.rickshawGraph.configure({
      renderer: (self.stacked.is(":checked") ? "stack" : "line"),
      interpolation: "linear",
      series: self.data
    });
    self.rickshawGraph.render();
  }

  var hoverDetail = new Rickshaw.Graph.HoverDetail({
    graph: self.rickshawGraph
  });

  var legend = new Rickshaw.Graph.Legend({
    element: self.legend[0],
    graph: self.rickshawGraph
  });

  var shelving = new Rickshaw.Graph.Behavior.Series.Toggle({
    graph: self.rickshawGraph,
    legend: legend
  });

  self.changeHandler();
};
Prometheus.Graph.prototype.resizeGraph = function() {
  var self = this;
  self.rickshawGraph.configure({
    height: Math.max(self.graph.innerHeight(), 100),
    width: Math.max(self.graph.innerWidth(), 200),
  });
  self.rickshawGraph.render();
};

function parseGraphOptionsFromUrl() {
  var hashOptions = window.location.hash.slice(1);
  if (!hashOptions) {
    return [];
  }
  var optionsJSON = decodeURIComponent(window.location.hash.slice(1));
  return JSON.parse(optionsJSON);
}

function storeGraphOptionsInUrl(options) {
  var allGraphsOptions = [];
  for (var i = 0; i < graphs.length; i++) {
    allGraphsOptions.push(graphs[i].getOptions());
  }
  var optionsJSON = JSON.stringify(allGraphsOptions);
  window.location.hash = encodeURIComponent(optionsJSON);
}

function addGraph(options) {
  var graph = new Prometheus.Graph($("#graph_container"), options);
  graphs.push(graph);
  graph.onChange(function() {
    storeGraphOptionsInUrl();
  });
  $(window).resize(function() {
    graph.resizeGraph();
  });
}

function init() {
  jQuery.ajaxSetup({
    cache: false
  });

  var options = parseGraphOptionsFromUrl();
  if (options.length == 0) {
    options.push({});
  }
  for (var i = 0; i < options.length; i++) {
    addGraph(options[i]);
  }

  $("#add_graph").click(function() { addGraph({}); });
}

$(init);
<|endoftext|>


<commit_before>const players = require('../wot/players');
const config = require('../config');

module.exports = run;

let commands = {
  '_wotbot': {
    '_say': {
      args: 1,
      length: [Infinity],
      func: (str) => {
        return Promise.resolve(str);
      }
    },
    '_help': {
      args: 0,
      func: () => {
        return Promise.resolve("Use `wotbot say` to make WoTBot talk back to you or use `player info` or `player search` to search about for players");
      }
    }
  },
  '_player': {
    '_info': {
      args: 1,
      length: [1],
      func: players.playerInfoLink
    },
    '_search': {
      args: 1,
      length: [1],
      func: (input) => {
        return players.searchPlayerNames(input).then(names => {
          if (names.length === 0) {
            return "No players found";
          }
          let truncatedNames = names.length > 10 ? names.slice(0, 10) : names;
          return truncatedNames.join('\n');
        }).catch(err => {
          console.log(err);
          return "Error occurred during searching";
        });
      }
    },
    '_help': {
      args: 0,
      func: () => {
        return Promise.resolve(
          "`player info` for a single player's clantools link\n" +
          "`player search` to search by name\n" +
          "`player wn8` for single player's wn8");
      }
    },
    '_check' : {
      args: 1,
      length: [1],
      func: (input) => {
        return players.checkTanks(input).then(tanks => {
          if (tanks.length === 0) {
            return "Has none of the tanks";
          }
          return tanks.map(tank => tank.name).join(', ')
        }).catch(err => {
          console.log(err);
          return "Error occurred during checking";
        });
      }
    },
    // '_wn8' : { //Deactivated due to issues
    //   args: 1,
    //   length: [1],
    //   func: players.wn8Of
    // }
  },
  "_schedule" : {
    "_add" : {
      args: [2],
      length: [1, Infinity],
      func: (day, message) => {

      }
    },
    "_help" : {
      args: 0,
      func: () => Promise.resolve(
        "Format for adding to schedule:      schedule add [day spelled out] [message]\n" +
        "Format for removing from schedule:  schedule remove [day spelled out]\n" +
        "Format for updating schedule:       schedule update [day spelled out] [new message]\n" +
        "Format for getting schedule:        schedule get or @schedule\n" +
        "Notes: only one scheduled event is allowed per day and bot is dumb can only plan up to week in advance. \n")
    }
  }
};

/**
 * Run the given string command
 *
 * @param inputStr {String}
 * @returns {Promise.<String>}
 */
function run (inputStr) {
  let input = inputStr.split(' ');
  let first = input[0];
  if (config.nodeEnv === 'local' && first && typeof(first) === 'string') {
    if (first.toLowerCase() === 'local') {
      return process(input.slice(1), commands, {});
    }
  }
  else {
    return process(input, commands, {});
  }
}

/**
 * Process the current input and args for the given commands tree
 *
 * @param input {Array}
 * @param cmdTree {Object}
 * @param args {Object}
 * @returns {Promise.<String>}
 */
function process (input, cmdTree, args) {
  if (cmdTree) {
    if (cmdTree.args !== undefined) {
      let len = cmdTree.length ? cmdTree.length[0] : 0;
      let next = input.slice(0, len); //TODO abstract over # of args and length of length
      if (cmdTree.func) {
        console.log(args.last, next.join(' '));
        return cmdTree.func(next.join(' '));
      }
    }

    else {
      let next = input[0];
      if (next && typeof next === 'string') {
        next = next.toLowerCase();
        args.last = next;
        return process(input.slice(1), cmdTree['_' + next], args);
      }
    }
  }
}<commit_msg>Specifically don't support schedule<commit_after>const players = require('../wot/players');
const config = require('../config');

module.exports = run;

function notSupported () {
  return Promise.resolve('Action is not currently supported');
}

let commands = {
  '_wotbot': {
    '_say': {
      args: 1,
      length: [Infinity],
      func: (str) => {
        return Promise.resolve(str);
      }
    },
    '_help': {
      args: 0,
      func: () => {
        return Promise.resolve("Use `wotbot say` to make WoTBot talk back to you or use `player info` or `player search` to search about for players");
      }
    }
  },
  '_player': {
    '_info': {
      args: 1,
      length: [1],
      func: players.playerInfoLink
    },
    '_search': {
      args: 1,
      length: [1],
      func: (input) => {
        return players.searchPlayerNames(input).then(names => {
          if (names.length === 0) {
            return "No players found";
          }
          let truncatedNames = names.length > 10 ? names.slice(0, 10) : names;
          return truncatedNames.join('\n');
        }).catch(err => {
          console.log(err);
          return "Error occurred during searching";
        });
      }
    },
    '_help': {
      args: 0,
      func: () => {
        return Promise.resolve(
          "`player info` for a single player's clantools link\n" +
          "`player search` to search by name\n" +
          "`player wn8` for single player's wn8");
      }
    },
    '_check' : {
      args: 1,
      length: [1],
      func: (input) => {
        return players.checkTanks(input).then(tanks => {
          if (tanks.length === 0) {
            return "Has none of the tanks";
          }
          return tanks.map(tank => tank.name).join(', ')
        }).catch(err => {
          console.log(err);
          return "Error occurred during checking";
        });
      }
    },
    // '_wn8' : { //Deactivated due to issues
    //   args: 1,
    //   length: [1],
    //   func: players.wn8Of
    // }
  },
  "_schedule" : {
    "_add" : {
      args: 2,
      length: [1, Infinity],
      func: (day, message) => {
        return notSupported();
      }
    },
    "_remove" : {
      args: 1,
      length: [1],
      func: (day) => {
        return notSupported();
      }
    },
    "_update" : {
      args: 2,
      length: [1, Infinity],
      func: (day, message) => {
        return notSupported();
      }
    },
    "_get" : {
      args: 0,
      func: notSupported
    },
    "_help" : {
      args: 0,
      func: () => Promise.resolve(
        "Format for adding to schedule:      schedule add [day spelled out] [message]\n" +
        "Format for removing from schedule:  schedule remove [day spelled out]\n" +
        "Format for updating schedule:       schedule update [day spelled out] [new message]\n" +
        "Format for getting schedule:        schedule get or @schedule\n" +
        "Notes: only one scheduled event is allowed per day and bot is dumb can only plan up to week in advance. \n")
    }
  }
};

/**
 * Run the given string command
 *
 * @param inputStr {String}
 * @returns {Promise.<String>}
 */
function run (inputStr) {
  let input = inputStr.split(' ');
  let first = input[0];
  if (config.nodeEnv === 'local' && first && typeof(first) === 'string') {
    if (first.toLowerCase() === 'local') {
      return process(input.slice(1), commands, {});
    }
  }
  else {
    return process(input, commands, {});
  }
}

/**
 * Process the current input and args for the given commands tree
 *
 * @param input {Array}
 * @param cmdTree {Object}
 * @param args {Object}
 * @returns {Promise.<String>}
 */
function process (input, cmdTree, args) {
  if (cmdTree) {
    if (cmdTree.args !== undefined) {
      let len = cmdTree.length ? cmdTree.length[0] : 0;
      let next = input.slice(0, len); //TODO abstract over # of args and length of length
      if (cmdTree.func) {
        console.log(args.last, next.join(' '));
        return cmdTree.func(next.join(' '));
      }
    }

    else {
      let next = input[0];
      if (next && typeof next === 'string') {
        next = next.toLowerCase();
        args.last = next;
        return process(input.slice(1), cmdTree['_' + next], args);
      }
    }
  }
}<|endoftext|>


<commit_before>import Q from "q";
import _ from "underscore";
import iocSpecBuilder from "./iocSpecBuilder";

function throwNotImplemented(nameOfMethod) {
    console.warn(`[YepApp] ${nameOfMethod} not implemented!`);
}

class Delegate {
    constructor(handlers) {
        _.extend(this, handlers);
    }
}

class Components {
    constructor(...listOfComponents) {
        this._list = listOfComponents;
    }
    bootstrap(specs) {
        return Q.all(this._list.map(
            (component) => Q.when(component.bootstrap(specs))
        ));
    }
}

class Bootstrapper {
    constructor(components) {
        this._components = components;
        this._specs = iocSpecBuilder.define();
    }
    bootstrap() {
        if (this._components) {
            this._components.bootstrap(this._specs);
        }
    }
    getComponent() {
        throwNotImplemented("getComponent");
    }
    bootstrapBefore() {
        throwNotImplemented("bootstrapBefore");
    }
    bootstrapAfter() {
        throwNotImplemented("bootstrapAfter");
    }
}

var _instance = null;

export default class App {
    // private contructor
    constructor(components, delegate) {
        if (_instance) {
            throw new Error("App can only have one instance at a time. Try App.terminate() first.");
        }

        this._bootstrapper = new Bootstrapper(components);
        this._delegate = delegate;

        // providers
        this._session = null;

        _instance = this;
    }

    bootstrap() {
        return Q.when(this._delegate.onBeforeBootstrapped(this._bootstrapper))
            .then(() => this._bootstrapper.bootstrap())
            .then(() => this._bootstrapComplete());
    }

    _bootstrapComplete() {
        this._initProviders();
        return this._delegate.onBootstrapped(this._bootstrapper);
    }

    _initProviders() {
        this._session = this._delegate.provideSession();
    }

    static instance() {
        if (!_instance) {
            throw new Error("Can not return App.instance(). App.run must be invoked first.");
        }
        return _instance;
    }

    static run(components, delegate) {
        var _app = new App(components, delegate);
        return _app.bootstrap().then(() => delegate.onReady());
    }

    static terminate() {
        _instance = null;
    }

    get session() {
        return this._session;
    }
    static session() {
        return App.instance().session;
    }

    static user() {
        return App.session().user;
    }

    static logger() {
    }

    static localize() {
    }

    static dispatchAction() {
    }

    static get events() {
    }

    static get Components() { return Components; }
    static get Delegate() { return Delegate; }
}
<commit_msg>Actually wire specs :)<commit_after>import Q from "q";
import _ from "underscore";
import iocSpecBuilder from "./iocSpecBuilder";

function throwNotImplemented(nameOfMethod) {
    console.warn(`[YepApp] ${nameOfMethod} not implemented!`);
}

class Delegate {
    constructor(handlers) {
        _.extend(this, handlers);
    }
}

class Components {
    constructor(...listOfComponents) {
        this._list = listOfComponents;
    }
    bootstrap(specs) {
        return Q.all(this._list.map(
            (component) => Q.when(component.bootstrap(specs))
        ));
    }
}

class Bootstrapper {
    constructor(components) {
        this._components = components;
        this._specs = iocSpecBuilder.define();
    }
    bootstrap() {
        return this._components.bootstrap(this._specs)
            .then(() => this._specs.wire());
    }
    getComponent() {
        throwNotImplemented("getComponent");
    }
    bootstrapBefore() {
        throwNotImplemented("bootstrapBefore");
    }
    bootstrapAfter() {
        throwNotImplemented("bootstrapAfter");
    }
}

var _instance = null;

export default class App {
    // private contructor
    constructor(components, delegate) {
        if (_instance) {
            throw new Error("App can only have one instance at a time. Try App.terminate() first.");
        }

        this._bootstrapper = new Bootstrapper(components);
        this._delegate = delegate;

        // providers
        this._session = null;

        _instance = this;
    }

    bootstrap() {
        return Q.when(this._delegate.onBeforeBootstrapped(this._bootstrapper))
            .then(() => this._bootstrapper.bootstrap())
            .then(() => this._bootstrapComplete());
    }

    _bootstrapComplete() {
        this._initProviders();
        return this._delegate.onBootstrapped(this._bootstrapper);
    }

    _initProviders() {
        this._session = this._delegate.provideSession();
    }

    static instance() {
        if (!_instance) {
            throw new Error("Can not return App.instance(). App.run must be invoked first.");
        }
        return _instance;
    }

    static run(components, delegate) {
        var _app = new App(components, delegate);
        return _app.bootstrap().then(() => delegate.onReady());
    }

    static terminate() {
        _instance = null;
    }

    get session() {
        return this._session;
    }
    static session() {
        return App.instance().session;
    }

    static user() {
        return App.session().user;
    }

    static logger() {
    }

    static localize() {
    }

    static dispatchAction() {
    }

    static get events() {
    }

    static get Components() { return Components; }
    static get Delegate() { return Delegate; }
}
<|endoftext|>


<commit_before>'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

var _react = require('react');

var _react2 = _interopRequireDefault(_react);

var _d = require('d3');

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }

var Bar = function (_React$Component) {
  _inherits(Bar, _React$Component);

  function Bar(props) {
    _classCallCheck(this, Bar);

    var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(Bar).call(this, props));

    _this.onClick = _this._onClick.bind(_this);
    _this.onMouseEnter = _this._onMouseEnter.bind(_this);
    _this.onMouseDown = _this._onMouseDown.bind(_this);
    return _this;
  }

  _createClass(Bar, [{
    key: 'componentWillUnmount',
    value: function componentWillUnmount() {
      if (this._onMouseLeave) {
        this._onMouseLeave();
      }
    }
  }, {
    key: '_onClick',
    value: function _onClick(event) {
      if (this.props.brushed) return;
      if (this.props.tooltipData) {
        this.props.onClick(event, this.props.tooltipData);
      }
    }
  }, {
    key: '_onMouseEnter',
    value: function _onMouseEnter(event) {
      if (this.props.tooltipData) {
        this.props.onEnter(event, this.props.tooltipData);
      }
    }
  }, {
    key: '_onMouseDown',
    value: function _onMouseDown(event) {
      if (this.props.tooltipData) {
        // console.log('Bar :: mousedown')
        var newEvent = new MouseEvent('mousedown', event);
        if (this.props.brushed) {
          var target = (0, _d.select)('.selection');
          var leftMargin = (0, _d.select)('.overlay').node().getBoundingClientRect().left;
          var selectionWidth = parseFloat(target.attr('width'));
          var min = parseFloat(target.attr('x')) + leftMargin;
          var max = parseFloat(target.attr('x')) + selectionWidth + leftMargin;
          if (target.style('display') === 'none' || event.pageX < min || event.pageX > max) {
            target = (0, _d.select)('.overlay').node();
          } else {
            target = target.node();
          }
          target.dispatchEvent(newEvent);
        }
      }
    }
  }, {
    key: 'render',
    value: function render() {
      var _props = this.props;
      var className = _props.className;
      var data = _props.data;
      var name = _props.name;
      var width = _props.width;
      var height = _props.height;
      var y = _props.y;
      var x = _props.x;
      var style = _props.style;

      className = className ? 'histogram-bar ' + className : 'histogram-bar';
      if (this.props.brushed) {
        className += ' brushed';
      }
      return _react2.default.createElement('rect', {
        className: className,
        'data-name': name,
        'data-x': data.x,
        'data-y': data.y,
        width: width,
        height: height,
        x: x,
        y: y,
        onClick: this.onClick,
        onMouseDown: this.onMouseDown,
        onMouseEnter: this.onMouseEnter,
        style: style
      });
    }
  }]);

  return Bar;
}(_react2.default.Component);

Bar.defaultProps = {
  brushed: false,
  height: 0,
  name: '',
  width: 0,
  onClick: function onClick() {
    return null;
  },
  tooltipData: null,
  y: 0,
  x: 0,
  style: {}
};

Bar.propTypes = {
  brushed: _react.PropTypes.bool.isRequired,
  className: _react.PropTypes.string,
  data: _react.PropTypes.object,
  height: _react.PropTypes.number.isRequired,
  name: _react.PropTypes.string,
  width: _react.PropTypes.number.isRequired,
  onClick: _react.PropTypes.func,
  onEnter: _react.PropTypes.func,
  onLeave: _react.PropTypes.func,
  tooltipData: _react.PropTypes.object,
  y: _react.PropTypes.number.isRequired,
  x: _react.PropTypes.number,
  style: _react.PropTypes.object
};

exports.default = Bar;<commit_msg>Forgot to run `npm run build`<commit_after>'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

var _react = require('react');

var _react2 = _interopRequireDefault(_react);

var _d = require('d3');

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }

var Bar = function (_React$Component) {
  _inherits(Bar, _React$Component);

  function Bar(props) {
    _classCallCheck(this, Bar);

    var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(Bar).call(this, props));

    _this.onClick = _this._onClick.bind(_this);
    _this.onMouseEnter = _this._onMouseEnter.bind(_this);
    _this.onMouseDown = _this._onMouseDown.bind(_this);
    _this.onMouseLeave = _this._onMouseLeave.bind(_this);
    return _this;
  }

  _createClass(Bar, [{
    key: 'componentWillUnmount',
    value: function componentWillUnmount() {
      if (this._onMouseLeave) {
        this._onMouseLeave();
      }
    }
  }, {
    key: '_onClick',
    value: function _onClick(event) {
      if (this.props.brushed) return;
      if (this.props.tooltipData) {
        this.props.onClick(event, this.props.tooltipData);
      }
    }
  }, {
    key: '_onMouseEnter',
    value: function _onMouseEnter(event) {
      if (this.props.tooltipData) {
        this.props.onEnter(event, this.props.tooltipData);
      }
    }
  }, {
    key: '_onMouseLeave',
    value: function _onMouseLeave(event) {
      if (this.props.tooltipData) {
        this.props.onLeave(event, this.props.tooltipData);
      }
    }
  }, {
    key: '_onMouseDown',
    value: function _onMouseDown(event) {
      if (this.props.tooltipData) {
        // console.log('Bar :: mousedown')
        var newEvent = new MouseEvent('mousedown', event);
        if (this.props.brushed) {
          var target = (0, _d.select)('.selection');
          var leftMargin = (0, _d.select)('.overlay').node().getBoundingClientRect().left;
          var selectionWidth = parseFloat(target.attr('width'));
          var min = parseFloat(target.attr('x')) + leftMargin;
          var max = parseFloat(target.attr('x')) + selectionWidth + leftMargin;
          if (target.style('display') === 'none' || event.pageX < min || event.pageX > max) {
            target = (0, _d.select)('.overlay').node();
          } else {
            target = target.node();
          }
          target.dispatchEvent(newEvent);
        }
      }
    }
  }, {
    key: 'render',
    value: function render() {
      var _props = this.props;
      var className = _props.className;
      var data = _props.data;
      var name = _props.name;
      var width = _props.width;
      var height = _props.height;
      var y = _props.y;
      var x = _props.x;
      var style = _props.style;

      className = className ? 'histogram-bar ' + className : 'histogram-bar';
      if (this.props.brushed) {
        className += ' brushed';
      }
      return _react2.default.createElement('rect', {
        className: className,
        'data-name': name,
        'data-x': data.x,
        'data-y': data.y,
        width: width,
        height: height,
        x: x,
        y: y,
        onClick: this.onClick,
        onMouseDown: this.onMouseDown,
        onMouseEnter: this.onMouseEnter,
        onMouseLeave: this.onMouseLeave,
        style: style
      });
    }
  }]);

  return Bar;
}(_react2.default.Component);

Bar.defaultProps = {
  brushed: false,
  height: 0,
  name: '',
  width: 0,
  onClick: function onClick() {
    return null;
  },
  tooltipData: null,
  y: 0,
  x: 0,
  style: {}
};

Bar.propTypes = {
  brushed: _react.PropTypes.bool.isRequired,
  className: _react.PropTypes.string,
  data: _react.PropTypes.object,
  height: _react.PropTypes.number.isRequired,
  name: _react.PropTypes.string,
  width: _react.PropTypes.number.isRequired,
  onClick: _react.PropTypes.func,
  onEnter: _react.PropTypes.func,
  onLeave: _react.PropTypes.func,
  tooltipData: _react.PropTypes.object,
  y: _react.PropTypes.number.isRequired,
  x: _react.PropTypes.number,
  style: _react.PropTypes.object
};

exports.default = Bar;<|endoftext|>


<commit_before>/**
 * @class OpenStreet
 */
export class OpenStreet {
  /**
   * @constructor
   */
  constructor() {

    this.settings = {
      url: 'http://nominatim.openstreetmap.org/search/',
      params: {
        q: '',
        format: 'json',
        addressdetails: 1,
        limit: 10,
        countrycodes: '',
        'accept-language': 'en-US'
      }
    };
  }

  getParameters(opt) {
    return {
      url: this.settings.url,
      params: {
        q: opt.query,
        format: this.settings.params.format,
        addressdetails: this.settings.params.addressdetails,
        limit: opt.limit || this.settings.params.limit,
        countrycodes: opt.countrycodes || this.settings.params.countrycodes,
        'accept-language': opt.lang || this.settings.params['accept-language']
      }
    };
  }

  handleResponse(results) {
    return results.map(result => ({
      lon: result.lon,
      lat: result.lat,
      address: {
        name: result.display_name,
        road: result.address.road || '',
        houseNumber: result.address.house_number || '',
        postcode: result.address.postcode,
        city: result.address.city || result.address.town,
        state: result.address.state,
        country: result.address.country
      },
      original: {
        formatted: result.display_name,
        details: result.address
      }
    }));
  }
}
<commit_msg>Update osm url to HTTPS<commit_after>/**
 * @class OpenStreet
 */
export class OpenStreet {
  /**
   * @constructor
   */
  constructor() {

    this.settings = {
      url: 'https://nominatim.openstreetmap.org/search/',
      params: {
        q: '',
        format: 'json',
        addressdetails: 1,
        limit: 10,
        countrycodes: '',
        'accept-language': 'en-US'
      }
    };
  }

  getParameters(opt) {
    return {
      url: this.settings.url,
      params: {
        q: opt.query,
        format: this.settings.params.format,
        addressdetails: this.settings.params.addressdetails,
        limit: opt.limit || this.settings.params.limit,
        countrycodes: opt.countrycodes || this.settings.params.countrycodes,
        'accept-language': opt.lang || this.settings.params['accept-language']
      }
    };
  }

  handleResponse(results) {
    return results.map(result => ({
      lon: result.lon,
      lat: result.lat,
      address: {
        name: result.display_name,
        road: result.address.road || '',
        houseNumber: result.address.house_number || '',
        postcode: result.address.postcode,
        city: result.address.city || result.address.town,
        state: result.address.state,
        country: result.address.country
      },
      original: {
        formatted: result.display_name,
        details: result.address
      }
    }));
  }
}
<|endoftext|>


<commit_before><commit_msg>run quiet, run correctly<commit_after><|endoftext|>


<commit_before>Ext.define('Emergence.proxy.Records', {
    extend: 'Jarvus.proxy.API',
    alias: 'proxy.records',
    alternateClassName: 'Emergence.ext.proxy.Records',
    requires: [
        'Emergence.util.API',
        'Ext.data.reader.Json',
        'Ext.data.writer.Json',
        'Ext.data.Request',
        'Ext.util.Collection'
    ],

    config: {
        connection: 'Emergence.util.API',
        include: null,
        relatedTable: null,
        summary: false,
        injectRelatedTables: true,
        injectModels: false,

        /**
         * @cfg The base URL for the managed collection (e.g. '/people')
         * @required
         */
        url: null,

        idParam: 'ID',
        pageParam: false,
        startParam: 'offset',
        limitParam: 'limit',
        sortParam: 'sort',
        directionParam: 'dir',
        filterParam: 'q',
        simpleSortMode: true,
        reader: {
            type: 'json',
            rootProperty: 'data',
            totalProperty: 'total',
            messageProperty: 'message',
            keepRawData: true
        },
        writer: {
            type: 'json',
            rootProperty: 'data',
            writeAllFields: false,
            allowSingle: false
        }
    },

    constructor: function() {
        this.relatedCollections = {};
        this.callParent(arguments);
    },

    // config handlers
    applyInclude: function(include) {
        if (!include || !include.length) {
            return null;
        }

        return typeof include == 'string' ? [include] : include;
    },

    updateInclude: function(include) {
        this._includeParam = include ? include.join(',') : null;
    },

    applyRelatedTable: function(relatedTable) {
        var relatedTableConfigs = [],
            length, i = 0, config, relationship, model;

        if (!relatedTable || !relatedTable.length) {
            return null;
        }

        if (!Ext.isArray(relatedTable)) {
            relatedTable = [relatedTable];
        }

        length = relatedTable.length;
        for (; i < length; i++) {
            config = relatedTable[i];

            if (typeof config == 'string') {
                config = {
                    relationship: config
                };
            }

            relationship = config.relationship;

            if (!relationship) {
                Ext.Logger.error('relatedTable config missing required attribute relationship');
            }

            model = config.model;

            if (typeof model == 'string') {
                Ext.syncRequire(model);
                model = config.model = Ext.ClassManager.get(model);
            }

            if (!config.foreignKey) {
                config.foreignKey = model ? model.idProperty : 'ID';
            }

            if (!config.localKey) {
                config.localKey = relationship + 'ID';
            }

            relatedTableConfigs.push(config);
        }

        return relatedTableConfigs;
    },

    updateRelatedTable: function(relatedTable, oldRelatedTable) {
        var relatedCollections = this.relatedCollections,
            initializedTables = [],
            relatedLength, relatedIndex, config, relationship, Model, foreignKey;

        this._relatedTableParam = relatedTable ? Ext.Array.pluck(relatedTable, 'relationship').join(',') : null;

        if (relatedTable) {
            relatedLength = relatedTable.length;
            relatedIndex = 0;
            for (; relatedIndex < relatedLength; relatedIndex++) {
                config = relatedTable[relatedIndex];
                relationship = config.relationship;
                Model = config.model;
                foreignKey = config.foreignKey;

                relatedCollections[relationship] = new Ext.util.Collection({
                    // eslint-disable-next-line no-loop-func
                    decoder: Model ? function(data) {
                        return new Model(data);
                    } : null,
                    // eslint-disable-next-line no-loop-func
                    keyFn: Model ? null : function(data) {
                        return data[foreignKey];
                    }
                });

                initializedTables.push(relationship);
            }
        }

        if (oldRelatedTable) {
            relatedLength = oldRelatedTable.length;
            relatedIndex = 0;
            for (; relatedIndex < relatedLength; relatedIndex++) {
                relationship = oldRelatedTable[relatedIndex].relationship;

                if (initializedTables.indexOf(relationship) == -1) {
                    delete relatedCollections[relationship];
                }
            }
        }
    },

    /**
     * TODO: overriding this entire method may no longer be necessary given the new Jarvus.proxy.API's template methods
     */
    buildRequest: function(operation) {
        var me = this,
            initialParams = Ext.apply({}, typeof operation.getParams == 'function' ? operation.getParams() : operation.params),
            // Clone params right now so that they can be mutated at any point further down the call stack
            params = Ext.applyIf(initialParams, typeof me.getExtraParams == 'function' ? me.getExtraParams() : me.extraParams || {}),
            request = new Ext.data.Request({
                action: typeof operation.getAction == 'function' ? operation.getAction() : operation.action,
                records: operation.getRecords(),
                operation: operation,
                params: Ext.applyIf(params, me.getParams(operation))
            });

        request.setUrl(operation.getUrl() || me.buildUrl(request));
        request.setMethod(me.getMethod(request));
        request.setHeaders(me.getHeaders(request));
        request.setTimeout(me.getTimeout(request));
        request.setWithCredentials(me.getWithCredentials());

        // compatibility with Jarvus.ext.override.proxy.DirtyParams since we're entirely replacing the buildRequest method it overrides
        if (typeof me.clearParamsDirty == 'function') {
            me.clearParamsDirty();
        }

        operation.setRequest(request);

        return request;
    },

    buildUrl: function(request) {
        var me = this,
            baseUrl = me.getUrl(request),
            action = typeof request.getAction == 'function' ? request.getAction() : request.action,
            operation, id, idParam, handleParam;

        switch (action) {
            case 'read':
                operation = typeof request.getOperation == 'function' ? request.getOperation() : request.operation;

                // use recordHandle if provided
                id = operation.recordHandle;
                if (id) {
                    baseUrl += '/' + encodeURIComponent(id);
                    break;
                }

                // apply id to path if idProperty is ID or Handle
                id = typeof operation.getId == 'function' ? operation.getId() : operation.id;
                idParam = typeof me.getIdParam == 'function'? me.getIdParam() : me.idParam;
                handleParam = (typeof me.getModel == 'function' ? me.getModel() : me.model).handleProperty || 'Handle';
                if (id && ((idParam == 'ID' && id > 0) || idParam == handleParam)) {
                    baseUrl += '/' + encodeURIComponent(id);
                }

                break;
            case 'create':
            case 'update':
                baseUrl += '/save';
                break;
            case 'destroy':
                baseUrl += '/destroy';
                break;
            default:
                Ext.Logger.error('Unhandled request action');
        }

        return baseUrl;
    },

    getParams: function(operation) {
        var me = this,
            includeParam = operation.include,
            relatedTableParam = me._relatedTableParam,
            summary = me.getSummary(),
            idParam = me.idParam,
            id = typeof operation.getId == 'function' ? operation.getId() : operation.id,
            params = me.callParent(arguments);

        if (id && idParam != 'ID') {
            params[idParam] = id;
        }

        if (includeParam) {
            if (Ext.isArray(includeParam)) {
                includeParam = includeParam.join(',');
            }
        } else {
            includeParam = me._includeParam;
        }

        if (includeParam) {
            params.include = includeParam;
        }

        if (relatedTableParam) {
            params.relatedTable = relatedTableParam;
        }

        if (summary) {
            params.summary = 'true';
        }

        return params;
    },

    encodeFilters: function(filters) {
        var out = [],
            length = filters.length,
            i = 0, filterData, filterValue;

        for (; i < length; i++) {
            filterData = filters[i].serialize();
            filterValue = filterData.value.toString();
            out[i] = filterData.property+ ':' + (filterValue.match(/\s/) ? '"' + filterValue + '"' : filterValue);
        }

        return out.join(' ');
    },

    getMethod: function(request) {
        switch (request.getAction()) {
            case 'read':
                return 'GET';
            case 'create':
            case 'update':
            case 'destroy':
                return 'POST';
            default:
                Ext.Logger.error('Unhandled request action');
                return null;
        }
    },

    extractResponseData: function() {
        var me = this,
            relatedCollections = me.relatedCollections,
            relatedTableConfigs = me.getRelatedTable(),
            injectRelatedTables = me.getInjectRelatedTables(),
            injectModels = me.getInjectModels(),
            rootProperty = me.getReader().getRootProperty(),
            data = me.callParent(arguments),
            relatedData = data.related,
            length, i = 0, config, relationship, relatedCollection, localKey,
            recordsData, recordsLength, recordIndex, recordData, relatedRecord;

        if (relatedTableConfigs && relatedData) {
            length = relatedTableConfigs.length;

            for (; i < length; i++) {
                config = relatedTableConfigs[i];
                relationship = config.relationship;
                relatedCollection = relatedCollections[relationship];
                relatedCollection.add(relatedData[relationship]);

                if (injectRelatedTables) {
                    localKey = config.localKey;
                    recordsData = rootProperty ? data[rootProperty] : data;
                    recordsLength = recordsData.length;
                    recordIndex = 0;

                    for (; recordIndex < recordsLength; recordIndex++) {
                        recordData = recordsData[recordIndex];
                        relatedRecord = relatedCollection.getByKey(recordData[localKey]);
                        recordData[relationship] = !injectModels && relatedRecord instanceof Ext.data.Model ? relatedRecord.getData() : relatedRecord;
                    }
                }
            }
        }

        return data;
    }
});
<commit_msg>Handle reading error message from exception<commit_after>Ext.define('Emergence.proxy.Records', {
    extend: 'Jarvus.proxy.API',
    alias: 'proxy.records',
    alternateClassName: 'Emergence.ext.proxy.Records',
    requires: [
        'Emergence.util.API',
        'Ext.data.reader.Json',
        'Ext.data.writer.Json',
        'Ext.data.Request',
        'Ext.util.Collection'
    ],

    config: {
        connection: 'Emergence.util.API',
        include: null,
        relatedTable: null,
        summary: false,
        injectRelatedTables: true,
        injectModels: false,

        /**
         * @cfg The base URL for the managed collection (e.g. '/people')
         * @required
         */
        url: null,

        idParam: 'ID',
        pageParam: false,
        startParam: 'offset',
        limitParam: 'limit',
        sortParam: 'sort',
        directionParam: 'dir',
        filterParam: 'q',
        simpleSortMode: true,
        reader: {
            type: 'json',
            rootProperty: 'data',
            totalProperty: 'total',
            messageProperty: 'message',
            keepRawData: true
        },
        writer: {
            type: 'json',
            rootProperty: 'data',
            writeAllFields: false,
            allowSingle: false
        }
    },

    constructor: function() {
        this.relatedCollections = {};
        this.callParent(arguments);
    },

    // config handlers
    applyInclude: function(include) {
        if (!include || !include.length) {
            return null;
        }

        return typeof include == 'string' ? [include] : include;
    },

    updateInclude: function(include) {
        this._includeParam = include ? include.join(',') : null;
    },

    applyRelatedTable: function(relatedTable) {
        var relatedTableConfigs = [],
            length, i = 0, config, relationship, model;

        if (!relatedTable || !relatedTable.length) {
            return null;
        }

        if (!Ext.isArray(relatedTable)) {
            relatedTable = [relatedTable];
        }

        length = relatedTable.length;
        for (; i < length; i++) {
            config = relatedTable[i];

            if (typeof config == 'string') {
                config = {
                    relationship: config
                };
            }

            relationship = config.relationship;

            if (!relationship) {
                Ext.Logger.error('relatedTable config missing required attribute relationship');
            }

            model = config.model;

            if (typeof model == 'string') {
                Ext.syncRequire(model);
                model = config.model = Ext.ClassManager.get(model);
            }

            if (!config.foreignKey) {
                config.foreignKey = model ? model.idProperty : 'ID';
            }

            if (!config.localKey) {
                config.localKey = relationship + 'ID';
            }

            relatedTableConfigs.push(config);
        }

        return relatedTableConfigs;
    },

    updateRelatedTable: function(relatedTable, oldRelatedTable) {
        var relatedCollections = this.relatedCollections,
            initializedTables = [],
            relatedLength, relatedIndex, config, relationship, Model, foreignKey;

        this._relatedTableParam = relatedTable ? Ext.Array.pluck(relatedTable, 'relationship').join(',') : null;

        if (relatedTable) {
            relatedLength = relatedTable.length;
            relatedIndex = 0;
            for (; relatedIndex < relatedLength; relatedIndex++) {
                config = relatedTable[relatedIndex];
                relationship = config.relationship;
                Model = config.model;
                foreignKey = config.foreignKey;

                relatedCollections[relationship] = new Ext.util.Collection({
                    // eslint-disable-next-line no-loop-func
                    decoder: Model ? function(data) {
                        return new Model(data);
                    } : null,
                    // eslint-disable-next-line no-loop-func
                    keyFn: Model ? null : function(data) {
                        return data[foreignKey];
                    }
                });

                initializedTables.push(relationship);
            }
        }

        if (oldRelatedTable) {
            relatedLength = oldRelatedTable.length;
            relatedIndex = 0;
            for (; relatedIndex < relatedLength; relatedIndex++) {
                relationship = oldRelatedTable[relatedIndex].relationship;

                if (initializedTables.indexOf(relationship) == -1) {
                    delete relatedCollections[relationship];
                }
            }
        }
    },

    /**
     * TODO: overriding this entire method may no longer be necessary given the new Jarvus.proxy.API's template methods
     */
    buildRequest: function(operation) {
        var me = this,
            initialParams = Ext.apply({}, typeof operation.getParams == 'function' ? operation.getParams() : operation.params),
            // Clone params right now so that they can be mutated at any point further down the call stack
            params = Ext.applyIf(initialParams, typeof me.getExtraParams == 'function' ? me.getExtraParams() : me.extraParams || {}),
            request = new Ext.data.Request({
                action: typeof operation.getAction == 'function' ? operation.getAction() : operation.action,
                records: operation.getRecords(),
                operation: operation,
                params: Ext.applyIf(params, me.getParams(operation))
            });

        request.setUrl(operation.getUrl() || me.buildUrl(request));
        request.setMethod(me.getMethod(request));
        request.setHeaders(me.getHeaders(request));
        request.setTimeout(me.getTimeout(request));
        request.setWithCredentials(me.getWithCredentials());

        // compatibility with Jarvus.ext.override.proxy.DirtyParams since we're entirely replacing the buildRequest method it overrides
        if (typeof me.clearParamsDirty == 'function') {
            me.clearParamsDirty();
        }

        operation.setRequest(request);

        return request;
    },

    buildUrl: function(request) {
        var me = this,
            baseUrl = me.getUrl(request),
            action = typeof request.getAction == 'function' ? request.getAction() : request.action,
            operation, id, idParam, handleParam;

        switch (action) {
            case 'read':
                operation = typeof request.getOperation == 'function' ? request.getOperation() : request.operation;

                // use recordHandle if provided
                id = operation.recordHandle;
                if (id) {
                    baseUrl += '/' + encodeURIComponent(id);
                    break;
                }

                // apply id to path if idProperty is ID or Handle
                id = typeof operation.getId == 'function' ? operation.getId() : operation.id;
                idParam = typeof me.getIdParam == 'function'? me.getIdParam() : me.idParam;
                handleParam = (typeof me.getModel == 'function' ? me.getModel() : me.model).handleProperty || 'Handle';
                if (id && ((idParam == 'ID' && id > 0) || idParam == handleParam)) {
                    baseUrl += '/' + encodeURIComponent(id);
                }

                break;
            case 'create':
            case 'update':
                baseUrl += '/save';
                break;
            case 'destroy':
                baseUrl += '/destroy';
                break;
            default:
                Ext.Logger.error('Unhandled request action');
        }

        return baseUrl;
    },

    getParams: function(operation) {
        var me = this,
            includeParam = operation.include,
            relatedTableParam = me._relatedTableParam,
            summary = me.getSummary(),
            idParam = me.idParam,
            id = typeof operation.getId == 'function' ? operation.getId() : operation.id,
            params = me.callParent(arguments);

        if (id && idParam != 'ID') {
            params[idParam] = id;
        }

        if (includeParam) {
            if (Ext.isArray(includeParam)) {
                includeParam = includeParam.join(',');
            }
        } else {
            includeParam = me._includeParam;
        }

        if (includeParam) {
            params.include = includeParam;
        }

        if (relatedTableParam) {
            params.relatedTable = relatedTableParam;
        }

        if (summary) {
            params.summary = 'true';
        }

        return params;
    },

    encodeFilters: function(filters) {
        var out = [],
            length = filters.length,
            i = 0, filterData, filterValue;

        for (; i < length; i++) {
            filterData = filters[i].serialize();
            filterValue = filterData.value.toString();
            out[i] = filterData.property+ ':' + (filterValue.match(/\s/) ? '"' + filterValue + '"' : filterValue);
        }

        return out.join(' ');
    },

    getMethod: function(request) {
        switch (request.getAction()) {
            case 'read':
                return 'GET';
            case 'create':
            case 'update':
            case 'destroy':
                return 'POST';
            default:
                Ext.Logger.error('Unhandled request action');
                return null;
        }
    },

    extractResponseData: function() {
        var me = this,
            relatedCollections = me.relatedCollections,
            relatedTableConfigs = me.getRelatedTable(),
            injectRelatedTables = me.getInjectRelatedTables(),
            injectModels = me.getInjectModels(),
            rootProperty = me.getReader().getRootProperty(),
            data = me.callParent(arguments),
            relatedData = data.related,
            length, i = 0, config, relationship, relatedCollection, localKey,
            recordsData, recordsLength, recordIndex, recordData, relatedRecord;

        if (relatedTableConfigs && relatedData) {
            length = relatedTableConfigs.length;

            for (; i < length; i++) {
                config = relatedTableConfigs[i];
                relationship = config.relationship;
                relatedCollection = relatedCollections[relationship];
                relatedCollection.add(relatedData[relationship]);

                if (injectRelatedTables) {
                    localKey = config.localKey;
                    recordsData = rootProperty ? data[rootProperty] : data;
                    recordsLength = recordsData.length;
                    recordIndex = 0;

                    for (; recordIndex < recordsLength; recordIndex++) {
                        recordData = recordsData[recordIndex];
                        relatedRecord = relatedCollection.getByKey(recordData[localKey]);
                        recordData[relationship] = !injectModels && relatedRecord instanceof Ext.data.Model ? relatedRecord.getData() : relatedRecord;
                    }
                }
            }
        }

        return data;
    },

    setException: function(operation, response) {
        var status = response.status,
            responseData = response.data,
            message = responseData && responseData.message;

        if (!message) {
            if (status == 400) {
                message = 'The server rejected this operation, please check your input and try again. If this problem persists, please backup your work to another application and report this to your technical support contact';
            } else {
                message = 'This operation failed for an unexpected reason, please check your input and try again. If this problem persists, please backup your work to another application and report this to your technical support contact';
            }
        }

        operation.setException(message);
    }
});
<|endoftext|>


<commit_before><commit_msg>removed wids multi reference to docs in which the user is involved with.<commit_after><|endoftext|>


<commit_before><commit_msg>Expose underlying express lib for convenience<commit_after><|endoftext|>


<commit_before><commit_msg>Parse args for consistency sake.<commit_after><|endoftext|>


<commit_before><commit_msg>fix callback and added related<commit_after><|endoftext|>


<commit_before><commit_msg>removed unecessary comments<commit_after><|endoftext|>