<commit_before>// vim: set ts=2 sw=2 et colorcolumn=100 :

/**
 * Namespace
 * @namespace
 */
var geochart_geojson = {};

(function(context) {

context.CONSTANTS = {
  // zIndex of selected or highlighted features
  // The selected (click) and highlighted (hover) features must have a zIndex higher than
  // the other features.
  selectedZIndex: 999,
  highlightedZIndex: 1000,
  // zIndex of the tooltip
  // The tooltip must have a zIndex higher than the features and the selected and highlighted
  // features.
  tooltipZIndex: 2000
}

/**
 * GeoChart with GeoJSON support
 * @class
 *
 * These charts are very similar to the Google Charts geochart, but with GeoJSON support.
 *
 * Code based on many Google Charts and Google Maps API guides and references.
 *
 * See:
 *
 * - https://developers.google.com/chart/interactive/docs/dev/
 * - https://developers.google.com/chart/interactive/docs/gallery/geochart
 * - https://developers.google.com/chart/interactive/docs/datatables_dataviews
 * - https://developers.google.com/chart/interactive/docs/reference
 * - https://developers.google.com/maps/documentation/javascript/
 *
 * @param {object} container - The HTML container for the chart.
 */
context.GeoChart = function(container) {
  this.container = container;

  this.data_ = null;
  this.options_ = null;
  // The inner Google Maps map object
  // This object will hold the GeoJSON map (in its overlay layer), the tooltip overlay and the
  // legend control.
  // Optionally, it will also handle the underlying map layer (map, satellite or simple map) and
  // the map control (zoom, map drag). These features are disabled by default, so the
  this.maps_map_ = null;
  this.tooltip_ = null;
  this.legend_ = null;
  // Min and max values of the DataTable rows
  // Used in the gradient color generation.
  this.min_ = 0;
  this.max_ = 0;
  // Feature selected by the user
  this.feature_selected_ = null;
}

context.GeoChart.prototype.DEFAULT_OPTIONS = {
  mapsOptions: null,
  mapsBackground: "none",
  mapsControl: false,
  featuresStyle: {
    fillColor: "#f5f5f5",
    strokeColor: "#dddddd",
    fillOpacity: 1,
    strokeWeight: 1
  },
  featuresHighlightedStyle: {
    strokeWeight: 3,
    strokeOpacity: 1,
  },
  featuresGradientColors: ["#efe6dc", "#109618"],
  featuresGradientStrokeColors: ["#d7cfc6", "0e8716"],
  tooltip: {
    borderStyle: "solid",
    borderWidth: "1px",
    borderColor: "#cccccc",
    backgroundColor: "#ffffff",
    padding: "4px",
    textStyle: {
      color: "#000000",
      fontFamily: "Arial",
      fontSize: "13px",
      margin: "2px"
    }
  },
  tooltipOffset: 12
}

// TODO Implement other `mapsBackground` and `mapsControl` options
context.GeoChart.prototype.getMapsOptions_ = function() {
  var maps_options = this.options_.mapsOptions;

  if (this.options_.mapsBackground === "none") {
    maps_options["styles"] = [{
      "stylers": [{"visibility": "off"}]
    }];
    maps_options["backgroundColor"] = "none";
  } else {
    throw "Invalid `mapsBackground` option";
  }

  if (this.options_.mapsControl === false) {
    maps_options["disableDefaultUI"] = true;
    maps_options["scrollwheel"] = false;
    maps_options["draggable"] = false;
    maps_options["disableDoubleClickZoom"] = true;
  } else {
    throw "Invalid `mapsControl` option";
  }

  return maps_options;
}

context.GeoChart.prototype.draw = function(data, options={}) {
  this.data_ = data;
  // FIXME This doesn't run a deep copy.
  // See: https://stackoverflow.com/questions/27936772/how-to-deep-merge-instead-of-shallow-merge
  this.options_ = Object.assign({}, context.GeoChart.prototype.DEFAULT_OPTIONS, options);

  var this_ = this;

  // Create the Google Maps object
  var maps_options = this.getMapsOptions_();
  // TODO We could implement custom zooming when mapsBackground = "none" using custom
  // projections.
  // See: https://groups.google.com/forum/#!topic/google-maps-js-api-v3/AbOHZlLQLCg
  this.map_ = new google.maps.Map(this.container, maps_options);

  // Load the GeoJSON data
  this.map_.data.loadGeoJson(
      this.options_.geoJson, this.options_.geoJsonOptions,
      function(features) {
        var min = Number.MAX_VALUE;
        var max = -Number.MAX_VALUE;

        // Populate the feature "data-" properties
        for (var row = 0; row < data.getNumberOfRows(); row++) {
          var id = data.getValue(row, 0);
          var value = data.getValue(row, 1);
          var feature = this_.map_.data.getFeatureById(id);

          // Keep track of min and max values
          if (value < min) {
            min = value;
          }
          if (value > max) {
            max = value;
          }
          feature.setProperty("data-row", row);
          feature.setProperty("data-id", id);
          feature.setProperty("data-label", data.getColumnLabel(1));
          feature.setProperty("data-value", value);
        }
        this_.min_ = min;
        this_.max_ = max;

        // Create the tooltip
        google.maps.event.addListener(this_.map_, "tooltip-ready", function() {

          // Trigger the ready event
          // See: https://developers.google.com/chart/interactive/docs/dev/events#the-ready-event
          google.visualization.events.trigger(this_, "ready", null);
        });
        this_.tooltip_ = new context.Tooltip(this_);
      }
  );

  // Define the feature styles
  this.map_.data.setStyle(function(feature) {
    // Default style
    var style = Object.assign(
        {}, {cursor: "default"},
        this_.DEFAULT_OPTIONS.featuresStyle);

    // Feature with data style
    // Colorize the features with data (using the gradient colors)
    if (feature.getProperty("data-value") !== undefined) {
      var fill_color_arr = [];
      var stroke_color_arr = [];

      var gradient_colors_arr = [
          this_.getColorArray_(this_.DEFAULT_OPTIONS.featuresGradientColors[0]),
          this_.getColorArray_(this_.DEFAULT_OPTIONS.featuresGradientColors[1])
      ];
      var gradient_stroke_colors_arr = [
          this_.getColorArray_(this_.DEFAULT_OPTIONS.featuresGradientStrokeColors[0]),
          this_.getColorArray_(this_.DEFAULT_OPTIONS.featuresGradientStrokeColors[1])
      ];
      var relative_value = this_.getRelativeValue_(feature.getProperty("data-value"));

      for (var i = 0; i < 3; i++) {
        fill_color_arr[i] =
            (gradient_colors_arr[1][i] - gradient_colors_arr[0][i]) * relative_value +
            gradient_colors_arr[0][i];
        stroke_color_arr[i] =
            ((gradient_stroke_colors_arr[1][i] - gradient_stroke_colors_arr[0][i]) *
            relative_value) + gradient_stroke_colors_arr[0][i];
      }

      style = Object.assign(style, {
        fillColor: "rgb(" + fill_color_arr[0] + ", " + fill_color_arr[1] + ", " +
            fill_color_arr[2] + ")",
        strokeColor: "rgb(" + stroke_color_arr[0] + ", " + stroke_color_arr[1] + ", " +
            stroke_color_arr[2] + ")"
      });

      // Selected feature style
      if (feature.getProperty("data-selected") === true) {
        style = Object.assign(
            style, this_.DEFAULT_OPTIONS.featuresHighlightedStyle,
            {zIndex: context.CONSTANTS.selectedZIndex}
        );
      }
    }

    return style;
  });

  // Mouse event handlers

  this.map_.data.addListener("mouseover", function(event) {
    var highlighted_style = Object.assign(
        {}, this_.DEFAULT_OPTIONS.featuresHighlightedStyle,
        {zIndex: context.CONSTANTS.highlightedZIndex});

    if (event.feature !== this_.feature_selected_) {
        this_.map_.data.revertStyle();
        this_.map_.data.overrideStyle(event.feature, highlighted_style);
    }
    if (event.feature.getProperty("data-value") !== undefined) {
      this_.tooltip_.drawTooltip(event.feature, event.latLng);
    }
  });

  this.map_.data.addListener("mouseout", function(event) {
    if (event.feature !== this_.feature_selected_) {
        this_.map_.data.revertStyle();
    }
  });

  this.map_.data.addListener("click", function(event) {
    this_.map_.data.revertStyle();
    if (event.feature !== this_.feature_selected_) {
      if (event.feature.getProperty("data-value") !== undefined) {
        this_.selectFeature_(event.feature);
      } else {
        this_.unselectFeature_(event.feature);
      }
    }
  });

  this.map_.addListener("click", function(event) {
    this_.map_.data.revertStyle();
    this_.unselectFeature_(event.feature);
  });

}

// Based on: https://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb
context.GeoChart.prototype.getColorArray_ = function(color) {
  var short_regex = /^#?([\da-f])([\da-f])([\da-f])$/i;
  var regex = /^#?([\da-f]{2})([\da-f]{2})([\da-f]{2})$/i;

  var color_array = null;

  color = color.replace(short_regex, function(m, r, g, b) {
    return "#" + r + r + g + g + b + b;
  });
  var result = regex.exec(color);
  if (! result) {
    throw "Invalid color string";
  }
  color_array = [parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16)];

  return color_array;
}

context.GeoChart.prototype.getRelativeValue_ = function(value) {
  return (value - this.min_) / (this.max_ - this.min_);
}

// Entry selected by the user
// See: https://developers.google.com/chart/interactive/docs/reference#getselection
context.GeoChart.prototype.getSelection = function() {
  if (! this.feature_selected_) {
    return [];
  } else {
    return [{row: this.feature_selected_.getProperty("data-row"), column: null}];
  }
}

context.GeoChart.prototype.setSelection = function(selection) {
  var id = "";
  var feature = null;

  // Implemented only for zero and single row selections
  if (! selection.length) {
    this.unselectFeature_();
  } else if (selection.length === 1) {
      id = this.data_.getValue(selection[0].row, 0);
      feature = this.map_.data.getFeatureById(id);
      this.selectFeature_(feature);
  }
}

context.GeoChart.prototype.selectFeature_ = function(feature) {
  this.unselectFeature_();
  this.feature_selected_ = feature;
  this.feature_selected_.setProperty("data-selected", true);
}

context.GeoChart.prototype.unselectFeature_ = function() {
  if (this.feature_selected_) {
    this.feature_selected_.removeProperty("data-selected");
    this.feature_selected_ = null;
  }
}

/**
 * Tooltip for GeoChart GeoJSON
 * @class
 *
 * It's an overlay layer to be placed on a Google Maps map.
 *
 * @param {object} geoChart - The GeoChart GeoJSON object where the tooltip will be placed.
 */
context.Tooltip = function(geoChart) {
  this.geo_chart_ = geoChart;

  this.div_ = null;
  this.id_span_ = null;
  this.label_span_ = null;
  this.value_span_ = null;

  this.LatLng = null;

  this.setMap(geoChart.map_);
}

context.Tooltip.prototype = new google.maps.OverlayView();

context.Tooltip.prototype.onAdd = function() {
  // Create the main div
  var div = document.createElement("div");
  var div_style = {};
  div_style = Object.assign(
      {}, {position: "absolute", visibility: "hidden"},
      this.geo_chart_.options_.tooltip,
      {zIndex: context.CONSTANTS.tooltipZIndex});
  delete div_style.textStyle;
  Object.assign(div.style, div_style);

  var p_style = Object.assign({}, this.geo_chart_.options_.tooltip.textStyle);

  // Create the first inner paragraph
  var p1 = document.createElement("p");
  Object.assign(p1.style, p_style);
  var id_span = document.createElement("span");
  id_span.style.fontWeight = "bold";
  p1.appendChild(id_span);
  div.appendChild(p1);

  // Create the second inner paragraph
  var p2 = document.createElement("p");
  Object.assign(p2.style, p_style);
  var label_span = document.createElement("span");
  var value_span = document.createElement("span");
  value_span.style.fontWeight = "bold";
  p2.appendChild(label_span);
  p2.appendChild(document.createTextNode(": "));
  p2.appendChild(value_span);
  div.appendChild(p2);

  this.div_ = div;
  this.id_span_ = id_span;
  this.label_span_ = label_span;
  this.value_span_ = value_span;

  this.getPanes().overlayLayer.appendChild(div);
}

context.Tooltip.prototype.draw = function() {
  // Do not draw nothing at first
  // Only trigger an event
  google.maps.event.trigger(this.map_, 'tooltip-ready');
}

context.Tooltip.prototype.drawTooltip = function(feature, latLng) {
  // Calculate positioning
  var s = this.geo_chart_.options_.tooltipOffset;
  var px = this.getProjection().fromLatLngToDivPixel(latLng);
  var w = this.div_.offsetWidth;
  var h = this.div_.offsetHeight;
  var top = 0;
  var left = 0;

  // Start with div up and left
  top = px.y - s - h;
  left = px.x - s - w;
  // Change div side if necessary
  if (top < 0) {
    top = px.y + s;
  }
  if (left < 0) {
    left = px.x + s;
  }

  // Set text and positioning
  this.id_span_.innerText = feature.getId();
  this.label_span_.innerText = feature.getProperty("data-label");
  this.value_span_.innerText = feature.getProperty("data-value");
  this.div_.style.top = top + 'px';
  this.div_.style.left = left + 'px';
  this.div_.style.visibility = "visible";
}

context.Tooltip.prototype.undrawTooltip = function() {
  this.div_.style.visibility = "hidden";
}

})(geochart_geojson);
<commit_msg>Fix in tooltip and tooltip events<commit_after>// vim: set ts=2 sw=2 et colorcolumn=100 :

/**
 * Namespace
 * @namespace
 */
var geochart_geojson = {};

(function(context) {

context.CONSTANTS = {
  // zIndex of selected or highlighted features
  // The selected (click) and highlighted (hover) features must have a zIndex higher than
  // the other features.
  selectedZIndex: 999,
  highlightedZIndex: 1000,
  // zIndex of the tooltip
  // The tooltip must have a zIndex higher than the features and the selected and highlighted
  // features.
  tooltipZIndex: 2000
}

/**
 * GeoChart with GeoJSON support
 * @class
 *
 * These charts are very similar to the Google Charts geochart, but with GeoJSON support.
 *
 * Code based on many Google Charts and Google Maps API guides and references.
 *
 * See:
 *
 * - https://developers.google.com/chart/interactive/docs/dev/
 * - https://developers.google.com/chart/interactive/docs/gallery/geochart
 * - https://developers.google.com/chart/interactive/docs/datatables_dataviews
 * - https://developers.google.com/chart/interactive/docs/reference
 * - https://developers.google.com/maps/documentation/javascript/
 *
 * @param {object} container - The HTML container for the chart.
 */
context.GeoChart = function(container) {
  this.container = container;

  this.data_ = null;
  this.options_ = null;
  // The inner Google Maps map object
  // This object will hold the GeoJSON map (in its overlay layer), the tooltip overlay and the
  // legend control.
  // Optionally, it will also handle the underlying map layer (map, satellite or simple map) and
  // the map control (zoom, map drag). These features are disabled by default, so the
  this.maps_map_ = null;
  this.tooltip_ = null;
  this.legend_ = null;
  // Min and max values of the DataTable rows
  // Used in the gradient color generation.
  this.min_ = 0;
  this.max_ = 0;
  // Feature selected by the user
  this.feature_selected_ = null;
}

context.GeoChart.prototype.DEFAULT_OPTIONS = {
  mapsOptions: null,
  mapsBackground: "none",
  mapsControl: false,
  featuresStyle: {
    fillColor: "#f5f5f5",
    strokeColor: "#dddddd",
    fillOpacity: 1,
    strokeWeight: 1
  },
  featuresHighlightedStyle: {
    strokeWeight: 3,
    strokeOpacity: 1,
  },
  featuresGradientColors: ["#efe6dc", "#109618"],
  featuresGradientStrokeColors: ["#d7cfc6", "0e8716"],
  tooltip: {
    borderStyle: "solid",
    borderWidth: "1px",
    borderColor: "#cccccc",
    backgroundColor: "#ffffff",
    padding: "4px",
    textStyle: {
      color: "#000000",
      fontFamily: "Arial",
      fontSize: "13px",
      margin: "2px"
    }
  },
  tooltipOffset: 12
}

// TODO Implement other `mapsBackground` and `mapsControl` options
context.GeoChart.prototype.getMapsOptions_ = function() {
  var maps_options = this.options_.mapsOptions;

  if (this.options_.mapsBackground === "none") {
    maps_options["styles"] = [{
      "stylers": [{"visibility": "off"}]
    }];
    maps_options["backgroundColor"] = "none";
  } else {
    throw "Invalid `mapsBackground` option";
  }

  if (this.options_.mapsControl === false) {
    maps_options["disableDefaultUI"] = true;
    maps_options["scrollwheel"] = false;
    maps_options["draggable"] = false;
    maps_options["disableDoubleClickZoom"] = true;
  } else {
    throw "Invalid `mapsControl` option";
  }

  return maps_options;
}

context.GeoChart.prototype.draw = function(data, options={}) {
  this.data_ = data;
  // FIXME This doesn't run a deep copy.
  // See: https://stackoverflow.com/questions/27936772/how-to-deep-merge-instead-of-shallow-merge
  this.options_ = Object.assign({}, context.GeoChart.prototype.DEFAULT_OPTIONS, options);

  var this_ = this;

  // Create the Google Maps object
  var maps_options = this.getMapsOptions_();
  // TODO We could implement custom zooming when mapsBackground = "none" using custom
  // projections.
  // See: https://groups.google.com/forum/#!topic/google-maps-js-api-v3/AbOHZlLQLCg
  this.map_ = new google.maps.Map(this.container, maps_options);

  // Load the GeoJSON data
  this.map_.data.loadGeoJson(
      this.options_.geoJson, this.options_.geoJsonOptions,
      function(features) {
        var min = Number.MAX_VALUE;
        var max = -Number.MAX_VALUE;

        // Populate the feature "data-" properties
        for (var row = 0; row < data.getNumberOfRows(); row++) {
          var id = data.getValue(row, 0);
          var value = data.getValue(row, 1);
          var feature = this_.map_.data.getFeatureById(id);

          // Keep track of min and max values
          if (value < min) {
            min = value;
          }
          if (value > max) {
            max = value;
          }
          feature.setProperty("data-row", row);
          feature.setProperty("data-id", id);
          feature.setProperty("data-label", data.getColumnLabel(1));
          feature.setProperty("data-value", value);
        }
        this_.min_ = min;
        this_.max_ = max;

        // Create the tooltip
        google.maps.event.addListener(this_.map_, "tooltip-ready", function() {

          // Trigger the ready event
          // See: https://developers.google.com/chart/interactive/docs/dev/events#the-ready-event
          google.visualization.events.trigger(this_, "ready", null);
        });
        this_.tooltip_ = new context.Tooltip(this_);
      }
  );

  // Define the feature styles
  this.map_.data.setStyle(function(feature) {
    // Default style
    var style = Object.assign(
        {}, {cursor: "default"},
        this_.DEFAULT_OPTIONS.featuresStyle);

    // Feature with data style
    // Colorize the features with data (using the gradient colors)
    if (feature.getProperty("data-value") !== undefined) {
      var fill_color_arr = [];
      var stroke_color_arr = [];

      var gradient_colors_arr = [
          this_.getColorArray_(this_.DEFAULT_OPTIONS.featuresGradientColors[0]),
          this_.getColorArray_(this_.DEFAULT_OPTIONS.featuresGradientColors[1])
      ];
      var gradient_stroke_colors_arr = [
          this_.getColorArray_(this_.DEFAULT_OPTIONS.featuresGradientStrokeColors[0]),
          this_.getColorArray_(this_.DEFAULT_OPTIONS.featuresGradientStrokeColors[1])
      ];
      var relative_value = this_.getRelativeValue_(feature.getProperty("data-value"));

      for (var i = 0; i < 3; i++) {
        fill_color_arr[i] =
            (gradient_colors_arr[1][i] - gradient_colors_arr[0][i]) * relative_value +
            gradient_colors_arr[0][i];
        stroke_color_arr[i] =
            ((gradient_stroke_colors_arr[1][i] - gradient_stroke_colors_arr[0][i]) *
            relative_value) + gradient_stroke_colors_arr[0][i];
      }

      style = Object.assign(style, {
        fillColor: "rgb(" + fill_color_arr[0] + ", " + fill_color_arr[1] + ", " +
            fill_color_arr[2] + ")",
        strokeColor: "rgb(" + stroke_color_arr[0] + ", " + stroke_color_arr[1] + ", " +
            stroke_color_arr[2] + ")"
      });

      // Selected feature style
      if (feature.getProperty("data-selected") === true) {
        style = Object.assign(
            style, this_.DEFAULT_OPTIONS.featuresHighlightedStyle,
            {zIndex: context.CONSTANTS.selectedZIndex}
        );
      }
    }

    return style;
  });

  // Mouse event handlers

  this.map_.data.addListener("mouseover", function(event) {
    var highlighted_style = Object.assign(
        {}, this_.DEFAULT_OPTIONS.featuresHighlightedStyle,
        {zIndex: context.CONSTANTS.highlightedZIndex});

    if (event.feature !== this_.feature_selected_) {
        this_.map_.data.revertStyle();
        this_.map_.data.overrideStyle(event.feature, highlighted_style);
    }
  });

  this.map_.data.addListener("mouseout", function(event) {
    if (event.feature !== this_.feature_selected_) {
        this_.map_.data.revertStyle();
    }
    this_.tooltip_.undrawTooltip();
  });

  this.map_.data.addListener("mousemove", function(event) {
    if (event.feature.getProperty("data-value") !== undefined) {
      this_.tooltip_.drawTooltip(event.feature, event.latLng);
    }
  });

  this.map_.data.addListener("click", function(event) {
    this_.map_.data.revertStyle();
    if (event.feature !== this_.feature_selected_) {
      if (event.feature.getProperty("data-value") !== undefined) {
        this_.selectFeature_(event.feature);
      } else {
        this_.unselectFeature_();
      }
    }
  });

  this.map_.addListener("click", function(event) {
    this_.map_.data.revertStyle();
    this_.unselectFeature_();
  });

}

// Based on: https://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb
context.GeoChart.prototype.getColorArray_ = function(color) {
  var short_regex = /^#?([\da-f])([\da-f])([\da-f])$/i;
  var regex = /^#?([\da-f]{2})([\da-f]{2})([\da-f]{2})$/i;

  var color_array = null;

  color = color.replace(short_regex, function(m, r, g, b) {
    return "#" + r + r + g + g + b + b;
  });
  var result = regex.exec(color);
  if (! result) {
    throw "Invalid color string";
  }
  color_array = [parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16)];

  return color_array;
}

context.GeoChart.prototype.getRelativeValue_ = function(value) {
  return (value - this.min_) / (this.max_ - this.min_);
}

// Entry selected by the user
// See: https://developers.google.com/chart/interactive/docs/reference#getselection
context.GeoChart.prototype.getSelection = function() {
  if (! this.feature_selected_) {
    return [];
  } else {
    return [{row: this.feature_selected_.getProperty("data-row"), column: null}];
  }
}

context.GeoChart.prototype.setSelection = function(selection) {
  var id = "";
  var feature = null;

  // Implemented only for zero and single row selections
  if (! selection.length) {
    this.unselectFeature_();
  } else if (selection.length === 1) {
      id = this.data_.getValue(selection[0].row, 0);
      feature = this.map_.data.getFeatureById(id);
      this.selectFeature_(feature);
  }
}

context.GeoChart.prototype.selectFeature_ = function(feature) {
  this.unselectFeature_();
  this.feature_selected_ = feature;
  this.feature_selected_.setProperty("data-selected", true);
}

context.GeoChart.prototype.unselectFeature_ = function() {
  if (this.feature_selected_) {
    this.feature_selected_.removeProperty("data-selected");
    this.feature_selected_ = null;
  }
}

/**
 * Tooltip for GeoChart GeoJSON
 * @class
 *
 * It's an overlay layer to be placed on a Google Maps map.
 *
 * @param {object} geoChart - The GeoChart GeoJSON object where the tooltip will be placed.
 */
context.Tooltip = function(geoChart) {
  this.geo_chart_ = geoChart;

  this.div_ = null;
  this.id_span_ = null;
  this.label_span_ = null;
  this.value_span_ = null;

  this.LatLng = null;

  this.setMap(geoChart.map_);
}

context.Tooltip.prototype = new google.maps.OverlayView();

context.Tooltip.prototype.onAdd = function() {
  // Create the main div
  var div = document.createElement("div");
  var div_style = {};
  div_style = Object.assign(
      {}, {position: "absolute", visibility: "hidden"},
      this.geo_chart_.options_.tooltip,
      {zIndex: context.CONSTANTS.tooltipZIndex});
  delete div_style.textStyle;
  Object.assign(div.style, div_style);

  var p_style = Object.assign({}, this.geo_chart_.options_.tooltip.textStyle);

  // Create the first inner paragraph
  var p1 = document.createElement("p");
  Object.assign(p1.style, p_style);
  var id_span = document.createElement("span");
  id_span.style.fontWeight = "bold";
  p1.appendChild(id_span);
  div.appendChild(p1);

  // Create the second inner paragraph
  var p2 = document.createElement("p");
  Object.assign(p2.style, p_style);
  var label_span = document.createElement("span");
  var value_span = document.createElement("span");
  value_span.style.fontWeight = "bold";
  p2.appendChild(label_span);
  p2.appendChild(document.createTextNode(": "));
  p2.appendChild(value_span);
  div.appendChild(p2);

  this.div_ = div;
  this.id_span_ = id_span;
  this.label_span_ = label_span;
  this.value_span_ = value_span;

  this.getPanes().overlayLayer.appendChild(div);
}

context.Tooltip.prototype.draw = function() {
  // Do not draw nothing at first
  // Only trigger an event
  google.maps.event.trigger(this.map_, 'tooltip-ready');
}

context.Tooltip.prototype.drawTooltip = function(feature, latLng) {
  // Update text
  var id = feature.getId();
  if (id !== this.id_span_.innerText) {
    this.id_span_.innerText = id;
    this.label_span_.innerText = feature.getProperty("data-label");
    this.value_span_.innerText = feature.getProperty("data-value");
  }

  // Calculate positioning
  var s = this.geo_chart_.options_.tooltipOffset;
  var px = this.getProjection().fromLatLngToDivPixel(latLng);
  var w = this.div_.offsetWidth;
  var h = this.div_.offsetHeight;
  var top = 0;
  var left = 0;
  // Start with div up and left
  top = px.y - s - h;
  left = px.x - s - w;
  // Change div side if necessary
  if (top < 0) {
    top = px.y + s;
  }
  if (left < 0) {
    left = px.x + s;
  }
  this.div_.style.top = top;
  this.div_.style.left = left;

  this.div_.style.visibility = "visible";
}

context.Tooltip.prototype.undrawTooltip = function() {
  this.div_.style.visibility = "hidden";
}

})(geochart_geojson);
<|endoftext|>


<commit_before>/* eslint-disable max-len, no-else-return */

// the collatz conjecture
// https://youtu.be/VrrnjYgDBEk?t=729
// http://www.cs.utep.edu/vladik/cs2401.10a/Ch_14_Recursion.pdf

// http://www.w3resource.com/javascript-exercises/javascript-recursion-functions-exercises.php
// http://www.w3resource.com/c-programming-exercises/recursion/index.php
// https://roman01la.github.io/recursion-exercises/
// https://cscircles.cemc.uwaterloo.ca/16-recursion/
// http://www.bowdoin.edu/~ltoma/teaching/cs107/spring05/recursion.html
// http://codingbat.com/java/Recursion-1
// http://www.cse.chalmers.se/edu/year/2016/course/TDA555/ex-week2.html
// https://www.inf.unibz.it/~calvanese/teaching/04-05-ip/exercises/recursion/strings/
// http://www.geeksforgeeks.org/category/algorithm/recursion/
// https://www.hackerrank.com/domains/fp/fp-recursion
// http://www.cs.wustl.edu/~kjg/cse131/modules/recursion/lab.html
// http://anandology.com/python-practice-book/functional-programming.html

// Implement Ruby's `select` in JavaScript recursively.
// Example: iterating over [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] and only returning even numbers.
export const arraySelectEven = (array) => {
  // eslint-disable-next-line
  console.log('evenNumbers')
  return array
}

// Implement Ruby's `select` in JavaScript recursively.
// Example: iterating over [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] and only returning even numbers.
//
// Use FN to determine which to actually select. Is applied ONLY on one element at a time.
export const arraySelect = (array, fn) => {
  // eslint-disable-next-line
  console.log('evenNumbers')

  // eslint-disable-next-line
  console.log('user func', fn)
  return array
}

// Implement Ruby's `drop_while` in JavaScript recursively.
// Example: iterating over [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] and only returning integers greater than 7.
// drop_while: http://ruby-doc.org/core-2.2.3/Enumerable.html#method-i-drop
export const dropWhile = (array) => (
  array
)

/**
 * Reverse a string.
 *
 * The substr() method returns the characters in a string beginning at the specified
 * location through the specified number of characters.
 *
 * The charAt() method returns the specified character from a string.
 *
 * So what's happening is that if we aren't at an empty string we are going to recurse
 * reverseString and pass it the a substring sans the first chracter of our current string.
 * We append the current string's character at the first character as well.
 *
 * For the world hello:
 *    recursionReverse('hello')
 *    (recursionReverse('ello') + 'h')
 *    ((recursionReverse('llo') + 'e') + 'h')
 *    (((recursionReverse('lo') + 'l') + 'e') + 'h')
 *    ((((recursionReverse('o') + 'l') + 'l' ) + 'e') + 'h')
 *    (((('o') + 'l') + 'l' ) + 'e') + 'h')
 *
 * Another way of doing this specifically for the text 'hello' w/o using recursion.
 *     const helloReverse = () => (get_H())
 *     const get_H = () => (get_E() + 'h')
 *     const get_E = () => (get_L1() + 'e')
 *     const get_L1 = () => (get_L2() + 'l')
 *     const get_L2 = () => (get_O() + 'l')
 *     const get_O = () => ('o')
 *     helloReverse()
 *
 * What is odd is how we're doing the return in the recurisve function. This is often what
 * throws me off. We are passing in a substring for as long as we have a string left and
 * then we call ourself again. What is important here is the string.charAt(0) as this is
 * what we're using to build out the reverse string. until we reach the end. The diagram
 * above explains it best.
 *
 * @see http://bit.ly/2v4XBZE
 * @see http://eddmann.com/posts/ten-ways-to-reverse-a-string-in-javascript/ *
 * @param {string} string String to reverse.
 * @return {string}
 */
export const reverseString = (string) => {
  if (string.length === 0) {
    return string
  }

  return reverseString(string.substr(1)) + string.charAt(0)
}

/**
 * Reverse a string using a simpler ternary.
 *
 * The substr() method returns the characters in a string beginning at the specified
 * location through the specified number of characters.
 *
 * The charAt() method returns the specified character from a string.
 *
 * So what's happening is that if we aren't at an empty string we are going to recurse
 * reverseString and pass it the a substring sans the first chracter of our current string.
 * We append the current string's character at the first character as well.
 *
 * For the world hello:
 *    recursionReverse('hello')
 *    (recursionReverse('ello') + 'h')
 *    ((recursionReverse('llo') + 'e') + 'h')
 *    (((recursionReverse('lo') + 'l') + 'e') + 'h')
 *    ((((recursionReverse('o') + 'l') + 'l' ) + 'e') + 'h')
 *    (((('o') + 'l') + 'l' ) + 'e') + 'h')
 *
 * What is odd is how we're doing the return in the recurisve function. This is often what
 * throws me off. We are passing in a substring for as long as we have a string left and
 * then we call ourself again. What is important here is the string.charAt(0) as this is
 * what we're using to build out the reverse string. until we reach the end. The diagram
 * above explains it best.
 *
 * @see http://bit.ly/2v4XBZE
 * @see http://eddmann.com/posts/ten-ways-to-reverse-a-string-in-javascript/ *
 * @param {string} string String to reverse.
 * @return {string}
 */
export const reverseStringTernary = (string) => (
  (string.length === 0)
    ? string
    : reverseStringTernary(string.substr(1)) + string.charAt(0)
)
<commit_msg>An explicit description<commit_after>/* eslint-disable max-len, no-else-return */

// the collatz conjecture
// https://youtu.be/VrrnjYgDBEk?t=729
// http://www.cs.utep.edu/vladik/cs2401.10a/Ch_14_Recursion.pdf

// http://www.w3resource.com/javascript-exercises/javascript-recursion-functions-exercises.php
// http://www.w3resource.com/c-programming-exercises/recursion/index.php
// https://roman01la.github.io/recursion-exercises/
// https://cscircles.cemc.uwaterloo.ca/16-recursion/
// http://www.bowdoin.edu/~ltoma/teaching/cs107/spring05/recursion.html
// http://codingbat.com/java/Recursion-1
// http://www.cse.chalmers.se/edu/year/2016/course/TDA555/ex-week2.html
// https://www.inf.unibz.it/~calvanese/teaching/04-05-ip/exercises/recursion/strings/
// http://www.geeksforgeeks.org/category/algorithm/recursion/
// https://www.hackerrank.com/domains/fp/fp-recursion
// http://www.cs.wustl.edu/~kjg/cse131/modules/recursion/lab.html
// http://anandology.com/python-practice-book/functional-programming.html

// Implement Ruby's `select` in JavaScript recursively.
// Example: iterating over [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] and only returning even numbers.
export const arraySelectEven = (array) => {
  // eslint-disable-next-line
  console.log('evenNumbers')
  return array
}

// Implement Ruby's `select` in JavaScript recursively.
// Example: iterating over [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] and only returning even numbers.
//
// Use FN to determine which to actually select. Is applied ONLY on one element at a time.
export const arraySelect = (array, fn) => {
  // eslint-disable-next-line
  console.log('evenNumbers')

  // eslint-disable-next-line
  console.log('user func', fn)
  return array
}

// Implement Ruby's `drop_while` in JavaScript recursively.
// Example: iterating over [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] and only returning integers greater than 7.
// drop_while: http://ruby-doc.org/core-2.2.3/Enumerable.html#method-i-drop
export const dropWhile = (array) => (
  array
)

/**
 * Reverse a string.
 *
 * The substr() method returns the characters in a string beginning at the specified
 * location through the specified number of characters.
 *
 * The charAt() method returns the specified character from a string.
 *
 * So what's happening is that if we aren't at an empty string we are going to recurse
 * reverseString and pass it the a substring sans the first chracter of our current string.
 * We append the current string's character at the first character as well.
 *
 * For the world hello:
 *    recursionReverse('hello')
 *    (recursionReverse('ello') + 'h')
 *    ((recursionReverse('llo') + 'e') + 'h')
 *    (((recursionReverse('lo') + 'l') + 'e') + 'h')
 *    ((((recursionReverse('o') + 'l') + 'l' ) + 'e') + 'h')
 *    (((('o') + 'l') + 'l' ) + 'e') + 'h')
 *
 * Another way of doing this specifically for the text 'hello' w/o using recursion.
 *     const helloReverse = () => (get_H())
 *     const get_H = () => (get_E() + 'h')
 *     const get_E = () => (get_L1() + 'e')
 *     const get_L1 = () => (get_L2() + 'l')
 *     const get_L2 = () => (get_O() + 'l')
 *     const get_O = () => ('o')
 *     helloReverse()
 *
 * What is odd is how we're doing the return in the recurisve function. This is often what
 * throws me off. We are passing in a substring for as long as we have a string left and
 * then we call ourself again. What is important here is the string.charAt(0) as this is
 * what we're using to build out the reverse string. until we reach the end. The diagram
 * above explains it best.
 *
 * @see http://bit.ly/2v4XBZE
 * @see http://eddmann.com/posts/ten-ways-to-reverse-a-string-in-javascript/ *
 * @param {string} string String to reverse.
 * @return {string}
 */
export const reverseString = (string) => {
  if (string.length === 0) {
    return string
  }

  return reverseString(string.substr(1)) + string.charAt(0)
}

/**
 * Reverse a string using a simpler ternary.
 *
 * The substr() method returns the characters in a string beginning at the specified
 * location through the specified number of characters.
 *
 * The charAt() method returns the specified character from a string.
 *
 * So what's happening is that if we aren't at an empty string we are going to recurse
 * reverseString and pass it the a substring sans the first chracter of our current string.
 * We append the current string's character at the first character as well.
 *
 * For the world hello:
 *    recursionReverse('hello')
 *    (recursionReverse('ello') + 'h')
 *    ((recursionReverse('llo') + 'e') + 'h')
 *    (((recursionReverse('lo') + 'l') + 'e') + 'h')
 *    ((((recursionReverse('o') + 'l') + 'l' ) + 'e') + 'h')
 *    (((('o') + 'l') + 'l' ) + 'e') + 'h')
 *
 * Another way of doing this specifically for the text 'hello' w/o using recursion.
 *     const helloReverse = () => (get_H())
 *     const get_H = () => (get_E() + 'h')
 *     const get_E = () => (get_L1() + 'e')
 *     const get_L1 = () => (get_L2() + 'l')
 *     const get_L2 = () => (get_O() + 'l')
 *     const get_O = () => ('o')
 *     helloReverse()
 *
 * What is odd is how we're doing the return in the recurisve function. This is often what
 * throws me off. We are passing in a substring for as long as we have a string left and
 * then we call ourself again. What is important here is the string.charAt(0) as this is
 * what we're using to build out the reverse string. until we reach the end. The diagram
 * above explains it best.
 *
 * @see http://bit.ly/2v4XBZE
 * @see http://eddmann.com/posts/ten-ways-to-reverse-a-string-in-javascript/ *
 * @param {string} string String to reverse.
 * @return {string}
 */
export const reverseStringTernary = (string) => (
  (string.length === 0)
    ? string
    : reverseStringTernary(string.substr(1)) + string.charAt(0)
)
<|endoftext|>


<commit_before>import { EventEmitter } from 'events';
import Dispatcher from '../dispatcher/AppDispatcher';
import ActionTypes from '../constants/ActionTypes';
import MZBenchActions from '../actions/MZBenchActions';
import Misc from '../utils/Misc.js';

const CHANGE_EVENT = 'metrics_change';
const DATA_RESAMPLING_RATE = 1; // hours

let data = {
    benchId: undefined,
    guid: undefined,

    last_resampling_date: undefined,

    starting_date: undefined,
    map: new Map([]),
    batch_counter: new Map([])
};

function _clearData() {
    data.last_resampling_date = Date.now();

    data.starting_date = undefined;
    data.map.clear();
    data.batch_counter.clear();
}

function _updateData(metric, rawData) {
    const updates = rawData.split("\n");
    updates.forEach((update) => {
        _applyUpdate(metric, update);
    });
}

function _updateBatchCounter(metric) {
    let m = data.batch_counter;
    if (m.has(metric)) {
        m.set(metric, m.get(metric) + 1);
    } else {
        m.set(metric, 1);
    }
}

function _applyUpdate(metric, update) {
    const tokens = update.split("\t");

    if(tokens.length >= 2) {
        const date = Number.parseInt(tokens[0]);
        const value = Number.parseFloat(tokens[1]);

        if(!Number.isNaN(date) && !Number.isNaN(value)) {
            _addObservation(metric, { date: date, value: value });
        }
    }
}

function _addObservation(metric, observation) {
    if(data.map.has(metric)) {
        _updateMetric(metric, observation);
    } else {
        _createMetric(metric, observation);
    }
}

function _createMetric(metric, observation) {
    if(!data.starting_date) {
        data.starting_date = observation.date;
    }

    data.map.set(metric, new Array({"date": _convertDate(observation.date), "value": observation.value}));
}

function _updateMetric(metric, observation) {
    data.map.get(metric).push({"date": _convertDate(observation.date), "value": observation.value});
}

function _convertDate(rawDate) {
    return rawDate - data.starting_date;
}

class MetricsStore extends EventEmitter {
    constructor() {
        super();
        this.setMaxListeners(Infinity);
    }

    emitChange() {
        return this.emit(CHANGE_EVENT);
    }

    onChange(callback) {
        this.on(CHANGE_EVENT, callback);
    }

    off(callback) {
        this.removeListener(CHANGE_EVENT, callback);
    }

    getCurrentBenchId() {
        return data.benchId;
    }

    performResamplingIfNeeded() {
        const sec_from_last_resampling = (Date.now() - data.last_resampling_date)/1000;
        if(sec_from_last_resampling > 3*60) { // DATA_RESAMPLING_RATE*3600
            let metrics_list = [];
            data.map.forEach((value, key) => {
                metrics_list.push(key);
            });

            this.resetSubscriptions(data.benchId);
            metrics_list.map((metric) => {
                this.addSubscription(metric);
            });
        }
    }

    resetSubscriptions(newBenchId) {
        _clearData();
        data.benchId = newBenchId;
        data.guid = Misc.gen_guid();
    }

    addSubscription(metrics) {
        MZBenchActions.sendSubscribe(data.benchId, metrics, data.guid);
    }

    changeCurrentBench(benchId, GUID) {
        data.benchId = benchId;
        data.guid = GUID;
        _clearData();
    }

    isDataLoaded() {
        return true;
    }

    updateMetricData(metric, guid, rawData) {
        if(data.guid == guid) {
            _updateData(metric, rawData);
        }
    }

    updateMetricBatchCounter(metric, guid) {
        if(data.guid == guid) {
            _updateBatchCounter(metric);
            this.performResamplingIfNeeded();
        }
    }

    getMetricData(metric) {
        if(data.map.has(metric)) {
            return data.map.get(metric);
        } else {
            return [];
        }
    }

    getBatchCounter(metric) {
        if(data.batch_counter.has(metric)) {
            return data.batch_counter.get(metric);
        } else {
            return 0;
        }
    }

    getMetricMaxDate(metric) {
        if(data.map.has(metric)) {
            let m = data.map.get(metric);
            return m[m.length - 1]["date"];
        } else {
            return 0;
        }
    }
};

var _MetricsStore = new MetricsStore();
export default _MetricsStore;

_MetricsStore.dispatchToken = Dispatcher.register((action) => {
    switch(action.type) {
        case ActionTypes.SUBSCRIBE_METRICS:
            _MetricsStore.addSubscription(action.metrics);
            break;
        case ActionTypes.METRIC_DATA:
            _MetricsStore.updateMetricData(action.metric, action.guid, action.data);
            _MetricsStore.emitChange();
            break;
        case ActionTypes.METRIC_BATCH_END:
            _MetricsStore.updateMetricBatchCounter(action.metric, action.guid);
            _MetricsStore.emitChange();
            break;
        default:
    }
});
<commit_msg>Set back correct resampling rate<commit_after>import { EventEmitter } from 'events';
import Dispatcher from '../dispatcher/AppDispatcher';
import ActionTypes from '../constants/ActionTypes';
import MZBenchActions from '../actions/MZBenchActions';
import Misc from '../utils/Misc.js';

const CHANGE_EVENT = 'metrics_change';
const DATA_RESAMPLING_RATE = 1; // hours

let data = {
    benchId: undefined,
    guid: undefined,

    last_resampling_date: undefined,

    starting_date: undefined,
    map: new Map([]),
    batch_counter: new Map([])
};

function _clearData() {
    data.last_resampling_date = Date.now();

    data.starting_date = undefined;
    data.map.clear();
    data.batch_counter.clear();
}

function _updateData(metric, rawData) {
    const updates = rawData.split("\n");
    updates.forEach((update) => {
        _applyUpdate(metric, update);
    });
}

function _updateBatchCounter(metric) {
    let m = data.batch_counter;
    if (m.has(metric)) {
        m.set(metric, m.get(metric) + 1);
    } else {
        m.set(metric, 1);
    }
}

function _applyUpdate(metric, update) {
    const tokens = update.split("\t");

    if(tokens.length >= 2) {
        const date = Number.parseInt(tokens[0]);
        const value = Number.parseFloat(tokens[1]);

        if(!Number.isNaN(date) && !Number.isNaN(value)) {
            _addObservation(metric, { date: date, value: value });
        }
    }
}

function _addObservation(metric, observation) {
    if(data.map.has(metric)) {
        _updateMetric(metric, observation);
    } else {
        _createMetric(metric, observation);
    }
}

function _createMetric(metric, observation) {
    if(!data.starting_date) {
        data.starting_date = observation.date;
    }

    data.map.set(metric, new Array({"date": _convertDate(observation.date), "value": observation.value}));
}

function _updateMetric(metric, observation) {
    data.map.get(metric).push({"date": _convertDate(observation.date), "value": observation.value});
}

function _convertDate(rawDate) {
    return rawDate - data.starting_date;
}

class MetricsStore extends EventEmitter {
    constructor() {
        super();
        this.setMaxListeners(Infinity);
    }

    emitChange() {
        return this.emit(CHANGE_EVENT);
    }

    onChange(callback) {
        this.on(CHANGE_EVENT, callback);
    }

    off(callback) {
        this.removeListener(CHANGE_EVENT, callback);
    }

    getCurrentBenchId() {
        return data.benchId;
    }

    performResamplingIfNeeded() {
        const sec_from_last_resampling = (Date.now() - data.last_resampling_date)/1000;
        if(sec_from_last_resampling > DATA_RESAMPLING_RATE*3600) {
            let metrics_list = [];
            data.map.forEach((value, key) => {
                metrics_list.push(key);
            });

            this.resetSubscriptions(data.benchId);
            metrics_list.map((metric) => {
                this.addSubscription(metric);
            });
        }
    }

    resetSubscriptions(newBenchId) {
        _clearData();
        data.benchId = newBenchId;
        data.guid = Misc.gen_guid();
    }

    addSubscription(metrics) {
        MZBenchActions.sendSubscribe(data.benchId, metrics, data.guid);
    }

    changeCurrentBench(benchId, GUID) {
        data.benchId = benchId;
        data.guid = GUID;
        _clearData();
    }

    isDataLoaded() {
        return true;
    }

    updateMetricData(metric, guid, rawData) {
        if(data.guid == guid) {
            _updateData(metric, rawData);
        }
    }

    updateMetricBatchCounter(metric, guid) {
        if(data.guid == guid) {
            _updateBatchCounter(metric);
            this.performResamplingIfNeeded();
        }
    }

    getMetricData(metric) {
        if(data.map.has(metric)) {
            return data.map.get(metric);
        } else {
            return [];
        }
    }

    getBatchCounter(metric) {
        if(data.batch_counter.has(metric)) {
            return data.batch_counter.get(metric);
        } else {
            return 0;
        }
    }

    getMetricMaxDate(metric) {
        if(data.map.has(metric)) {
            let m = data.map.get(metric);
            return m[m.length - 1]["date"];
        } else {
            return 0;
        }
    }
};

var _MetricsStore = new MetricsStore();
export default _MetricsStore;

_MetricsStore.dispatchToken = Dispatcher.register((action) => {
    switch(action.type) {
        case ActionTypes.SUBSCRIBE_METRICS:
            _MetricsStore.addSubscription(action.metrics);
            break;
        case ActionTypes.METRIC_DATA:
            _MetricsStore.updateMetricData(action.metric, action.guid, action.data);
            _MetricsStore.emitChange();
            break;
        case ActionTypes.METRIC_BATCH_END:
            _MetricsStore.updateMetricBatchCounter(action.metric, action.guid);
            _MetricsStore.emitChange();
            break;
        default:
    }
});
<|endoftext|>


<commit_before>"use strict";
var Fiber = require('./fibers');
var util = require('util');
module.exports = Future;
Function.prototype.future = function(detach) {
	var fn = this;
	var ret = function() {
		var future = new FiberFuture(fn, this, arguments);
		if (detach) {
			future.detach();
		}
		return future;
	};
	ret.toString = function() {
		return '<<Future '+ fn+ '.future()>>';
	};
	return ret;
};

function Future() {}

/**
 * Run a function(s) in a future context, and return a future to their return value. This is useful
 * for instances where you want a closure to be able to `.wait()`. This also lets you wait for
 * mulitple parallel opertions to run.
 */
Future.task = function(fn) {
	if (arguments.length === 1) {
		return fn.future()();
	} else {
		var future = new Future, pending = arguments.length, error, values = new Array(arguments.length);
		for (var ii = 0; ii < arguments.length; ++ii) {
			arguments[ii].future()().resolve(function(ii, err, val) {
				if (err) {
					error = err;
				}
				values[ii] = val;
				if (--pending === 0) {
					if (error) {
						future.throw(error);
					} else {
						future.return(values);
					}
				}
			}.bind(null, ii));
		}
		return future;
	}
};

/**
 * Wrap node-style async functions to instead return futures. This assumes that the last parameter
 * of the function is a callback.
 *
 * If a single function is passed a future-returning function is created. If an object is passed a
 * new object is returned with all functions wrapped.
 *
 * The value that is returned from the invocation of the underlying function is assigned to the
 * property `_` on the future. This is useful for functions like `execFile` which take a callback,
 * but also return meaningful information.
 *
 * `multi` indicates that this callback will return more than 1 argument after `err`. For example,
 * `child_process.exec()`
 *
 * `suffix` will append a string to every method that was overridden, if you pass an object to
 * `Future.wrap()`. Default is 'Future'.
 *
 * var readFileFuture = Future.wrap(require('fs').readFile);
 * var fs = Future.wrap(require('fs'));
 * fs.readFileFuture('example.txt').wait();
 */
Future.wrap = function(fnOrObject, multi, suffix, stop) {
	if (typeof fnOrObject === 'object') {
		var wrapped = Object.create(fnOrObject);
		for (var ii in fnOrObject) {
			if (wrapped[ii] instanceof Function) {
				wrapped[suffix === undefined ? ii+ 'Future' : ii+ suffix] = Future.wrap(wrapped[ii], multi, suffix, stop);
			}
		}
		return wrapped;
	} else if (typeof fnOrObject === 'function') {
		var fn = function() {
			var future = new Future;
			var args = Array.prototype.slice.call(arguments);
			if (multi) {
				var cb = future.resolver();
				args.push(function(err) {
					cb(err, Array.prototype.slice.call(arguments, 1));
				});
			} else {
				args.push(future.resolver());
			}
			future._ = fnOrObject.apply(this, args);
			return future;
		}
		// Modules like `request` return a function that has more functions as properties. Handle this
		// in some kind of reasonable way.
		if (!stop) {
			var proto = Object.create(fnOrObject);
			for (var ii in fnOrObject) {
				if (fnOrObject.hasOwnProperty(ii) && fnOrObject[ii] instanceof Function) {
					proto[ii] = proto[ii];
				}
			}
			fn.__proto__ = Future.wrap(proto, multi, suffix, true);
		}
		return fn;
	}
};

/**
 * Wait on a series of futures and then return. If the futures throw an exception this function
 * /won't/ throw it back. You can get the value of the future by calling get() on it directly. If
 * you want to wait on a single future you're better off calling future.wait() on the instance.
 */
Future.wait = function wait(/* ... */) {

	// Normalize arguments + pull out a FiberFuture for reuse if possible
	var futures = [], singleFiberFuture;
	for (var ii = 0; ii < arguments.length; ++ii) {
		var arg = arguments[ii];
		if (arg instanceof Future) {
			// Ignore already resolved fibers
			if (arg.isResolved()) {
				continue;
			}
			// Look for fiber reuse
			if (!singleFiberFuture && arg instanceof FiberFuture && !arg.started) {
				singleFiberFuture = arg;
				continue;
			}
			futures.push(arg);
		} else if (arg instanceof Array) {
			for (var jj = 0; jj < arg.length; ++jj) {
				var aarg = arg[jj];
				if (aarg instanceof Future) {
					// Ignore already resolved fibers
					if (aarg.isResolved()) {
						continue;
					}
					// Look for fiber reuse
					if (!singleFiberFuture && aarg instanceof FiberFuture && !aarg.started) {
						singleFiberFuture = aarg;
						continue;
					}
					futures.push(aarg);
				} else {
					throw new Error(aarg+ ' is not a future');
				}
			}
		} else {
			throw new Error(arg+ ' is not a future');
		}
	}

	// Resumes current fiber
	var fiber = Fiber.current;
	if (!fiber) {
		throw new Error('Can\'t wait without a fiber');
	}

	// Resolve all futures
	var pending = futures.length + (singleFiberFuture ? 1 : 0);
	function cb() {
		if (!--pending) {
			fiber.run();
		}
	}
	for (var ii = 0; ii < futures.length; ++ii) {
		futures[ii].resolve(cb);
	}

	// Reusing a fiber?
	if (singleFiberFuture) {
		singleFiberFuture.started = true;
		try {
			singleFiberFuture.return(
				singleFiberFuture.fn.apply(singleFiberFuture.context, singleFiberFuture.args));
		} catch(e) {
			singleFiberFuture.throw(e);
		}
		--pending;
	}

	// Yield this fiber
	if (pending) {
		Fiber.yield();
	}
};

Future.prototype = {
	/**
	 * Return the value of this future. If the future hasn't resolved yet this will throw an error.
	 */
	get: function() {
		if (!this.resolved) {
			throw new Error('Future must resolve before value is ready');
		} else if (this.error) {
			// Link the stack traces up
			var error = this.error;
			var localStack = {};
			Error.captureStackTrace(localStack, Future.prototype.get);
			var futureStack = Object.getOwnPropertyDescriptor(error, 'futureStack');
			if (!futureStack) {
				futureStack = Object.getOwnPropertyDescriptor(error, 'stack');
				if (futureStack) {
					Object.defineProperty(error, 'futureStack', futureStack);
				}
			}
			if (futureStack && futureStack.get) {
				Object.defineProperty(error, 'stack', {
					get: function() {
						var stack = futureStack.get.apply(error);
						if (stack) {
							stack = stack.split('\n');
							return [stack[0]]
								.concat(localStack.stack.split('\n').slice(1))
								.concat('    - - - - -')
								.concat(stack.slice(1))
								.join('\n');
						} else {
							return localStack.stack;
						}
					},
					enumerable: true,
				});
			}
			throw error;
		} else {
			return this.value;
		}
	},

	/**
	 * Mark this future as returned. All pending callbacks will be invoked immediately.
	 */
	"return": function(value) {
		if (this.resolved) {
			throw new Error('Future resolved more than once');
		}
		this.value = value;
		this.resolved = true;

		var callbacks = this.callbacks;
		if (callbacks) {
			delete this.callbacks;
			for (var ii = 0; ii < callbacks.length; ++ii) {
				try {
					var ref = callbacks[ii];
					if (ref[1]) {
						ref[1](value);
					} else {
						ref[0](undefined, value);
					}
				} catch(ex) {
					// console.log('Resolve cb threw', String(ex.stack || ex.message || ex));
					process.nextTick(function() {
						throw(ex);
					});
				}
			}
		}
	},

	/**
	 * Throw from this future as returned. All pending callbacks will be invoked immediately.
	 */
	"throw": function(error) {
		if (this.resolved) {
			throw new Error('Future resolved more than once');
		} else if (!error) {
			throw new Error('Must throw non-empty error');
		}
		this.error = error;
		this.resolved = true;

		var callbacks = this.callbacks;
		if (callbacks) {
			delete this.callbacks;
			for (var ii = 0; ii < callbacks.length; ++ii) {
				try {
					var ref = callbacks[ii];
					if (ref[1]) {
						ref[0].throw(error);
					} else {
						ref[0](error);
					}
				} catch(ex) {
					// console.log('Resolve cb threw', String(ex.stack || ex.message || ex));
					process.nextTick(function() {
						throw(ex);
					});
				}
			}
		}
	},

	/**
	 * "detach" this future. Basically this is useful if you want to run a task in a future, you
	 * aren't interested in its return value, but if it throws you don't want the exception to be
	 * lost. If this fiber throws, an exception will be thrown to the event loop and node will
	 * probably fall down.
	 */
	detach: function() {
		this.resolve(function(err) {
			if (err) {
				throw err;
			}
		});
	},

	/**
	 * Returns whether or not this future has resolved yet.
	 */
	isResolved: function() {
		return this.resolved === true;
	},

	/**
	 * Returns a node-style function which will mark this future as resolved when called.
	 */
	resolver: function() {
		return function(err, val) {
			if (err) {
				this.throw(err);
			} else {
				this.return(val);
			}
		}.bind(this);
	},

	/**
	 * Waits for this future to resolve and then invokes a callback.
	 *
	 * If two arguments are passed, the first argument is a future which will be thrown to in the case
	 * of error, and the second is a function(val){} callback.
	 *
	 * If only one argument is passed it is a standard function(err, val){} callback.
	 */
	resolve: function(arg1, arg2) {
		if (this.resolved) {
			if (arg2) {
				if (this.error) {
					arg1.throw(this.error);
				} else {
					arg2(this.value);
				}
			} else {
				arg1(this.error, this.value);
			}
		} else {
			(this.callbacks = this.callbacks || []).push([arg1, arg2]);
		}
		return this;
	},

	/**
	 * Resolve only in the case of success
	 */
	resolveSuccess: function(cb) {
		this.resolve(function(err, val) {
			if (err) {
				return;
			}
			cb(val);
		});
		return this;
	},

	/**
	 * Propogate results to another future.
	 */
	proxy: function(future) {
		this.resolve(function(err, val) {
			if (err) {
				future.throw(err);
			} else {
				future.return(val);
			}
		});
	},

	/**
	 * Propogate only errors to an another future or array of futures.
	 */
	proxyErrors: function(futures) {
		this.resolve(function(err) {
			if (!err) {
				return;
			}
			if (futures instanceof Array) {
				for (var ii = 0; ii < futures.length; ++ii) {
					futures[ii].throw(err);
				}
			} else {
				futures.throw(err);
			}
		});
		return this;
	},

	/**
	 * Differs from its functional counterpart in that it actually resolves the future. Thus if the
	 * future threw, future.wait() will throw.
	 */
	wait: function() {
		if (this.isResolved()) {
			return this.get();
		}
		Future.wait(this);
		return this.get();
	},
};

/**
 * A function call which loads inside a fiber automatically and returns a future.
 */
function FiberFuture(fn, context, args) {
	this.fn = fn;
	this.context = context;
	this.args = args;
	this.started = false;
	var that = this;
	process.nextTick(function() {
		if (!that.started) {
			that.started = true;
			Fiber(function() {
				try {
					that.return(fn.apply(context, args));
				} catch(e) {
					that.throw(e);
				}
			}).run();
		}
	});
}
util.inherits(FiberFuture, Future);
<commit_msg>Add setter on Error<commit_after>"use strict";
var Fiber = require('./fibers');
var util = require('util');
module.exports = Future;
Function.prototype.future = function(detach) {
	var fn = this;
	var ret = function() {
		var future = new FiberFuture(fn, this, arguments);
		if (detach) {
			future.detach();
		}
		return future;
	};
	ret.toString = function() {
		return '<<Future '+ fn+ '.future()>>';
	};
	return ret;
};

function Future() {}

/**
 * Run a function(s) in a future context, and return a future to their return value. This is useful
 * for instances where you want a closure to be able to `.wait()`. This also lets you wait for
 * mulitple parallel opertions to run.
 */
Future.task = function(fn) {
	if (arguments.length === 1) {
		return fn.future()();
	} else {
		var future = new Future, pending = arguments.length, error, values = new Array(arguments.length);
		for (var ii = 0; ii < arguments.length; ++ii) {
			arguments[ii].future()().resolve(function(ii, err, val) {
				if (err) {
					error = err;
				}
				values[ii] = val;
				if (--pending === 0) {
					if (error) {
						future.throw(error);
					} else {
						future.return(values);
					}
				}
			}.bind(null, ii));
		}
		return future;
	}
};

/**
 * Wrap node-style async functions to instead return futures. This assumes that the last parameter
 * of the function is a callback.
 *
 * If a single function is passed a future-returning function is created. If an object is passed a
 * new object is returned with all functions wrapped.
 *
 * The value that is returned from the invocation of the underlying function is assigned to the
 * property `_` on the future. This is useful for functions like `execFile` which take a callback,
 * but also return meaningful information.
 *
 * `multi` indicates that this callback will return more than 1 argument after `err`. For example,
 * `child_process.exec()`
 *
 * `suffix` will append a string to every method that was overridden, if you pass an object to
 * `Future.wrap()`. Default is 'Future'.
 *
 * var readFileFuture = Future.wrap(require('fs').readFile);
 * var fs = Future.wrap(require('fs'));
 * fs.readFileFuture('example.txt').wait();
 */
Future.wrap = function(fnOrObject, multi, suffix, stop) {
	if (typeof fnOrObject === 'object') {
		var wrapped = Object.create(fnOrObject);
		for (var ii in fnOrObject) {
			if (wrapped[ii] instanceof Function) {
				wrapped[suffix === undefined ? ii+ 'Future' : ii+ suffix] = Future.wrap(wrapped[ii], multi, suffix, stop);
			}
		}
		return wrapped;
	} else if (typeof fnOrObject === 'function') {
		var fn = function() {
			var future = new Future;
			var args = Array.prototype.slice.call(arguments);
			if (multi) {
				var cb = future.resolver();
				args.push(function(err) {
					cb(err, Array.prototype.slice.call(arguments, 1));
				});
			} else {
				args.push(future.resolver());
			}
			future._ = fnOrObject.apply(this, args);
			return future;
		}
		// Modules like `request` return a function that has more functions as properties. Handle this
		// in some kind of reasonable way.
		if (!stop) {
			var proto = Object.create(fnOrObject);
			for (var ii in fnOrObject) {
				if (fnOrObject.hasOwnProperty(ii) && fnOrObject[ii] instanceof Function) {
					proto[ii] = proto[ii];
				}
			}
			fn.__proto__ = Future.wrap(proto, multi, suffix, true);
		}
		return fn;
	}
};

/**
 * Wait on a series of futures and then return. If the futures throw an exception this function
 * /won't/ throw it back. You can get the value of the future by calling get() on it directly. If
 * you want to wait on a single future you're better off calling future.wait() on the instance.
 */
Future.wait = function wait(/* ... */) {

	// Normalize arguments + pull out a FiberFuture for reuse if possible
	var futures = [], singleFiberFuture;
	for (var ii = 0; ii < arguments.length; ++ii) {
		var arg = arguments[ii];
		if (arg instanceof Future) {
			// Ignore already resolved fibers
			if (arg.isResolved()) {
				continue;
			}
			// Look for fiber reuse
			if (!singleFiberFuture && arg instanceof FiberFuture && !arg.started) {
				singleFiberFuture = arg;
				continue;
			}
			futures.push(arg);
		} else if (arg instanceof Array) {
			for (var jj = 0; jj < arg.length; ++jj) {
				var aarg = arg[jj];
				if (aarg instanceof Future) {
					// Ignore already resolved fibers
					if (aarg.isResolved()) {
						continue;
					}
					// Look for fiber reuse
					if (!singleFiberFuture && aarg instanceof FiberFuture && !aarg.started) {
						singleFiberFuture = aarg;
						continue;
					}
					futures.push(aarg);
				} else {
					throw new Error(aarg+ ' is not a future');
				}
			}
		} else {
			throw new Error(arg+ ' is not a future');
		}
	}

	// Resumes current fiber
	var fiber = Fiber.current;
	if (!fiber) {
		throw new Error('Can\'t wait without a fiber');
	}

	// Resolve all futures
	var pending = futures.length + (singleFiberFuture ? 1 : 0);
	function cb() {
		if (!--pending) {
			fiber.run();
		}
	}
	for (var ii = 0; ii < futures.length; ++ii) {
		futures[ii].resolve(cb);
	}

	// Reusing a fiber?
	if (singleFiberFuture) {
		singleFiberFuture.started = true;
		try {
			singleFiberFuture.return(
				singleFiberFuture.fn.apply(singleFiberFuture.context, singleFiberFuture.args));
		} catch(e) {
			singleFiberFuture.throw(e);
		}
		--pending;
	}

	// Yield this fiber
	if (pending) {
		Fiber.yield();
	}
};

Future.prototype = {
	/**
	 * Return the value of this future. If the future hasn't resolved yet this will throw an error.
	 */
	get: function() {
		if (!this.resolved) {
			throw new Error('Future must resolve before value is ready');
		} else if (this.error) {
			// Link the stack traces up
			var error = this.error;
			var localStack = {};
			Error.captureStackTrace(localStack, Future.prototype.get);
			var futureStack = Object.getOwnPropertyDescriptor(error, 'futureStack');
			if (!futureStack) {
				futureStack = Object.getOwnPropertyDescriptor(error, 'stack');
				if (futureStack) {
					Object.defineProperty(error, 'futureStack', futureStack);
				}
			}
			if (futureStack && futureStack.get) {
				Object.defineProperty(error, 'stack', {
					get: function() {
						var stack = futureStack.get.apply(error);
						if (stack) {
							stack = stack.split('\n');
							return [stack[0]]
								.concat(localStack.stack.split('\n').slice(1))
								.concat('    - - - - -')
								.concat(stack.slice(1))
								.join('\n');
						} else {
							return localStack.stack;
						}
					},
					set: function(stack) {
						Object.defineProperty(error, 'stack', {
							value: stack,
							configurable: true,
							enumerable: false,
							writable: true,
						});
					},
					configurable: true,
					enumerable: false,
				});
			}
			throw error;
		} else {
			return this.value;
		}
	},

	/**
	 * Mark this future as returned. All pending callbacks will be invoked immediately.
	 */
	"return": function(value) {
		if (this.resolved) {
			throw new Error('Future resolved more than once');
		}
		this.value = value;
		this.resolved = true;

		var callbacks = this.callbacks;
		if (callbacks) {
			delete this.callbacks;
			for (var ii = 0; ii < callbacks.length; ++ii) {
				try {
					var ref = callbacks[ii];
					if (ref[1]) {
						ref[1](value);
					} else {
						ref[0](undefined, value);
					}
				} catch(ex) {
					// console.log('Resolve cb threw', String(ex.stack || ex.message || ex));
					process.nextTick(function() {
						throw(ex);
					});
				}
			}
		}
	},

	/**
	 * Throw from this future as returned. All pending callbacks will be invoked immediately.
	 */
	"throw": function(error) {
		if (this.resolved) {
			throw new Error('Future resolved more than once');
		} else if (!error) {
			throw new Error('Must throw non-empty error');
		}
		this.error = error;
		this.resolved = true;

		var callbacks = this.callbacks;
		if (callbacks) {
			delete this.callbacks;
			for (var ii = 0; ii < callbacks.length; ++ii) {
				try {
					var ref = callbacks[ii];
					if (ref[1]) {
						ref[0].throw(error);
					} else {
						ref[0](error);
					}
				} catch(ex) {
					// console.log('Resolve cb threw', String(ex.stack || ex.message || ex));
					process.nextTick(function() {
						throw(ex);
					});
				}
			}
		}
	},

	/**
	 * "detach" this future. Basically this is useful if you want to run a task in a future, you
	 * aren't interested in its return value, but if it throws you don't want the exception to be
	 * lost. If this fiber throws, an exception will be thrown to the event loop and node will
	 * probably fall down.
	 */
	detach: function() {
		this.resolve(function(err) {
			if (err) {
				throw err;
			}
		});
	},

	/**
	 * Returns whether or not this future has resolved yet.
	 */
	isResolved: function() {
		return this.resolved === true;
	},

	/**
	 * Returns a node-style function which will mark this future as resolved when called.
	 */
	resolver: function() {
		return function(err, val) {
			if (err) {
				this.throw(err);
			} else {
				this.return(val);
			}
		}.bind(this);
	},

	/**
	 * Waits for this future to resolve and then invokes a callback.
	 *
	 * If two arguments are passed, the first argument is a future which will be thrown to in the case
	 * of error, and the second is a function(val){} callback.
	 *
	 * If only one argument is passed it is a standard function(err, val){} callback.
	 */
	resolve: function(arg1, arg2) {
		if (this.resolved) {
			if (arg2) {
				if (this.error) {
					arg1.throw(this.error);
				} else {
					arg2(this.value);
				}
			} else {
				arg1(this.error, this.value);
			}
		} else {
			(this.callbacks = this.callbacks || []).push([arg1, arg2]);
		}
		return this;
	},

	/**
	 * Resolve only in the case of success
	 */
	resolveSuccess: function(cb) {
		this.resolve(function(err, val) {
			if (err) {
				return;
			}
			cb(val);
		});
		return this;
	},

	/**
	 * Propogate results to another future.
	 */
	proxy: function(future) {
		this.resolve(function(err, val) {
			if (err) {
				future.throw(err);
			} else {
				future.return(val);
			}
		});
	},

	/**
	 * Propogate only errors to an another future or array of futures.
	 */
	proxyErrors: function(futures) {
		this.resolve(function(err) {
			if (!err) {
				return;
			}
			if (futures instanceof Array) {
				for (var ii = 0; ii < futures.length; ++ii) {
					futures[ii].throw(err);
				}
			} else {
				futures.throw(err);
			}
		});
		return this;
	},

	/**
	 * Differs from its functional counterpart in that it actually resolves the future. Thus if the
	 * future threw, future.wait() will throw.
	 */
	wait: function() {
		if (this.isResolved()) {
			return this.get();
		}
		Future.wait(this);
		return this.get();
	},
};

/**
 * A function call which loads inside a fiber automatically and returns a future.
 */
function FiberFuture(fn, context, args) {
	this.fn = fn;
	this.context = context;
	this.args = args;
	this.started = false;
	var that = this;
	process.nextTick(function() {
		if (!that.started) {
			that.started = true;
			Fiber(function() {
				try {
					that.return(fn.apply(context, args));
				} catch(e) {
					that.throw(e);
				}
			}).run();
		}
	});
}
util.inherits(FiberFuture, Future);
<|endoftext|>


<commit_before>(function webpackUniversalModuleDefinition(root, factory) {
	if(typeof exports === 'object' && typeof module === 'object')
		module.exports = factory();
	else if(typeof define === 'function' && define.amd)
		define([], factory);
	else if(typeof exports === 'object')
		exports["VanillaSharing"] = factory();
	else
		root["VanillaSharing"] = factory();
})(this, function() {
return /******/ (function(modules) { // webpackBootstrap
/******/ 	// The module cache
/******/ 	var installedModules = {};
/******/
/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {
/******/
/******/ 		// Check if module is in cache
/******/ 		if(installedModules[moduleId])
/******/ 			return installedModules[moduleId].exports;
/******/
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = installedModules[moduleId] = {
/******/ 			i: moduleId,
/******/ 			l: false,
/******/ 			exports: {}
/******/ 		};
/******/
/******/ 		// Execute the module function
/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ 		// Flag the module as loaded
/******/ 		module.l = true;
/******/
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/
/******/
/******/ 	// expose the modules object (__webpack_modules__)
/******/ 	__webpack_require__.m = modules;
/******/
/******/ 	// expose the module cache
/******/ 	__webpack_require__.c = installedModules;
/******/
/******/ 	// identity function for calling harmony imports with the correct context
/******/ 	__webpack_require__.i = function(value) { return value; };
/******/
/******/ 	// define getter function for harmony exports
/******/ 	__webpack_require__.d = function(exports, name, getter) {
/******/ 		if(!__webpack_require__.o(exports, name)) {
/******/ 			Object.defineProperty(exports, name, {
/******/ 				configurable: false,
/******/ 				enumerable: true,
/******/ 				get: getter
/******/ 			});
/******/ 		}
/******/ 	};
/******/
/******/ 	// getDefaultExport function for compatibility with non-harmony modules
/******/ 	__webpack_require__.n = function(module) {
/******/ 		var getter = module && module.__esModule ?
/******/ 			function getDefault() { return module['default']; } :
/******/ 			function getModuleExports() { return module; };
/******/ 		__webpack_require__.d(getter, 'a', getter);
/******/ 		return getter;
/******/ 	};
/******/
/******/ 	// Object.prototype.hasOwnProperty.call
/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ 	// __webpack_public_path__
/******/ 	__webpack_require__.p = "";
/******/
/******/ 	// Load entry module and return exports
/******/ 	return __webpack_require__(__webpack_require__.s = 11);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
var WIN_PARAMS = exports.WIN_PARAMS = 'scrollbars=0, resizable=1, menubar=0, left=100, top=100, width=550, height=440, toolbar=0, status=0';
var VK_MAX_LENGTH = exports.VK_MAX_LENGTH = 80;

/***/ }),
/* 1 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = encodeParams;
function encodeParams(obj) {
  return Object.keys(obj).filter(function (k) {
    return typeof obj[k] !== 'undefined';
  }).map(function (k) {
    return encodeURIComponent(k) + '=' + encodeURIComponent(obj[k]);
  }).join('&');
}

/***/ }),
/* 2 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

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

var _config = __webpack_require__(0);

var _encodeParams = __webpack_require__(1);

var _encodeParams2 = _interopRequireDefault(_encodeParams);

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

function fb() {
  var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  var fbAppId = options.fbAppId,
      description = options.description,
      title = options.title,
      url = options.url,
      image = options.image,
      redirectUri = options.redirectUri;

  if (!fbAppId) {
    throw new Error('fbAppId is not defined');
  }

  var params = (0, _encodeParams2.default)({
    app_id: fbAppId,
    display: 'popup',
    redirect_uri: redirectUri,
    link: url,
    name: title,
    description: description,
    picture: image
  });

  return window.open('https://www.facebook.com/dialog/feed?' + params, '_blank', _config.WIN_PARAMS);
}

/***/ }),
/* 3 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

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

var _config = __webpack_require__(0);

var _encodeParams = __webpack_require__(1);

var _encodeParams2 = _interopRequireDefault(_encodeParams);

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

function gp() {
  var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  var url = options.url;

  var params = (0, _encodeParams2.default)({ url: url });

  return window.open('https://plus.google.com/share?' + params, '_blank', _config.WIN_PARAMS);
}

/***/ }),
/* 4 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

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

var _config = __webpack_require__(0);

var _encodeParams = __webpack_require__(1);

var _encodeParams2 = _interopRequireDefault(_encodeParams);

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

function mail() {
  var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  var url = options.url,
      title = options.title,
      description = options.description,
      image = options.image;

  var params = (0, _encodeParams2.default)({
    share_url: url,
    title: title,
    description: description,
    imageurl: image
  });

  return window.open('http://connect.mail.ru/share?' + params, '_blank', _config.WIN_PARAMS);
}

/***/ }),
/* 5 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

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

var _config = __webpack_require__(0);

var _encodeParams = __webpack_require__(1);

var _encodeParams2 = _interopRequireDefault(_encodeParams);

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

function ok() {
  var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  var url = options.url,
      title = options.title;

  var params = (0, _encodeParams2.default)({
    'st.cmd': 'addShare',
    'st._surl': url,
    title: title
  });

  return window.open('https://ok.ru/dk?' + params, '_blank', _config.WIN_PARAMS);
}

/***/ }),
/* 6 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

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

var _config = __webpack_require__(0);

var _encodeParams = __webpack_require__(1);

var _encodeParams2 = _interopRequireDefault(_encodeParams);

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

function telegram() {
  var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  var url = options.url,
      title = options.title;

  var params = (0, _encodeParams2.default)({
    url: url,
    text: title
  });

  return window.open('https://t.me/share/url?' + params, '_blank', _config.WIN_PARAMS);
}

/***/ }),
/* 7 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

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

var _config = __webpack_require__(0);

var _encodeParams = __webpack_require__(1);

var _encodeParams2 = _interopRequireDefault(_encodeParams);

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

function tw() {
  var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  var title = options.title,
      url = options.url;

  var params = (0, _encodeParams2.default)({
    text: title,
    url: url
  });

  return window.open('https://twitter.com/intent/tweet?' + params, '_blank', _config.WIN_PARAMS);
}

/***/ }),
/* 8 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

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

var _config = __webpack_require__(0);

var _encodeParams = __webpack_require__(1);

var _encodeParams2 = _interopRequireDefault(_encodeParams);

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

function viber() {
  var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  var url = options.url,
      title = options.title;

  var arr = [];

  if (title) {
    arr.push(title);
  }

  if (url) {
    arr.push(url);
  }

  var params = (0, _encodeParams2.default)({
    text: arr.join(' ')
  });

  // TODO: check for mobile ?
  return window.open('viber://forward?' + params, '_blank', _config.WIN_PARAMS);
}

/***/ }),
/* 9 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

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

var _config = __webpack_require__(0);

var _encodeParams = __webpack_require__(1);

var _encodeParams2 = _interopRequireDefault(_encodeParams);

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

function vk() {
  var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  var url = options.url,
      image = options.image,
      isVkParse = options.isVkParse;
  var description = options.description,
      title = options.title;

  if (description && description.length > _config.VK_MAX_LENGTH) {
    description = description.substr(0, _config.VK_MAX_LENGTH) + '...';
  }

  if (title && title.length > _config.VK_MAX_LENGTH) {
    title = title.substr(0, _config.VK_MAX_LENGTH) + '...';
  }

  var params = void 0;
  if (isVkParse) {
    params = (0, _encodeParams2.default)({ url: url });
  } else {
    params = (0, _encodeParams2.default)({ url: url, title: title, description: description, image: image, noparse: true });
  }

  return window.open('https://vk.com/share.php?' + params, '_blank', _config.WIN_PARAMS);
}

/***/ }),
/* 10 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

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

var _config = __webpack_require__(0);

var _encodeParams = __webpack_require__(1);

var _encodeParams2 = _interopRequireDefault(_encodeParams);

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

function whatsapp() {
  var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  var url = options.url,
      title = options.title;

  var params = (0, _encodeParams2.default)({
    href: url,
    text: title
  });

  // TODO: check for mobile ?
  return window.open('whatsapp://send?' + params, '_blank', _config.WIN_PARAMS);
}

/***/ }),
/* 11 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.whatsapp = exports.vk = exports.viber = exports.tw = exports.telegram = exports.ok = exports.mail = exports.gp = exports.fb = undefined;

var _fb = __webpack_require__(2);

var _fb2 = _interopRequireDefault(_fb);

var _gp = __webpack_require__(3);

var _gp2 = _interopRequireDefault(_gp);

var _mail = __webpack_require__(4);

var _mail2 = _interopRequireDefault(_mail);

var _ok = __webpack_require__(5);

var _ok2 = _interopRequireDefault(_ok);

var _telegram = __webpack_require__(6);

var _telegram2 = _interopRequireDefault(_telegram);

var _tw = __webpack_require__(7);

var _tw2 = _interopRequireDefault(_tw);

var _viber = __webpack_require__(8);

var _viber2 = _interopRequireDefault(_viber);

var _vk = __webpack_require__(9);

var _vk2 = _interopRequireDefault(_vk);

var _whatsapp = __webpack_require__(10);

var _whatsapp2 = _interopRequireDefault(_whatsapp);

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

exports.fb = _fb2.default;
exports.gp = _gp2.default;
exports.mail = _mail2.default;
exports.ok = _ok2.default;
exports.telegram = _telegram2.default;
exports.tw = _tw2.default;
exports.viber = _viber2.default;
exports.vk = _vk2.default;
exports.whatsapp = _whatsapp2.default;

/***/ })
/******/ ]);
});<commit_msg>chore: update build<commit_after>(function webpackUniversalModuleDefinition(root, factory) {
	if(typeof exports === 'object' && typeof module === 'object')
		module.exports = factory();
	else if(typeof define === 'function' && define.amd)
		define([], factory);
	else if(typeof exports === 'object')
		exports["VanillaSharing"] = factory();
	else
		root["VanillaSharing"] = factory();
})(this, function() {
return /******/ (function(modules) { // webpackBootstrap
/******/ 	// The module cache
/******/ 	var installedModules = {};
/******/
/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {
/******/
/******/ 		// Check if module is in cache
/******/ 		if(installedModules[moduleId])
/******/ 			return installedModules[moduleId].exports;
/******/
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = installedModules[moduleId] = {
/******/ 			i: moduleId,
/******/ 			l: false,
/******/ 			exports: {}
/******/ 		};
/******/
/******/ 		// Execute the module function
/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ 		// Flag the module as loaded
/******/ 		module.l = true;
/******/
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/
/******/
/******/ 	// expose the modules object (__webpack_modules__)
/******/ 	__webpack_require__.m = modules;
/******/
/******/ 	// expose the module cache
/******/ 	__webpack_require__.c = installedModules;
/******/
/******/ 	// identity function for calling harmony imports with the correct context
/******/ 	__webpack_require__.i = function(value) { return value; };
/******/
/******/ 	// define getter function for harmony exports
/******/ 	__webpack_require__.d = function(exports, name, getter) {
/******/ 		if(!__webpack_require__.o(exports, name)) {
/******/ 			Object.defineProperty(exports, name, {
/******/ 				configurable: false,
/******/ 				enumerable: true,
/******/ 				get: getter
/******/ 			});
/******/ 		}
/******/ 	};
/******/
/******/ 	// getDefaultExport function for compatibility with non-harmony modules
/******/ 	__webpack_require__.n = function(module) {
/******/ 		var getter = module && module.__esModule ?
/******/ 			function getDefault() { return module['default']; } :
/******/ 			function getModuleExports() { return module; };
/******/ 		__webpack_require__.d(getter, 'a', getter);
/******/ 		return getter;
/******/ 	};
/******/
/******/ 	// Object.prototype.hasOwnProperty.call
/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ 	// __webpack_public_path__
/******/ 	__webpack_require__.p = "";
/******/
/******/ 	// Load entry module and return exports
/******/ 	return __webpack_require__(__webpack_require__.s = 11);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
var WIN_PARAMS = exports.WIN_PARAMS = 'scrollbars=0, resizable=1, menubar=0, left=100, top=100, width=550, height=440, toolbar=0, status=0';
var VK_MAX_LENGTH = exports.VK_MAX_LENGTH = 80;

/***/ }),
/* 1 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = encodeParams;
function encodeParams(obj) {
  return Object.keys(obj).filter(function (k) {
    return typeof obj[k] !== 'undefined';
  }).map(function (k) {
    return encodeURIComponent(k) + '=' + encodeURIComponent(obj[k]);
  }).join('&');
}

/***/ }),
/* 2 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

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

var _config = __webpack_require__(0);

var _encodeParams = __webpack_require__(1);

var _encodeParams2 = _interopRequireDefault(_encodeParams);

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

function fb() {
  var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  var fbAppId = options.fbAppId,
      description = options.description,
      title = options.title,
      url = options.url,
      image = options.image,
      redirectUri = options.redirectUri;

  if (!fbAppId) {
    throw new Error('fbAppId is not defined');
  }

  var params = (0, _encodeParams2.default)({
    app_id: fbAppId,
    display: 'popup',
    redirect_uri: redirectUri,
    link: url,
    name: title,
    description: description,
    picture: image
  });

  return window.open('https://www.facebook.com/dialog/feed?' + params, '_blank', _config.WIN_PARAMS);
}

/***/ }),
/* 3 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

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

var _config = __webpack_require__(0);

var _encodeParams = __webpack_require__(1);

var _encodeParams2 = _interopRequireDefault(_encodeParams);

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

function gp() {
  var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  var url = options.url;

  var params = (0, _encodeParams2.default)({ url: url });

  return window.open('https://plus.google.com/share?' + params, '_blank', _config.WIN_PARAMS);
}

/***/ }),
/* 4 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

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

var _config = __webpack_require__(0);

var _encodeParams = __webpack_require__(1);

var _encodeParams2 = _interopRequireDefault(_encodeParams);

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

function mail() {
  var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  var url = options.url,
      title = options.title,
      description = options.description,
      image = options.image;

  var params = (0, _encodeParams2.default)({
    share_url: url,
    title: title,
    description: description,
    imageurl: image
  });

  return window.open('http://connect.mail.ru/share?' + params, '_blank', _config.WIN_PARAMS);
}

/***/ }),
/* 5 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

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

var _config = __webpack_require__(0);

var _encodeParams = __webpack_require__(1);

var _encodeParams2 = _interopRequireDefault(_encodeParams);

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

function ok() {
  var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  var url = options.url,
      title = options.title;

  var params = (0, _encodeParams2.default)({
    'st.cmd': 'addShare',
    'st._surl': url,
    title: title
  });

  return window.open('https://ok.ru/dk?' + params, '_blank', _config.WIN_PARAMS);
}

/***/ }),
/* 6 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

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

var _config = __webpack_require__(0);

var _encodeParams = __webpack_require__(1);

var _encodeParams2 = _interopRequireDefault(_encodeParams);

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

function telegram() {
  var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  var url = options.url,
      title = options.title;

  var params = (0, _encodeParams2.default)({
    url: url,
    text: title
  });

  return window.open('https://t.me/share/url?' + params, '_blank', _config.WIN_PARAMS);
}

/***/ }),
/* 7 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

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

var _config = __webpack_require__(0);

var _encodeParams = __webpack_require__(1);

var _encodeParams2 = _interopRequireDefault(_encodeParams);

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

function tw() {
  var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  var title = options.title,
      url = options.url;

  var params = (0, _encodeParams2.default)({
    text: title,
    url: url
  });

  return window.open('https://twitter.com/intent/tweet?' + params, '_blank', _config.WIN_PARAMS);
}

/***/ }),
/* 8 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

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

var _config = __webpack_require__(0);

var _encodeParams = __webpack_require__(1);

var _encodeParams2 = _interopRequireDefault(_encodeParams);

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

function viber() {
  var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  var url = options.url,
      title = options.title;

  var params = (0, _encodeParams2.default)({
    text: [title, url].filter(function (item) {
      return item;
    }).join(' '),
    fallback: 'https://viber.com'
  });

  return window.open('https://app.adjust.com/u783g1_kw9yml?' + params, '_blank', _config.WIN_PARAMS);
}

/***/ }),
/* 9 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

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

var _config = __webpack_require__(0);

var _encodeParams = __webpack_require__(1);

var _encodeParams2 = _interopRequireDefault(_encodeParams);

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

function vk() {
  var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  var url = options.url,
      image = options.image,
      isVkParse = options.isVkParse;
  var description = options.description,
      title = options.title;

  if (description && description.length > _config.VK_MAX_LENGTH) {
    description = description.substr(0, _config.VK_MAX_LENGTH) + '...';
  }

  if (title && title.length > _config.VK_MAX_LENGTH) {
    title = title.substr(0, _config.VK_MAX_LENGTH) + '...';
  }

  var params = void 0;
  if (isVkParse) {
    params = (0, _encodeParams2.default)({ url: url });
  } else {
    params = (0, _encodeParams2.default)({ url: url, title: title, description: description, image: image, noparse: true });
  }

  return window.open('https://vk.com/share.php?' + params, '_blank', _config.WIN_PARAMS);
}

/***/ }),
/* 10 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

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

var _config = __webpack_require__(0);

var _encodeParams = __webpack_require__(1);

var _encodeParams2 = _interopRequireDefault(_encodeParams);

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

function whatsapp() {
  var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  var url = options.url,
      title = options.title;

  var params = (0, _encodeParams2.default)({
    href: url,
    text: title
  });

  // TODO: check for mobile ?
  return window.open('whatsapp://send?' + params, '_blank', _config.WIN_PARAMS);
}

/***/ }),
/* 11 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.whatsapp = exports.vk = exports.viber = exports.tw = exports.telegram = exports.ok = exports.mail = exports.gp = exports.fb = undefined;

var _fb = __webpack_require__(2);

var _fb2 = _interopRequireDefault(_fb);

var _gp = __webpack_require__(3);

var _gp2 = _interopRequireDefault(_gp);

var _mail = __webpack_require__(4);

var _mail2 = _interopRequireDefault(_mail);

var _ok = __webpack_require__(5);

var _ok2 = _interopRequireDefault(_ok);

var _telegram = __webpack_require__(6);

var _telegram2 = _interopRequireDefault(_telegram);

var _tw = __webpack_require__(7);

var _tw2 = _interopRequireDefault(_tw);

var _viber = __webpack_require__(8);

var _viber2 = _interopRequireDefault(_viber);

var _vk = __webpack_require__(9);

var _vk2 = _interopRequireDefault(_vk);

var _whatsapp = __webpack_require__(10);

var _whatsapp2 = _interopRequireDefault(_whatsapp);

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

exports.fb = _fb2.default;
exports.gp = _gp2.default;
exports.mail = _mail2.default;
exports.ok = _ok2.default;
exports.telegram = _telegram2.default;
exports.tw = _tw2.default;
exports.viber = _viber2.default;
exports.vk = _vk2.default;
exports.whatsapp = _whatsapp2.default;

/***/ })
/******/ ]);
});<|endoftext|>