<commit_before>import ChartjsAdapter from "./adapters/chartjs";
import HighchartsAdapter from "./adapters/highcharts";
import GoogleChartsAdapter from "./adapters/google";

import { merge, isFunction, isArray, toStr, toFloat, toDate, toArr, sortByTime, sortByNumberSeries, isDate } from "./helpers";
import { pushRequest } from "./request-queue";

let config = window.Chartkick || {};
let adapters = [];

// helpers

function setText(element, text) {
  if (document.body.innerText) {
    element.innerText = text;
  } else {
    element.textContent = text;
  }
}

function chartError(element, message) {
  setText(element, "Error Loading Chart: " + message);
  element.style.color = "#ff0000";
}

function errorCatcher(chart) {
  try {
    chart.__render();
  } catch (err) {
    chartError(chart.element, err.message);
    throw err;
  }
}

function fetchDataSource(chart, dataSource) {
  if (typeof dataSource === "string") {
    pushRequest(dataSource, function (data) {
      chart.rawData = data;
      errorCatcher(chart);
    }, function (message) {
      chartError(chart.element, message);
    });
  } else {
    chart.rawData = dataSource;
    errorCatcher(chart);
  }
}

function addDownloadButton(chart) {
  let element = chart.element;
  let link = document.createElement("a");
  link.download = chart.options.download === true ? "chart.png" : chart.options.download; // http://caniuse.com/download
  link.style.position = "absolute";
  link.style.top = "20px";
  link.style.right = "20px";
  link.style.zIndex = 1000;
  link.style.lineHeight = "20px";
  link.target = "_blank"; // for safari
  let image = document.createElement("img");
  image.alt = "Download";
  image.style.border = "none";
  // icon from font-awesome
  // http://fa2png.io/
  image.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAMAAAC6V+0/AAABCFBMVEUAAADMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMywEsqxAAAAV3RSTlMAAQIDBggJCgsMDQ4PERQaHB0eISIjJCouLzE0OTo/QUJHSUpLTU5PUllhYmltcHh5foWLjI+SlaCio6atr7S1t7m6vsHHyM7R2tze5Obo7fHz9ff5+/1hlxK2AAAA30lEQVQYGUXBhVYCQQBA0TdYWAt2d3d3YWAHyur7/z9xgD16Lw0DW+XKx+1GgX+FRzM3HWQWrHl5N/oapW5RPe0PkBu+UYeICvozTWZVK23Ao04B79oJrOsJDOoxkZoQPWgX29pHpCZEk7rEvQYiNSFq1UMqvlCjJkRBS1R8hb00Vb/TajtBL7nTHE1X1vyMQF732dQhyF2o6SAwrzP06iUQzvwsArlnzcOdrgBhJyHa1QOgO9U1GsKuvjUTjavliZYQ8nNPapG6sap/3nrIdJ6bOWzmX/fy0XVpfzZP3S8OJT3g9EEiJwAAAABJRU5ErkJggg==";
  link.appendChild(image);
  element.style.position = "relative";

  chart.downloadAttached = true;

  // mouseenter
  addEvent(element, "mouseover", function(e) {
    let related = e.relatedTarget;
    // check download option again to ensure it wasn't changed
    if (!related || (related !== this && !childOf(this, related)) && chart.options.download) {
      link.href = chart.toImage();
      element.appendChild(link);
    }
  });

  // mouseleave
  addEvent(element, "mouseout", function(e) {
    let related = e.relatedTarget;
    if (!related || (related !== this && !childOf(this, related))) {
      if (link.parentNode) {
        link.parentNode.removeChild(link);
      }
    }
  });
}

// http://stackoverflow.com/questions/10149963/adding-event-listener-cross-browser
function addEvent(elem, event, fn) {
  if (elem.addEventListener) {
    elem.addEventListener(event, fn, false);
  } else {
    elem.attachEvent("on" + event, function() {
      // set the this pointer same as addEventListener when fn is called
      return(fn.call(elem, window.event));
    });
  }
}

// https://gist.github.com/shawnbot/4166283
function childOf(p, c) {
  if (p === c) return false;
  while (c && c !== p) c = c.parentNode;
  return c === p;
}

function addAdapter(adapter) {
  if (adapters.indexOf(adapter) === -1) {
    adapters.push(adapter);
  }
}

function loadAdapters() {
  if ("Chart" in window) {
    addAdapter(ChartjsAdapter);
  }

  if ("Highcharts" in window) {
    addAdapter(HighchartsAdapter);
  }

  if (window.google && (window.google.setOnLoadCallback || window.google.charts)) {
    addAdapter(GoogleChartsAdapter);
  }
}

function dataEmpty(data, chartType) {
  if (chartType === "PieChart" || chartType === "GeoChart" || chartType === "Timeline") {
    return data.length === 0;
  } else {
    for (let i = 0; i < data.length; i++) {
      if (data[i].data.length > 0) {
        return false;
      }
    }
    return true;
  }
}

function renderChart(chartType, chart) {
  if (chart.options.messages && chart.options.messages.empty && dataEmpty(chart.data, chartType)) {
    setText(chart.element, chart.options.messages.empty);
  } else {
    callAdapter(chartType, chart);
    if (chart.options.download && !chart.downloadAttached && chart.adapter === "chartjs") {
      addDownloadButton(chart);
    }
  }
}

// TODO remove chartType if cross-browser way
// to get the name of the chart class
function callAdapter(chartType, chart) {
  let i, adapter, fnName, adapterName;
  fnName = "render" + chartType;
  adapterName = chart.options.adapter;

  loadAdapters();

  for (i = 0; i < adapters.length; i++) {
    adapter = adapters[i];
    if ((!adapterName || adapterName === adapter.name) && isFunction(adapter[fnName])) {
      chart.adapter = adapter.name;
      return adapter[fnName](chart);
    }
  }

  if (adapters.length > 0) {
    throw new Error("No charting library found for " + chartType);
  } else {
    throw new Error("No charting libraries found - be sure to include one before your charts");
  }
}

// process data

let toFormattedKey = function (key, keyType) {
  if (keyType === "number") {
    key = toFloat(key);
  } else if (keyType === "datetime") {
    key = toDate(key);
  } else {
    key = toStr(key);
  }
  return key;
};

let formatSeriesData = function (data, keyType) {
  let r = [], key, j;
  for (j = 0; j < data.length; j++) {
    if (keyType === "bubble") {
      r.push([toFloat(data[j][0]), toFloat(data[j][1]), toFloat(data[j][2])]);
    } else {
      key = toFormattedKey(data[j][0], keyType);
      r.push([key, toFloat(data[j][1])]);
    }
  }
  if (keyType === "datetime") {
    r.sort(sortByTime);
  } else if (keyType === "number") {
    r.sort(sortByNumberSeries);
  }
  return r;
};

function detectDiscrete(series) {
  let i, j, data;
  for (i = 0; i < series.length; i++) {
    data = toArr(series[i].data);
    for (j = 0; j < data.length; j++) {
      if (!isDate(data[j][0])) {
        return true;
      }
    }
  }
  return false;
}

// creates a shallow copy of each element of the array
// elements are expected to be objects
function copySeries(series) {
  let newSeries = [], i, j;
  for (i = 0; i < series.length; i++) {
    let copy = {};
    for (j in series[i]) {
      if (series[i].hasOwnProperty(j)) {
        copy[j] = series[i][j];
      }
    }
    newSeries.push(copy);
  }
  return newSeries;
}

function processSeries(chart, keyType) {
  let i;

  let opts = chart.options;
  let series = chart.rawData;

  // see if one series or multiple
  if (!isArray(series) || typeof series[0] !== "object" || isArray(series[0])) {
    series = [{name: opts.label || "Value", data: series}];
    chart.hideLegend = true;
  } else {
    chart.hideLegend = false;
  }
  if ((opts.discrete === null || opts.discrete === undefined) && keyType !== "bubble" && keyType !== "number") {
    chart.discrete = detectDiscrete(series);
  } else {
    chart.discrete = opts.discrete;
  }
  if (chart.discrete) {
    keyType = "string";
  }
  if (chart.options.xtype) {
    keyType = chart.options.xtype;
  }

  // right format
  series = copySeries(series);
  for (i = 0; i < series.length; i++) {
    series[i].data = formatSeriesData(toArr(series[i].data), keyType);
  }

  return series;
}

function processSimple(chart) {
  let perfectData = toArr(chart.rawData), i;
  for (i = 0; i < perfectData.length; i++) {
    perfectData[i] = [toStr(perfectData[i][0]), toFloat(perfectData[i][1])];
  }
  return perfectData;
}

// define classes

class Chart {
  constructor(element, dataSource, options) {
    let elementId;
    if (typeof element === "string") {
      elementId = element;
      element = document.getElementById(element);
      if (!element) {
        throw new Error("No element with id " + elementId);
      }
    }
    this.element = element;
    this.options = merge(Chartkick.options, options || {});
    this.dataSource = dataSource;

    Chartkick.charts[element.id] = this;

    fetchDataSource(this, dataSource);

    if (this.options.refresh) {
      this.startRefresh();
    }
  }

  getElement() {
    return this.element;
  }

  getDataSource() {
    return this.dataSource;
  }

  getData() {
    return this.data;
  }

  getOptions() {
    return this.options;
  }

  getChartObject() {
    return this.chart;
  }

  getAdapter() {
    return this.adapter;
  }

  updateData(dataSource, options) {
    this.dataSource = dataSource;
    if (options) {
      this.__updateOptions(options);
    }
    fetchDataSource(this, dataSource);
  }

  setOptions(options) {
    this.__updateOptions(options);
    this.redraw();
  }

  redraw() {
    fetchDataSource(this, this.rawData);
  }

  refreshData() {
    if (typeof this.dataSource === "string") {
      // prevent browser from caching
      let sep = this.dataSource.indexOf("?") === -1 ? "?" : "&";
      let url = this.dataSource + sep + "_=" + (new Date()).getTime();
      fetchDataSource(this, url);
    }
  }

  startRefresh() {
    let refresh = this.options.refresh;

    if (!this.intervalId) {
      if (refresh) {
        this.intervalId = setInterval( () => {
          this.refreshData();
        }, refresh * 1000);
      } else {
        throw new Error("No refresh interval");
      }
    }
  }

  stopRefresh() {
    if (this.intervalId) {
      clearInterval(this.intervalId);
      this.intervalId = null;
    }
  }

  toImage() {
    if (this.adapter === "chartjs") {
      return this.chart.toBase64Image();
    } else {
      return null;
    }
  }

  __updateOptions(options) {
    let updateRefresh = options.refresh && options.refresh !== this.options.refresh;
    this.options = merge(Chartkick.options, options);
    if (updateRefresh) {
      this.stopRefresh();
      this.startRefresh();
    }
  }

  __render() {
    this.data = this.__processData();
    renderChart(this.__chartName(), this);
  }

  __config() {
    return config;
  }
}

class LineChart extends Chart {
  __processData() {
    return processSeries(this, "datetime");
  }

  __chartName() {
    return "LineChart";
  }
}

class PieChart extends Chart {
  __processData() {
    return processSimple(this);
  }

  __chartName() {
    return "PieChart";
  }
}

class ColumnChart extends Chart {
  __processData() {
    return processSeries(this, "string");
  }

  __chartName() {
    return "ColumnChart";
  }
}

class BarChart extends Chart {
  __processData() {
    return processSeries(this, "string");
  }

  __chartName() {
    return "BarChart";
  }
}

class AreaChart extends Chart {
  __processData() {
    return processSeries(this, "datetime");
  }

  __chartName() {
    return "AreaChart";
  }
}

class GeoChart extends Chart {
  __processData() {
    return processSimple(this);
  }

  __chartName() {
    return "GeoChart";
  }
}

class ScatterChart extends Chart {
  __processData() {
    return processSeries(this, "number");
  }

  __chartName() {
    return "ScatterChart";
  }
}

class BubbleChart extends Chart {
  __processData() {
    return processSeries(this, "bubble");
  }

  __chartName() {
    return "BubbleChart";
  }
}

class Timeline extends Chart {
  __processData() {
    let i, data = this.rawData;
    for (i = 0; i < data.length; i++) {
      data[i][1] = toDate(data[i][1]);
      data[i][2] = toDate(data[i][2]);
    }
    return data;
  }

  __chartName() {
    return "Timeline";
  }
}

const Chartkick = {
  LineChart: LineChart,
  PieChart: PieChart,
  ColumnChart: ColumnChart,
  BarChart: BarChart,
  AreaChart: AreaChart,
  GeoChart: GeoChart,
  ScatterChart: ScatterChart,
  BubbleChart: BubbleChart,
  Timeline: Timeline,
  charts: {},
  configure: function (options) {
    for (let key in options) {
      if (options.hasOwnProperty(key)) {
        config[key] = options[key];
      }
    }
  },
  eachChart: function (callback) {
    for (let chartId in Chartkick.charts) {
      if (Chartkick.charts.hasOwnProperty(chartId)) {
        callback(Chartkick.charts[chartId]);
      }
    }
  },
  config: config,
  options: {},
  adapters: adapters
};

export default Chartkick;
<commit_msg>Check if window defined - closes #96<commit_after>import ChartjsAdapter from "./adapters/chartjs";
import HighchartsAdapter from "./adapters/highcharts";
import GoogleChartsAdapter from "./adapters/google";

import { merge, isFunction, isArray, toStr, toFloat, toDate, toArr, sortByTime, sortByNumberSeries, isDate } from "./helpers";
import { pushRequest } from "./request-queue";

let config = (typeof window !== "undefined" && window.Chartkick) || {};
let adapters = [];

// helpers

function setText(element, text) {
  if (document.body.innerText) {
    element.innerText = text;
  } else {
    element.textContent = text;
  }
}

function chartError(element, message) {
  setText(element, "Error Loading Chart: " + message);
  element.style.color = "#ff0000";
}

function errorCatcher(chart) {
  try {
    chart.__render();
  } catch (err) {
    chartError(chart.element, err.message);
    throw err;
  }
}

function fetchDataSource(chart, dataSource) {
  if (typeof dataSource === "string") {
    pushRequest(dataSource, function (data) {
      chart.rawData = data;
      errorCatcher(chart);
    }, function (message) {
      chartError(chart.element, message);
    });
  } else {
    chart.rawData = dataSource;
    errorCatcher(chart);
  }
}

function addDownloadButton(chart) {
  let element = chart.element;
  let link = document.createElement("a");
  link.download = chart.options.download === true ? "chart.png" : chart.options.download; // http://caniuse.com/download
  link.style.position = "absolute";
  link.style.top = "20px";
  link.style.right = "20px";
  link.style.zIndex = 1000;
  link.style.lineHeight = "20px";
  link.target = "_blank"; // for safari
  let image = document.createElement("img");
  image.alt = "Download";
  image.style.border = "none";
  // icon from font-awesome
  // http://fa2png.io/
  image.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAMAAAC6V+0/AAABCFBMVEUAAADMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMywEsqxAAAAV3RSTlMAAQIDBggJCgsMDQ4PERQaHB0eISIjJCouLzE0OTo/QUJHSUpLTU5PUllhYmltcHh5foWLjI+SlaCio6atr7S1t7m6vsHHyM7R2tze5Obo7fHz9ff5+/1hlxK2AAAA30lEQVQYGUXBhVYCQQBA0TdYWAt2d3d3YWAHyur7/z9xgD16Lw0DW+XKx+1GgX+FRzM3HWQWrHl5N/oapW5RPe0PkBu+UYeICvozTWZVK23Ao04B79oJrOsJDOoxkZoQPWgX29pHpCZEk7rEvQYiNSFq1UMqvlCjJkRBS1R8hb00Vb/TajtBL7nTHE1X1vyMQF732dQhyF2o6SAwrzP06iUQzvwsArlnzcOdrgBhJyHa1QOgO9U1GsKuvjUTjavliZYQ8nNPapG6sap/3nrIdJ6bOWzmX/fy0XVpfzZP3S8OJT3g9EEiJwAAAABJRU5ErkJggg==";
  link.appendChild(image);
  element.style.position = "relative";

  chart.downloadAttached = true;

  // mouseenter
  addEvent(element, "mouseover", function(e) {
    let related = e.relatedTarget;
    // check download option again to ensure it wasn't changed
    if (!related || (related !== this && !childOf(this, related)) && chart.options.download) {
      link.href = chart.toImage();
      element.appendChild(link);
    }
  });

  // mouseleave
  addEvent(element, "mouseout", function(e) {
    let related = e.relatedTarget;
    if (!related || (related !== this && !childOf(this, related))) {
      if (link.parentNode) {
        link.parentNode.removeChild(link);
      }
    }
  });
}

// http://stackoverflow.com/questions/10149963/adding-event-listener-cross-browser
function addEvent(elem, event, fn) {
  if (elem.addEventListener) {
    elem.addEventListener(event, fn, false);
  } else {
    elem.attachEvent("on" + event, function() {
      // set the this pointer same as addEventListener when fn is called
      return(fn.call(elem, window.event));
    });
  }
}

// https://gist.github.com/shawnbot/4166283
function childOf(p, c) {
  if (p === c) return false;
  while (c && c !== p) c = c.parentNode;
  return c === p;
}

function addAdapter(adapter) {
  if (adapters.indexOf(adapter) === -1) {
    adapters.push(adapter);
  }
}

function loadAdapters() {
  if ("Chart" in window) {
    addAdapter(ChartjsAdapter);
  }

  if ("Highcharts" in window) {
    addAdapter(HighchartsAdapter);
  }

  if (window.google && (window.google.setOnLoadCallback || window.google.charts)) {
    addAdapter(GoogleChartsAdapter);
  }
}

function dataEmpty(data, chartType) {
  if (chartType === "PieChart" || chartType === "GeoChart" || chartType === "Timeline") {
    return data.length === 0;
  } else {
    for (let i = 0; i < data.length; i++) {
      if (data[i].data.length > 0) {
        return false;
      }
    }
    return true;
  }
}

function renderChart(chartType, chart) {
  if (chart.options.messages && chart.options.messages.empty && dataEmpty(chart.data, chartType)) {
    setText(chart.element, chart.options.messages.empty);
  } else {
    callAdapter(chartType, chart);
    if (chart.options.download && !chart.downloadAttached && chart.adapter === "chartjs") {
      addDownloadButton(chart);
    }
  }
}

// TODO remove chartType if cross-browser way
// to get the name of the chart class
function callAdapter(chartType, chart) {
  let i, adapter, fnName, adapterName;
  fnName = "render" + chartType;
  adapterName = chart.options.adapter;

  loadAdapters();

  for (i = 0; i < adapters.length; i++) {
    adapter = adapters[i];
    if ((!adapterName || adapterName === adapter.name) && isFunction(adapter[fnName])) {
      chart.adapter = adapter.name;
      return adapter[fnName](chart);
    }
  }

  if (adapters.length > 0) {
    throw new Error("No charting library found for " + chartType);
  } else {
    throw new Error("No charting libraries found - be sure to include one before your charts");
  }
}

// process data

let toFormattedKey = function (key, keyType) {
  if (keyType === "number") {
    key = toFloat(key);
  } else if (keyType === "datetime") {
    key = toDate(key);
  } else {
    key = toStr(key);
  }
  return key;
};

let formatSeriesData = function (data, keyType) {
  let r = [], key, j;
  for (j = 0; j < data.length; j++) {
    if (keyType === "bubble") {
      r.push([toFloat(data[j][0]), toFloat(data[j][1]), toFloat(data[j][2])]);
    } else {
      key = toFormattedKey(data[j][0], keyType);
      r.push([key, toFloat(data[j][1])]);
    }
  }
  if (keyType === "datetime") {
    r.sort(sortByTime);
  } else if (keyType === "number") {
    r.sort(sortByNumberSeries);
  }
  return r;
};

function detectDiscrete(series) {
  let i, j, data;
  for (i = 0; i < series.length; i++) {
    data = toArr(series[i].data);
    for (j = 0; j < data.length; j++) {
      if (!isDate(data[j][0])) {
        return true;
      }
    }
  }
  return false;
}

// creates a shallow copy of each element of the array
// elements are expected to be objects
function copySeries(series) {
  let newSeries = [], i, j;
  for (i = 0; i < series.length; i++) {
    let copy = {};
    for (j in series[i]) {
      if (series[i].hasOwnProperty(j)) {
        copy[j] = series[i][j];
      }
    }
    newSeries.push(copy);
  }
  return newSeries;
}

function processSeries(chart, keyType) {
  let i;

  let opts = chart.options;
  let series = chart.rawData;

  // see if one series or multiple
  if (!isArray(series) || typeof series[0] !== "object" || isArray(series[0])) {
    series = [{name: opts.label || "Value", data: series}];
    chart.hideLegend = true;
  } else {
    chart.hideLegend = false;
  }
  if ((opts.discrete === null || opts.discrete === undefined) && keyType !== "bubble" && keyType !== "number") {
    chart.discrete = detectDiscrete(series);
  } else {
    chart.discrete = opts.discrete;
  }
  if (chart.discrete) {
    keyType = "string";
  }
  if (chart.options.xtype) {
    keyType = chart.options.xtype;
  }

  // right format
  series = copySeries(series);
  for (i = 0; i < series.length; i++) {
    series[i].data = formatSeriesData(toArr(series[i].data), keyType);
  }

  return series;
}

function processSimple(chart) {
  let perfectData = toArr(chart.rawData), i;
  for (i = 0; i < perfectData.length; i++) {
    perfectData[i] = [toStr(perfectData[i][0]), toFloat(perfectData[i][1])];
  }
  return perfectData;
}

// define classes

class Chart {
  constructor(element, dataSource, options) {
    let elementId;
    if (typeof element === "string") {
      elementId = element;
      element = document.getElementById(element);
      if (!element) {
        throw new Error("No element with id " + elementId);
      }
    }
    this.element = element;
    this.options = merge(Chartkick.options, options || {});
    this.dataSource = dataSource;

    Chartkick.charts[element.id] = this;

    fetchDataSource(this, dataSource);

    if (this.options.refresh) {
      this.startRefresh();
    }
  }

  getElement() {
    return this.element;
  }

  getDataSource() {
    return this.dataSource;
  }

  getData() {
    return this.data;
  }

  getOptions() {
    return this.options;
  }

  getChartObject() {
    return this.chart;
  }

  getAdapter() {
    return this.adapter;
  }

  updateData(dataSource, options) {
    this.dataSource = dataSource;
    if (options) {
      this.__updateOptions(options);
    }
    fetchDataSource(this, dataSource);
  }

  setOptions(options) {
    this.__updateOptions(options);
    this.redraw();
  }

  redraw() {
    fetchDataSource(this, this.rawData);
  }

  refreshData() {
    if (typeof this.dataSource === "string") {
      // prevent browser from caching
      let sep = this.dataSource.indexOf("?") === -1 ? "?" : "&";
      let url = this.dataSource + sep + "_=" + (new Date()).getTime();
      fetchDataSource(this, url);
    }
  }

  startRefresh() {
    let refresh = this.options.refresh;

    if (!this.intervalId) {
      if (refresh) {
        this.intervalId = setInterval( () => {
          this.refreshData();
        }, refresh * 1000);
      } else {
        throw new Error("No refresh interval");
      }
    }
  }

  stopRefresh() {
    if (this.intervalId) {
      clearInterval(this.intervalId);
      this.intervalId = null;
    }
  }

  toImage() {
    if (this.adapter === "chartjs") {
      return this.chart.toBase64Image();
    } else {
      return null;
    }
  }

  __updateOptions(options) {
    let updateRefresh = options.refresh && options.refresh !== this.options.refresh;
    this.options = merge(Chartkick.options, options);
    if (updateRefresh) {
      this.stopRefresh();
      this.startRefresh();
    }
  }

  __render() {
    this.data = this.__processData();
    renderChart(this.__chartName(), this);
  }

  __config() {
    return config;
  }
}

class LineChart extends Chart {
  __processData() {
    return processSeries(this, "datetime");
  }

  __chartName() {
    return "LineChart";
  }
}

class PieChart extends Chart {
  __processData() {
    return processSimple(this);
  }

  __chartName() {
    return "PieChart";
  }
}

class ColumnChart extends Chart {
  __processData() {
    return processSeries(this, "string");
  }

  __chartName() {
    return "ColumnChart";
  }
}

class BarChart extends Chart {
  __processData() {
    return processSeries(this, "string");
  }

  __chartName() {
    return "BarChart";
  }
}

class AreaChart extends Chart {
  __processData() {
    return processSeries(this, "datetime");
  }

  __chartName() {
    return "AreaChart";
  }
}

class GeoChart extends Chart {
  __processData() {
    return processSimple(this);
  }

  __chartName() {
    return "GeoChart";
  }
}

class ScatterChart extends Chart {
  __processData() {
    return processSeries(this, "number");
  }

  __chartName() {
    return "ScatterChart";
  }
}

class BubbleChart extends Chart {
  __processData() {
    return processSeries(this, "bubble");
  }

  __chartName() {
    return "BubbleChart";
  }
}

class Timeline extends Chart {
  __processData() {
    let i, data = this.rawData;
    for (i = 0; i < data.length; i++) {
      data[i][1] = toDate(data[i][1]);
      data[i][2] = toDate(data[i][2]);
    }
    return data;
  }

  __chartName() {
    return "Timeline";
  }
}

const Chartkick = {
  LineChart: LineChart,
  PieChart: PieChart,
  ColumnChart: ColumnChart,
  BarChart: BarChart,
  AreaChart: AreaChart,
  GeoChart: GeoChart,
  ScatterChart: ScatterChart,
  BubbleChart: BubbleChart,
  Timeline: Timeline,
  charts: {},
  configure: function (options) {
    for (let key in options) {
      if (options.hasOwnProperty(key)) {
        config[key] = options[key];
      }
    }
  },
  eachChart: function (callback) {
    for (let chartId in Chartkick.charts) {
      if (Chartkick.charts.hasOwnProperty(chartId)) {
        callback(Chartkick.charts[chartId]);
      }
    }
  },
  config: config,
  options: {},
  adapters: adapters
};

export default Chartkick;
<|endoftext|>


<commit_before>
/**
 * THIS IS A GENERATED FILE
 *
 * Please see /tools/generateIndices.js to regenerate after structural changes or component additions / deletions
 */

export { default as animations } from './animations';
export { default as Expand } from './animations/Expand';
export { default as behaviors } from './behaviors';
export { default as ScrollBox } from './behaviors/ScrollBox';
export { default as components } from './components';
export { default as Accordion } from './components/Accordion';
export { default as AccordionGroup } from './components/Accordion/AccordionGroup';
export { default as Alert } from './components/Alert';
export { default as Anchor } from './components/Anchor';
export { default as BandwidthThemeProvider } from './components/BandwidthThemeProvider';
export { default as Breadcrumb } from './components/Breadcrumb';
export { default as Button } from './components/Button';
export { default as SubmitButton } from './components/Button/SubmitButton';
export { default as Callout } from './components/Callout';
export { default as Card } from './components/Card';
export { default as Checkbox } from './components/Checkbox';
export { default as Code } from './components/Code';
export { default as CodeBlock } from './components/Code/CodeBlock';
export { default as CodeContainer } from './components/Code/CodeContainer';
export { default as Comments } from './components/Comments';
export { default as FileLoader } from './components/FileLoader';
export { default as Gutter } from './components/Gutter';
export { default as Header } from './components/Header';
export { default as SubHeader } from './components/Header/SubHeader';
export { default as HelpText } from './components/HelpText';
export { default as Icon } from './components/Icon';
export { default as Input } from './components/Input';
export { default as Label } from './components/Label';
export { default as List } from './components/List';
export { default as ListItem } from './components/List/ListItem';
export { default as OrderedList } from './components/List/OrderedList';
export { default as UnorderedList } from './components/List/UnorderedList';
export { default as Loader } from './components/Loader';
export { default as Logo } from './components/Logo';
export { default as MethodTag } from './components/MethodTag';
export { default as Modal } from './components/Modal';
export { default as Money } from './components/Money';
export { default as Navigation } from './components/Navigation';
export { default as LogoHeader } from './components/Navigation/LogoHeader';
export { default as NavigationItem } from './components/Navigation/NavigationItem';
export { default as NavigationItems } from './components/Navigation/NavigationItems';
export { default as NewBadge } from './components/NewBadge';
export { default as Pagination } from './components/Pagination';
export { default as Radio } from './components/Radio';
export { default as RadioGroup } from './components/RadioGroup';
export { default as RadioButton } from './components/RadioGroup/RadioButton';
export { default as Select } from './components/Select';
export { default as SidebarList } from './components/SidebarList';
export { default as ShowMore } from './components/SidebarList/ShowMore';
export { default as SidebarListItem } from './components/SidebarList/SidebarListItem';
export { default as SortOptions } from './components/SortOptions';
export { default as Spacing } from './components/Spacing';
export { default as Steps } from './components/Steps';
export { default as Summary } from './components/Summary';
export { default as Table } from './components/Table';
export { default as TableCell } from './components/Table/TableCell';
export { default as TableControls } from './components/Table/TableControls';
export { default as TableHeader } from './components/Table/TableHeader';
export { default as TableRow } from './components/Table/TableRow';
export { default as Tabs } from './components/Tabs';
export { default as TextArea } from './components/TextArea';
export { default as Toast } from './components/Toast';
export { default as Toggle } from './components/Toggle';
export { default as childrenWithProps } from './extensions/childrenWithProps';
export { default as formatMoney } from './extensions/formatMoney';
export { default as generateId } from './extensions/generateId';
export { default as parseFlex } from './extensions/parseFlex';
export { default as provinces } from './extensions/provinces';
export { default as layouts } from './layouts';
export { default as Flow } from './layouts/Flow';
export { default as FlowFieldHoc } from './layouts/Flow/FlowFieldHoc';
export { default as FlowItem } from './layouts/Flow/FlowItem';
export { default as FlowRow } from './layouts/Flow/FlowRow';
export { default as Form } from './layouts/Form';
export { default as ActionFields } from './layouts/Form/ActionFields';
export { default as AnchorField } from './layouts/Form/AnchorField';
export { default as BinaryField } from './layouts/Form/BinaryField';
export { default as ButtonField } from './layouts/Form/ButtonField';
export { default as CheckboxField } from './layouts/Form/CheckboxField';
export { default as FieldGroup } from './layouts/Form/FieldGroup';
export { default as FieldWrapper } from './layouts/Form/FieldWrapper';
export { default as FileField } from './layouts/Form/FileField';
export { default as FlexFields } from './layouts/Form/FlexFields';
export { default as FormBox } from './layouts/Form/FormBox';
export { default as FormColumn } from './layouts/Form/FormColumn';
export { default as FormSectionHeader } from './layouts/Form/FormSectionHeader';
export { default as FormText } from './layouts/Form/FormText';
export { default as MultiFieldWrapper } from './layouts/Form/MultiFieldWrapper';
export { default as RadioField } from './layouts/Form/RadioField';
export { default as SelectField } from './layouts/Form/SelectField';
export { default as SubmitButtonField } from './layouts/Form/SubmitButtonField';
export { default as TextAreaField } from './layouts/Form/TextAreaField';
export { default as TextField } from './layouts/Form/TextField';
export { default as ToggleField } from './layouts/Form/ToggleField';
export { default as Page } from './layouts/Page';
export { default as Pane } from './layouts/Pane';
export { default as PaneColumn } from './layouts/Pane/PaneColumn';
export { default as PaneContent } from './layouts/Pane/PaneContent';
export { default as PaneLayout } from './layouts/Pane/PaneLayout';
export { default as PaneRow } from './layouts/Pane/PaneRow';
export { default as PaneSection } from './layouts/Pane/PaneSection';
export { default as SidebarLayout } from './layouts/SidebarLayout';
export { default as theme } from './theme';
export { default as baseTheme } from './theme/baseTheme';
export { default as defaultTheme } from './theme/defaultTheme';
export { default as errorTheme } from './theme/errorTheme';
export { default as index } from './theme/index';
export { default as largeTheme } from './theme/largeTheme';
export { default as secondaryTheme } from './theme/secondaryTheme';
export { default as smallTheme } from './theme/smallTheme';<commit_msg>Rebuild index<commit_after>
/**
 * THIS IS A GENERATED FILE
 *
 * Please see /tools/generateIndices.js to regenerate after structural changes or component additions / deletions
 */

export { default as animations } from './animations';
export { default as Expand } from './animations/Expand';
export { default as behaviors } from './behaviors';
export { default as ScrollBox } from './behaviors/ScrollBox';
export { default as components } from './components';
export { default as Accordion } from './components/Accordion';
export { default as AccordionGroup } from './components/Accordion/AccordionGroup';
export { default as Alert } from './components/Alert';
export { default as Anchor } from './components/Anchor';
export { default as BandwidthThemeProvider } from './components/BandwidthThemeProvider';
export { default as Breadcrumb } from './components/Breadcrumb';
export { default as Button } from './components/Button';
export { default as SubmitButton } from './components/Button/SubmitButton';
export { default as Callout } from './components/Callout';
export { default as Card } from './components/Card';
export { default as Checkbox } from './components/Checkbox';
export { default as Code } from './components/Code';
export { default as CodeBlock } from './components/Code/CodeBlock';
export { default as CodeContainer } from './components/Code/CodeContainer';
export { default as Comments } from './components/Comments';
export { default as FileLoader } from './components/FileLoader';
export { default as Gutter } from './components/Gutter';
export { default as Header } from './components/Header';
export { default as SubHeader } from './components/Header/SubHeader';
export { default as HelpText } from './components/HelpText';
export { default as Icon } from './components/Icon';
export { default as Input } from './components/Input';
export { default as Label } from './components/Label';
export { default as List } from './components/List';
export { default as ListItem } from './components/List/ListItem';
export { default as OrderedList } from './components/List/OrderedList';
export { default as UnorderedList } from './components/List/UnorderedList';
export { default as Loader } from './components/Loader';
export { default as Logo } from './components/Logo';
export { default as MethodTag } from './components/MethodTag';
export { default as Modal } from './components/Modal';
export { default as Money } from './components/Money';
export { default as Navigation } from './components/Navigation';
export { default as LogoHeader } from './components/Navigation/LogoHeader';
export { default as NavigationItem } from './components/Navigation/NavigationItem';
export { default as NavigationItems } from './components/Navigation/NavigationItems';
export { default as NewBadge } from './components/NewBadge';
export { default as Pagination } from './components/Pagination';
export { default as Radio } from './components/Radio';
export { default as RadioGroup } from './components/RadioGroup';
export { default as RadioButton } from './components/RadioGroup/RadioButton';
export { default as Select } from './components/Select';
export { default as SidebarList } from './components/SidebarList';
export { default as ShowMore } from './components/SidebarList/ShowMore';
export { default as SidebarListItem } from './components/SidebarList/SidebarListItem';
export { default as SortOptions } from './components/SortOptions';
export { default as Spacing } from './components/Spacing';
export { default as Steps } from './components/Steps';
export { default as Summary } from './components/Summary';
export { default as Table } from './components/Table';
export { default as TableCell } from './components/Table/TableCell';
export { default as TableControls } from './components/Table/TableControls';
export { default as TableHeader } from './components/Table/TableHeader';
export { default as TableRow } from './components/Table/TableRow';
export { default as Tabs } from './components/Tabs';
export { default as TextArea } from './components/TextArea';
export { default as Toast } from './components/Toast';
export { default as Toggle } from './components/Toggle';
export { default as childrenWithProps } from './extensions/childrenWithProps';
export { default as formatMoney } from './extensions/formatMoney';
export { default as generateId } from './extensions/generateId';
export { default as parseFlex } from './extensions/parseFlex';
export { default as provinces } from './extensions/provinces';
export { default as layouts } from './layouts';
export { default as Flow } from './layouts/Flow';
export { default as FlowItem } from './layouts/Flow/FlowItem';
export { default as FlowRow } from './layouts/Flow/FlowRow';
export { default as fields } from './layouts/Flow/fields';
export { default as FlowCheckbox } from './layouts/Flow/fields/FlowCheckbox';
export { default as FlowFile } from './layouts/Flow/fields/FlowFile';
export { default as FlowInput } from './layouts/Flow/fields/FlowInput';
export { default as FlowMulti } from './layouts/Flow/fields/FlowMulti';
export { default as FlowRadioGroup } from './layouts/Flow/fields/FlowRadioGroup';
export { default as FlowSelect } from './layouts/Flow/fields/FlowSelect';
export { default as FlowTextArea } from './layouts/Flow/fields/FlowTextArea';
export { default as FlowToggle } from './layouts/Flow/fields/FlowToggle';
export { default as Form } from './layouts/Form';
export { default as ActionFields } from './layouts/Form/ActionFields';
export { default as AnchorField } from './layouts/Form/AnchorField';
export { default as BinaryField } from './layouts/Form/BinaryField';
export { default as ButtonField } from './layouts/Form/ButtonField';
export { default as CheckboxField } from './layouts/Form/CheckboxField';
export { default as FieldGroup } from './layouts/Form/FieldGroup';
export { default as FieldWrapper } from './layouts/Form/FieldWrapper';
export { default as FileField } from './layouts/Form/FileField';
export { default as FlexFields } from './layouts/Form/FlexFields';
export { default as FormBox } from './layouts/Form/FormBox';
export { default as FormColumn } from './layouts/Form/FormColumn';
export { default as FormSectionHeader } from './layouts/Form/FormSectionHeader';
export { default as FormText } from './layouts/Form/FormText';
export { default as MultiFieldWrapper } from './layouts/Form/MultiFieldWrapper';
export { default as RadioField } from './layouts/Form/RadioField';
export { default as SelectField } from './layouts/Form/SelectField';
export { default as SubmitButtonField } from './layouts/Form/SubmitButtonField';
export { default as TextAreaField } from './layouts/Form/TextAreaField';
export { default as TextField } from './layouts/Form/TextField';
export { default as ToggleField } from './layouts/Form/ToggleField';
export { default as Page } from './layouts/Page';
export { default as Pane } from './layouts/Pane';
export { default as PaneColumn } from './layouts/Pane/PaneColumn';
export { default as PaneContent } from './layouts/Pane/PaneContent';
export { default as PaneLayout } from './layouts/Pane/PaneLayout';
export { default as PaneRow } from './layouts/Pane/PaneRow';
export { default as PaneSection } from './layouts/Pane/PaneSection';
export { default as SidebarLayout } from './layouts/SidebarLayout';
export { default as theme } from './theme';
export { default as baseTheme } from './theme/baseTheme';
export { default as defaultTheme } from './theme/defaultTheme';
export { default as errorTheme } from './theme/errorTheme';
export { default as index } from './theme/index';
export { default as largeTheme } from './theme/largeTheme';
export { default as secondaryTheme } from './theme/secondaryTheme';
export { default as smallTheme } from './theme/smallTheme';<|endoftext|>


<commit_before>
var parsers = require('../lib/parsers');

exports['Evaluate integer'] = function (test) {
    var parser = parsers.createParser("123");
    var expr = parser.parse("Integer");
    var result = expr.value.evaluate(null);

    test.ok(result);
    test.equal(result, 123);

    test.equal(parser.parse("Integer"), null);
};

exports['Evaluate integer as term'] = function (test) {
    var parser = parsers.createParser("123");
    var expr = parser.parse("Term");
    var result = expr.value.evaluate(null);

    test.ok(result);
    test.equal(result, 123);

    test.equal(parser.parse("Term"), null);
};

exports['Evaluate add integers'] = function (test) {
    var parser = parsers.createParser("1+2");
    var expr = parser.parse("Expression");
    var result = expr.value.evaluate(null);

    test.ok(result);
    test.equal(result, 3);

    test.equal(parser.parse("Expression"), null);
};

exports['Evaluate subtract integers'] = function (test) {
    var parser = parsers.createParser("1-2");
    var expr = parser.parse("Expression");
    var result = expr.value.evaluate(null);

    test.ok(result);
    test.equal(result, -1);

    test.equal(parser.parse("Expression"), null);
};

exports['Evaluate multiply integers'] = function (test) {
    var parser = parsers.createParser("2*3");
    var expr = parser.parse("Expression");
    var result = expr.value.evaluate(null);

    test.ok(result);
    test.equal(result, 6);

    test.equal(parser.parse("Expression"), null);
};

exports['Evaluate divide integers'] = function (test) {
    var parser = parsers.createParser("2/3");
    var expr = parser.parse("Expression");
    var result = expr.value.evaluate(null);

    test.ok(result);
    test.equal(result, 2/3);

    test.equal(parser.parse("Expression"), null);
};

exports['Evaluate add and multiply'] = function (test) {
    var parser = parsers.createParser("2+3*4");
    var expr = parser.parse("Expression");
    var result = expr.value.evaluate(null);

    test.ok(result);
    test.equal(result, 14);

    test.equal(parser.parse("Expression"), null);
};<commit_msg>Additional test: more precedence expressions<commit_after>
var parsers = require('../lib/parsers');

exports['Evaluate integer'] = function (test) {
    var parser = parsers.createParser("123");
    var expr = parser.parse("Integer");
    var result = expr.value.evaluate(null);

    test.ok(result);
    test.equal(result, 123);

    test.equal(parser.parse("Integer"), null);
};

exports['Evaluate integer as term'] = function (test) {
    var parser = parsers.createParser("123");
    var expr = parser.parse("Term");
    var result = expr.value.evaluate(null);

    test.ok(result);
    test.equal(result, 123);

    test.equal(parser.parse("Term"), null);
};

exports['Evaluate add integers'] = function (test) {
    var parser = parsers.createParser("1+2");
    var expr = parser.parse("Expression");
    var result = expr.value.evaluate(null);

    test.ok(result);
    test.equal(result, 3);

    test.equal(parser.parse("Expression"), null);
};

exports['Evaluate subtract integers'] = function (test) {
    var parser = parsers.createParser("1-2");
    var expr = parser.parse("Expression");
    var result = expr.value.evaluate(null);

    test.ok(result);
    test.equal(result, -1);

    test.equal(parser.parse("Expression"), null);
};

exports['Evaluate multiply integers'] = function (test) {
    var parser = parsers.createParser("2*3");
    var expr = parser.parse("Expression");
    var result = expr.value.evaluate(null);

    test.ok(result);
    test.equal(result, 6);

    test.equal(parser.parse("Expression"), null);
};

exports['Evaluate divide integers'] = function (test) {
    var parser = parsers.createParser("2/3");
    var expr = parser.parse("Expression");
    var result = expr.value.evaluate(null);

    test.ok(result);
    test.equal(result, 2/3);

    test.equal(parser.parse("Expression"), null);
};

exports['Evaluate add and multiply'] = function (test) {
    var parser = parsers.createParser("2+3*4");
    var expr = parser.parse("Expression");
    var result = expr.value.evaluate(null);

    test.ok(result);
    test.equal(result, 14);

    test.equal(parser.parse("Expression"), null);
};

exports['Evaluate add and divide'] = function (test) {
    var parser = parsers.createParser("2+3/4");
    var expr = parser.parse("Expression");
    var result = expr.value.evaluate(null);

    test.ok(result);
    test.equal(result, 2 + 3 / 4);

    test.equal(parser.parse("Expression"), null);
};

exports['Evaluate multiply and add'] = function (test) {
    var parser = parsers.createParser("2*3+4");
    var expr = parser.parse("Expression");
    var result = expr.value.evaluate(null);

    test.ok(result);
    test.equal(result, 10);

    test.equal(parser.parse("Expression"), null);
};

exports['Evaluate divide and subtract'] = function (test) {
    var parser = parsers.createParser("2/3-4");
    var expr = parser.parse("Expression");
    var result = expr.value.evaluate(null);

    test.ok(result);
    test.equal(result, 2/3-4);

    test.equal(parser.parse("Expression"), null);
};

<|endoftext|>


<commit_before>/**
 * Polymer Redux
 *
 * Creates a Class mixin for decorating Elements with a given Redux store.
 *
 * @param {Object} store Redux store.
 * @return {Function} Class mixin.
 */
export default function(store) {
    const subscribers = new Map()

    /**
     * Binds element's properties to state changes from the Redux store.
     *
     * @example
     *     const update = bind(el, props) // set bindings
     *     update(state) // manuel update
     *
     * @private
     * @param {HTMLElement} element
     * @param {Object} properties
     * @return {Function} Update function.
     */
    const bind = (element, properties) => {
        if (subscribers.has(element)) return

        const bindings = Object.keys(properties)
            .filter((name) => properties[name].hasOwnProperty('statePath'))

        /**
         * Updates an element's properties with the given state.
         *
         * @private
         * @param {Object} state
         */
        const update = (state) => {
            bindings.forEach((name) => {
                const { statePath, readOnly } = properties[name]
                const value = (typeof statePath === 'function')
                    ? statePath.call(element, state)
                    : Polymer.Path.get(state, statePath)

                if (readOnly) {
                    element._setProperty(name, value)
                } else {
                    element[name] = value
                }
            })
        }

        // redux listener
        const unsubscribe = store.subscribe(() => {
            const state = store.getState()
            update(state)

            element.dispatchEvent(new CustomEvent('state-changed', {
                detail: state
            }))
        })

        subscribers.set(element, unsubscribe)

        return update(store.getState())
    }

    /**
     * Unbinds an element from state changes in the Redux store.
     *
     * @private
     * @param {HTMLElement} element
     */
    const unbind = (element) => {
        const off = subscribers.get(element)
        if (typeof off === 'function') off()
    }

    /**
     * Redux Mixin
     *
     * @example
     *     const ReduxMixin = PolymerRedux(store)
     *     class Foo extends ReduxMixin(Polymer.Element) { }
     *
     * @param {Polymer.Element} parent The polymer parent element.
     * @return {Function} PolymerRedux mixed class.
     */
    return (parent) => class extends parent {
        connectedCallback() {
            super.connectedCallback()

            const config = this.constructor.config
            const properties = config && config.properties

            bind(this, properties || {})
        }

        disconnectedCallback() {
            super.disconnectedCallback()
            unbind(this)
        }

        /**
         * Dispatches an action to the Redux store.
         *
         * @example
         *     element.dispatch({ type: 'ACTION' })
         *
         * @example
         *     element.dispatch('actionCreator', 'foo', 'bar')
         *
         * @example
         *     element.dispatch((dispatch) => {
         *         dispatch({ type: 'MIDDLEWARE'})
         *     })
         *
         * @param  {...*} args
         * @return {Object} The action.
         */
        dispatch(...args) {
            const config = this.constructor.config
            const actions = config && config.actions

            // action creator
            let [ action ] = args
            if (typeof action === 'string') {
                if (!actions || typeof actions[action] !== 'function') {
                    throw new TypeError(`PolymerRedux: <${this.constructor.is}> has no action creator "${action}"`)
                }
                action = actions[action].apply(this.constructor, args.slice(1))
            }

            return store.dispatch(action)
        }
    }
}
<commit_msg>adding two-way bindings warning<commit_after>/**
 * Polymer Redux
 *
 * Creates a Class mixin for decorating Elements with a given Redux store.
 *
 * @param {Object} store Redux store.
 * @return {Function} Class mixin.
 */
export default function(store) {
    const subscribers = new Map()

    /**
     * Binds element's properties to state changes from the Redux store.
     *
     * @example
     *     const update = bind(el, props) // set bindings
     *     update(state) // manuel update
     *
     * @private
     * @param {HTMLElement} element
     * @param {Object} properties
     * @return {Function} Update function.
     */
    const bind = (element, properties) => {
        if (subscribers.has(element)) return

        const bindings = Object.keys(properties)
            .filter((name) => {
                const property = properties[name]
                if (property.hasOwnProperty('statePath') {
                    if (!property.readOnly && property.notify) {
                        console.warn(`PolymerRedux: <${element.constructor.is}>.${name} has "notify" enabled, two-way bindings goes against Redux's paradigm`)
                    }
                    return true
                }
                return false
            })

        /**
         * Updates an element's properties with the given state.
         *
         * @private
         * @param {Object} state
         */
        const update = (state) => {
            bindings.forEach((name) => {
                const { statePath, readOnly } = properties[name]
                const value = (typeof statePath === 'function')
                    ? statePath.call(element, state)
                    : Polymer.Path.get(state, statePath)

                if (readOnly) {
                    element._setProperty(name, value)
                } else {
                    element[name] = value
                }
            })
        }

        // redux listener
        const unsubscribe = store.subscribe(() => {
            const state = store.getState()
            update(state)

            element.dispatchEvent(new CustomEvent('state-changed', {
                detail: state
            }))
        })

        subscribers.set(element, unsubscribe)

        return update(store.getState())
    }

    /**
     * Unbinds an element from state changes in the Redux store.
     *
     * @private
     * @param {HTMLElement} element
     */
    const unbind = (element) => {
        const off = subscribers.get(element)
        if (typeof off === 'function') off()
    }

    /**
     * Redux Mixin
     *
     * @example
     *     const ReduxMixin = PolymerRedux(store)
     *     class Foo extends ReduxMixin(Polymer.Element) { }
     *
     * @param {Polymer.Element} parent The polymer parent element.
     * @return {Function} PolymerRedux mixed class.
     */
    return (parent) => class extends parent {
        connectedCallback() {
            super.connectedCallback()

            const config = this.constructor.config
            const properties = config && config.properties

            bind(this, properties || {})
        }

        disconnectedCallback() {
            super.disconnectedCallback()
            unbind(this)
        }

        /**
         * Dispatches an action to the Redux store.
         *
         * @example
         *     element.dispatch({ type: 'ACTION' })
         *
         * @example
         *     element.dispatch('actionCreator', 'foo', 'bar')
         *
         * @example
         *     element.dispatch((dispatch) => {
         *         dispatch({ type: 'MIDDLEWARE'})
         *     })
         *
         * @param  {...*} args
         * @return {Object} The action.
         */
        dispatch(...args) {
            const config = this.constructor.config
            const actions = config && config.actions

            // action creator
            let [ action ] = args
            if (typeof action === 'string') {
                if (!actions || typeof actions[action] !== 'function') {
                    throw new TypeError(`PolymerRedux: <${this.constructor.is}> has no action creator "${action}"`)
                }
                action = actions[action].apply(this.constructor, args.slice(1))
            }

            return store.dispatch(action)
        }
    }
}
<|endoftext|>


<commit_before>import {mockFunction, matchers} from '../src';
import {expect} from 'chai';

describe('examples', () => {
  describe('synchronous', () => {
    it('synchronous return', () => {
      let ninja = {
        getName: mockFunction()
      };

      ninja.getName.when().then.return('Ryu');
      expect(ninja.getName()).to.equal('Ryu');
    });

    it('synchronous error', done => {
      let ninja = {
        getWeapons: mockFunction()
      };
      ninja.getWeapons.when('swords').then.error('No swords present');
      //We didn't specify a no argument when, so this should not error.
      expect(ninja.getWeapons('stars')).not.to.be.ok;
      try {
        //This should throw an error
        ninja.getWeapons('swords');
      } catch (err){
        expect(err).to.be.ok;
        expect(err.message).to.equal('No swords present');
        done();
      }
    });
  });

  describe('callbacks', () => {
    it('callback return ', done => {
      let ninja = {
        flipOut: mockFunction()
      };
      ninja.flipOut.when(5).then.return('5 flip outs');
      ninja.flipOut(5, (err, result) => {
        expect(result).to.equal('5 flip outs');
        done(err);
      });
    });

    it('callback soft error', done => {
      let ninja = {
        sneak: mockFunction()
      };
      ninja.sneak.when().then.error('Not very sneaky');
      ninja.sneak(err => {
        expect(err.message).to.equal('Not very sneaky');
        done();
      });
    });

    it('callback hard error', done => {
      let ninja = {
        sing: mockFunction()
      };
      ninja.sing.when().then.forceError('Really bad at singing');
      try{
        ninja.sing(err => done(err));
      } catch (err) {
        expect(err.message).to.equal('Really bad at singing');
        done();
      }
    });
  });

  describe('promises', () => {
    it('promise resolve', done => {
      let ninja = {
        fight: mockFunction()
      };
      ninja.fight.when('pirate').then.promise.to.return('win');
      ninja.fight('pirate').then(result => {
        expect(result).to.equal('win');
        done();
      });
    });

    it('promise reject', done => {
      let ninja = {
        grapple: mockFunction()
      };
      ninja.grapple.when('pirate').then.promise.to.error('lose');
      ninja.grapple('pirate').catch(err => {
        expect(err.message).to.equal('lose');
        done();
      });
    });
  });

  describe('matchers', () => {
    it('any matcher', () => {
      let ninja = {
        roundhouse: mockFunction()
      };
      ninja.roundhouse.when(matchers.any(), matchers.any(), 3).then.return('Amazing');
      expect(ninja.roundhouse(null, null, 3)).to.equal('Amazing');
      expect(ninja.roundhouse(1, 'Bob', 3)).to.equal('Amazing');
      expect(ninja.roundhouse(undefined, NaN, 3)).to.equal('Amazing');
    });
  });

  describe('verification', () => {
    it('number of calls', () => {
      let ninja = {
        roundhouse: mockFunction()
      };
      ninja.roundhouse();
      expect(ninja.roundhouse.called()).to.have.length.of(1);
    });

    it('full call history', () => {
      let ninja = {
        roundhouse: mockFunction()
      };
      ninja.roundhouse('a');
      ninja.roundhouse('b');
      const callHistory = ninja.roundhouse.called();
      expect(callHistory).to.have.length.of(2);
      expect(callHistory[0].args).to.deep.equal(['a']);
      expect(callHistory[0].scope).to.equal(ninja);
      expect(callHistory[1].args).to.deep.equal(['b']);
      expect(callHistory[1].scope).to.equal(ninja);
    });

    it('search call history', () => {
      let ninja = {
        roundhouse: mockFunction()
      };
      ninja.roundhouse('a');
      ninja.roundhouse('b');
      const callHistory = ninja.roundhouse.called.with('a');
      expect(callHistory).to.have.length.of(1);
      expect(callHistory[0].args).to.deep.equal(['a']);
      expect(callHistory[0].scope).to.equal(ninja);
    });
  });
});
<commit_msg>Added verification examples.<commit_after>import {mockFunction, matchers} from '../src';
import {expect} from 'chai';

describe('examples', () => {
  describe('synchronous', () => {
    it('synchronous return', () => {
      let ninja = {
        getName: mockFunction()
      };

      ninja.getName.when().then.return('Ryu');
      expect(ninja.getName()).to.equal('Ryu');
    });

    it('synchronous error', done => {
      let ninja = {
        getWeapons: mockFunction()
      };
      ninja.getWeapons.when('swords').then.error('No swords present');
      //We didn't specify a no argument when, so this should not error.
      expect(ninja.getWeapons('stars')).not.to.be.ok;
      try {
        //This should throw an error
        ninja.getWeapons('swords');
      } catch (err){
        expect(err).to.be.ok;
        expect(err.message).to.equal('No swords present');
        done();
      }
    });
  });

  describe('callbacks', () => {
    it('callback return ', done => {
      let ninja = {
        flipOut: mockFunction()
      };
      ninja.flipOut.when(5).then.return('5 flip outs');
      ninja.flipOut(5, (err, result) => {
        expect(result).to.equal('5 flip outs');
        done(err);
      });
    });

    it('callback soft error', done => {
      let ninja = {
        sneak: mockFunction()
      };
      ninja.sneak.when().then.error('Not very sneaky');
      ninja.sneak(err => {
        expect(err.message).to.equal('Not very sneaky');
        done();
      });
    });

    it('callback hard error', done => {
      let ninja = {
        sing: mockFunction()
      };
      ninja.sing.when().then.forceError('Really bad at singing');
      try{
        ninja.sing(err => done(err));
      } catch (err) {
        expect(err.message).to.equal('Really bad at singing');
        done();
      }
    });
  });

  describe('promises', () => {
    it('promise resolve', done => {
      let ninja = {
        fight: mockFunction()
      };
      ninja.fight.when('pirate').then.promise.to.return('win');
      ninja.fight('pirate').then(result => {
        expect(result).to.equal('win');
        done();
      });
    });

    it('promise reject', done => {
      let ninja = {
        grapple: mockFunction()
      };
      ninja.grapple.when('pirate').then.promise.to.error('lose');
      ninja.grapple('pirate').catch(err => {
        expect(err.message).to.equal('lose');
        done();
      });
    });
  });

  describe('matchers', () => {
    it('any matcher', () => {
      let ninja = {
        roundhouse: mockFunction()
      };
      ninja.roundhouse.when(matchers.any(), matchers.any(), 3).then.return('Amazing');
      expect(ninja.roundhouse(null, null, 3)).to.equal('Amazing');
      expect(ninja.roundhouse(1, 'Bob', 3)).to.equal('Amazing');
      expect(ninja.roundhouse(undefined, NaN, 3)).to.equal('Amazing');
    });
  });

  describe('verification', () => {
    it('number of calls', () => {
      let ninja = {
        roundhouse: mockFunction()
      };
      ninja.roundhouse();
      expect(ninja.roundhouse.called()).to.have.length.of(1);
    });

    it('full call history', () => {
      let ninja = {
        roundhouse: mockFunction()
      };
      ninja.roundhouse('a');
      ninja.roundhouse('b');
      const callHistory = ninja.roundhouse.called();
      expect(callHistory).to.have.length.of(2);
      expect(callHistory[0].args).to.deep.equal(['a']);
      expect(callHistory[0].scope).to.equal(ninja);
      expect(callHistory[1].args).to.deep.equal(['b']);
      expect(callHistory[1].scope).to.equal(ninja);
    });

    it('search call history', () => {
      let ninja = {
        roundhouse: mockFunction()
      };
      ninja.roundhouse('a');
      ninja.roundhouse('b');
      expect(ninja.roundhouse.called.with('a')).to.be.ok;
    });
  });
});
<|endoftext|>


<commit_before>const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const fs = require('fs');
app.use(bodyParser.urlencoded({ extended: true }));
const HashMap = require('hashmap');
const dirname = fs.realpathSync('./');

var teams = new HashMap();
var ingredients = JSON.parse(fs.readFileSync(__dirname + '/data/ingredients.json', 'utf8'));
var suggestions = new HashMap();

// build api endpoint for getting js files
fs.readdirSync(dirname + '/src/js').forEach(file => {
    app.get('/js/' + file, function (req, res) {
        res.status(200).sendFile(dirname + '/src/js/' + file);
    });
});

// build api endpoint for getting bootstrap files
fs.readdirSync(dirname + '/node_modules/bootstrap/dist/css').forEach(file => {
    app.get('/css/' + file, function (req, res) {
        res.status(200).sendFile(dirname + '/node_modules/bootstrap/dist/css/' + file);
    });
});

app.get('/', function (req, res) {
    res.status(200).sendFile(__dirname + "/index.htm");
});

app.get('/teams', function (req, res) {
    res.status(200).sendFile(__dirname + "/teamPage.html");
});

app.get('/pizzas/amount', function (req, res) {
    let teamname = req.body.teamname;
    if (teamname != undefined) {
        let amount = {
            amount: teams.get(teamname).pizza_count
        };
        res.status(200).end(JSON.stringify(amount));
    } else {
        res.sendStatus(400);
    }
});

app.get('/pizzas/ingredients', function (req, res) {
    if (ingredients.length > 0) {
        res.status(200).end(JSON.stringify(ingredients));
    } else {
        res.sendStatus(404);
    }
});

app.get('/pizzas/suggestions', function (req, res) {
    let teamname = req.body.teamname;
    if (teamname != undefined) {
        // TODO
    } else {
        res.sendStatus(400);
    }
});

app.post('/sessioncreate/', function (req, res) {
    let teamname = req.body.teamname;
    if (teamname != undefined) {
        console.log('Creating team ' + teamname);
        let data = {
            teamsize: {
                number: 0,
                type: 'Not defined'
            },
            pizza_count: 0
        };
        teams.set(teamname, data);
        res.status(200).sendFile(__dirname + '/js/teamPage.js');
        res.redirect(302, '/teams/?teamname=' + teamname);
        res.status(200).end(JSON.stringify({ teamname: teamname }));
    } else {
        console.log('Creating Team: Failure because of undefined teamname!');
        res.sendStatus(400);
    }
});

app.post('/teams/teamsize/', function (req, res) {
    let count = req.body.count;
    let type = req.body.type;
    let teamname = req.body.teamname;
    if (count != undefined && type != undefined && teamname != undefined) {
        console.log('Update team size for team' + teamname);
        let pizza_count = computeNumberOfPizzas(count, type);
        let data = {
            teamsize: {
                number: count,
                type: type
            },
            pizza_count: pizza_count
        };
        teams.set(teamname, data);
        res.redirect(302, '/teams/?teamname=' + teamname);
        res.status(200).end(JSON.stringify({ teamname: teamname, data: data }));
    } else {
        console.log('Update team size failed');
        res.sendStatus(400);
    }
});

function computeNumberOfPizzas(count, type) {
    return 4; // TODO
};

function saveToFile(data, filename = "tfmap.log", exitOnWrite = false) {
    let folderName = "results";
    if (!fs.existsSync(folderName)) {
        fs.mkdirSync(folderName);
    }
    fs.writeFileSync('results/' + filename, data);
    console.log("The file was saved!");
    if (exitOnWrite) {
        process.exit(0);
    }
}

var server = app.listen(8081, function () {
    var host = server.address().address;
    var port = server.address().port;
    console.log("REST server listening at http://%s:%s", host, port);
});<commit_msg>Added endpoint for /pizzas/suggest<commit_after>const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const fs = require('fs');
app.use(bodyParser.urlencoded({ extended: true }));
const HashMap = require('hashmap');
const dirname = fs.realpathSync('./');

var teams = new HashMap();
var ingredients = JSON.parse(fs.readFileSync(__dirname + '/data/ingredients.json', 'utf8'));
var team_suggestions = new HashMap();

// build api endpoint for getting js files
fs.readdirSync(dirname + '/src/js').forEach(file => {
    app.get('/js/' + file, function (req, res) {
        res.status(200).sendFile(dirname + '/src/js/' + file);
    });
});

// build api endpoint for getting bootstrap files
fs.readdirSync(dirname + '/node_modules/bootstrap/dist/css').forEach(file => {
    app.get('/css/' + file, function (req, res) {
        res.status(200).sendFile(dirname + '/node_modules/bootstrap/dist/css/' + file);
    });
});

app.get('/', function (req, res) {
    res.status(200).sendFile(__dirname + "/index.htm");
});

app.get('/teams', function (req, res) {
    res.status(200).sendFile(__dirname + "/teamPage.html");
});

app.get('/pizzas/amount', function (req, res) {
    let teamname = req.body.teamname;
    if (teamname != undefined) {
        let amount = {
            amount: teams.get(teamname).pizza_count
        };
        res.status(200).end(JSON.stringify(amount));
    } else {
        res.sendStatus(400);
    }
});

app.get('/pizzas/ingredients', function (req, res) {
    if (ingredients.length > 0) {
        res.status(200).end(JSON.stringify(ingredients));
    } else {
        res.sendStatus(404);
    }
});

app.get('/pizzas/suggestions', function (req, res) {
    let teamname = req.body.teamname;
    if (teamname != undefined) {
        let suggestions = team_suggestions.get(teamname);
        res.status(200).end(JSON.stringify(suggestions));
    } else {
        res.sendStatus(400);
    }
});

app.get('/pizzas/suggest', function (req, res) {
    res.status(200).sendFile(__dirname + '/proposer.html');
});

app.post('/sessioncreate/', function (req, res) {
    let teamname = req.body.teamname;
    if (teamname != undefined) {
        console.log('Creating team ' + teamname);
        let data = {
            teamsize: {
                number: 0,
                type: 'Not defined'
            },
            pizza_count: 0
        };
        teams.set(teamname, data);
        team_suggestions.set(teamname, []);
        res.status(200).sendFile(__dirname + '/js/teamPage.js');
        res.redirect(302, '/teams/?teamname=' + teamname);
        res.status(200).end(JSON.stringify({ teamname: teamname }));
    } else {
        console.log('Creating Team: Failure because of undefined teamname!');
        res.sendStatus(400);
    }
});

app.post('/teams/teamsize/', function (req, res) {
    let count = req.body.count;
    let type = req.body.type;
    let teamname = req.body.teamname;
    if (count != undefined && type != undefined && teamname != undefined) {
        console.log('Update team size for team' + teamname);
        let pizza_count = computeNumberOfPizzas(count, type);
        let data = {
            teamsize: {
                number: count,
                type: type
            },
            pizza_count: pizza_count
        };
        teams.set(teamname, data);
        res.redirect(302, '/teams/?teamname=' + teamname);
        res.status(200).end(JSON.stringify({ teamname: teamname, data: data }));
    } else {
        console.log('Update team size failed');
        res.sendStatus(400);
    }
});

app.post('/pizzas/suggestions', function (req, res) {
    let teamname = req.body.teamname;
    let a = '';
    if (teamname != undefined) {

    } else {
        res.sendStatus(400);
    }
});

app.post('/pizzas/suggestions/vote', function (req, res) {
    let teamname = req.body.teamname;
    let suggestion_id = req.body.id;
    if (teamname != undefined && suggestion_id != undefined) {
        // TODO update voting
    } else {
        res.sendStatus(400);
    }
});

function computeNumberOfPizzas(count, type) {
    return 4; // TODO
};

function saveToFile(data, filename = "tfmap.log", exitOnWrite = false) {
    let folderName = "results";
    if (!fs.existsSync(folderName)) {
        fs.mkdirSync(folderName);
    }
    fs.writeFileSync('results/' + filename, data);
    console.log("The file was saved!");
    if (exitOnWrite) {
        process.exit(0);
    }
}

var server = app.listen(8081, function () {
    var host = server.address().address;
    var port = server.address().port;
    console.log("REST server listening at http://%s:%s", host, port);
});<|endoftext|>


<commit_before>import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import YTSearch from 'youtube-api-search';

import SearchBar   from './components/SearchBar';
import VideoList   from './components/VideoList';
import VideoDetail from './components/VideoDetail';

const API_KEY = process.env.YouTube_DATA_API;

class App extends Component {
  constructor(props) {
    super(props);

    this.state = { videos: [], selectedVideo: null };

    this.videoSearch('surfboards');
  }

  videoSearch(term) {
    YTSearch({key: API_KEY, term}, (videos) => {
      this.setState({
        videos: videos,
        selectedVideo: videos[0]
      });
    });
  }

  render() {
    return (

        <SearchBar onSearchTermChange={term => this.videoSearch(term)} />
        <VideoDetail video={this.state.selectedVideo} />
        <VideoList
         onVideoSelect={selectedVideo => this.setState({selectedVideo})}
         videos={this.state.videos} />

    )
  }
}

ReactDOM.render(<App />, document.querySelector('.container'));
<commit_msg>Add in lodash debounce for search lag fix<commit_after>import React, { Component } from 'react';
import ReactDOM from 'react-dom';

import YTSearch from 'youtube-api-search';
import _ from 'lodash';

import SearchBar   from './components/SearchBar';
import VideoList   from './components/VideoList';
import VideoDetail from './components/VideoDetail';

const API_KEY = process.env.YouTube_DATA_API;

class App extends Component {
  constructor(props) {
    super(props);

    this.state = { videos: [], selectedVideo: null };

    this.videoSearch('surfboards');
  }

  videoSearch(term) {
    YTSearch({key: API_KEY, term}, (videos) => {
      this.setState({
        videos: videos,
        selectedVideo: videos[0]
      });
    });
  }

  render() {
    const videoSearch = _.debounce((term) => { this.videoSearch(term) }, 300);

    return (

        <SearchBar onSearchTermChange={videoSearch} />
        <VideoDetail video={this.state.selectedVideo} />
        <VideoList
         onVideoSelect={selectedVideo => this.setState({selectedVideo})}
         videos={this.state.videos} />

    )
  }
}

ReactDOM.render(<App />, document.querySelector('.container'));
<|endoftext|>


<commit_before>import check from './support/check';

describe('for loops', () => {
  it('transforms basic for-of loops into for-in', () => {
    check(`
      for k of o
        k
    `, `
      for (var k in o) {
        k;
      }
    `);
  });

  it('transforms for-of loops with both key and value into for-in', () => {
    check(`
      for k, v of o
        k
    `, `
      for (var k in o) {
        var v = o[k];
        k;
      }
    `);
  });

  it('transforms for-of loops with both key and value plus unsafe-to-repeat target by saving a reference', () => {
    check(`
      for k, v of object()
        k
    `, `
      var iterable;
      for (var k in (iterable = object())) {
        var v = iterable[k];
        k;
      }
    `);
  });

  it('transforms for-of loops with destructured keys', () => {
    check(`
      for {a, b} of o
        a + b
    `, `
      for (var {a, b} in o) {
        a + b;
      }
    `);
  });

  it('transforms for-of loops with destructured keys and values', () => {
    check(`
      for {a, b}, {x, y} of o
        a + x
    `, `
      for (var key in o) {
        var {a, b} = key;
        var {x, y} = o[key];
        a + x;
      }
    `);
  });

  it('transforms for-of loops with destructured keys and values plus unsafe-to-repeat target', () => {
    check(`
      for {a, b}, {x, y} of object()
        a + x
    `, `
      var iterable;
      for (var key in (iterable = object())) {
        var {a, b} = key;
        var {x, y} = iterable[key];
        a + x;
      }
    `);
  });

  it('transforms for-in loops to typical `for` loops', () => {
    check(`
      for a in b
        a
    `, `
      for (var i = 0, a; i < b.length; i++) {
        a = b[i];
        a;
      }
    `);
  });

  it('allows iterating with for-in by a specific step size', () => {
    check(`
      for a in b by 2
        a
    `, `
      for (var i = 0, a; i < b.length; i += 2) {
        a = b[i];
        a;
      }
    `);
  });

  it('allows iterating with for-in in reverse', () => {
    check(`
      for a in b by -1
        a
    `, `
      for (var i = b.length - 1, a; i >= 0; i--) {
        a = b[i];
        a;
      }
    `);
  });

  it('allows iterating with for-in in reverse with a specific step size', () => {
    check(`
      for a in b by -2
        a
    `, `
      for (var i = b.length - 1, a; i >= 0; i -= 2) {
        a = b[i];
        a;
      }
    `);
  });

  it('allows filtering using a `when` clause', () => {
    check(`
      for a in b when a.c
        a
    `, `
      for (var i = 0, a; i < b.length; i++) {
        a = b[i];
        if (a.c) {
          a;
        }
      }
    `);
  });

  it('allows using both `when` and `by` clauses', () => {
    check(`
      for a in b by 2 when a.c
        a
    `, `
      for (var i = 0, a; i < b.length; i += 2) {
        a = b[i];
        if (a.c) {
          a;
        }
      }
    `);
  });

  it('extracts unsafe-to-repeat iteration targets before the for-in loop', () => {
    check(`
      for e in list()
        break
    `, `
      var iterable = list();
      for (var i = 0, e; i < iterable.length; i++) {
        e = iterable[i];
        break;
      }
    `);
  });

  it('special-cases for-in inclusive range loops to avoid creating arrays', () => {
    check(`
      for i in [0..10]
        i
    `, `
      for (var i = 0; i <= 10; i++) {
        i;
      }
    `);
  });

  it('special-cases for-in exclusive range loops to avoid creating arrays', () => {
    check(`
      for i in [0...10]
        i
    `, `
      for (var i = 0; i < 10; i++) {
        i;
      }
    `);
  });

  it('special-cases descending for-in inclusive range loops to avoid creating arrays', () => {
    check(`
      for i in [10..0]
        i
    `, `
      for (var i = 10; i >= 0; i--) {
        i;
      }
    `);
  });

  it('special-cases descending for-in exclusive range loops to avoid creating arrays', () => {
    check(`
      for i in [10...0]
        i
    `, `
      for (var i = 10; i > 0; i--) {
        i;
      }
    `);
  });

  it('special-cases descending for-in range loops with step count to avoid creating arrays', () => {
    check(`
      for i in [100..0] by -2
        i
    `, `
      for (var i = 100; i >= 0; i -= 2) {
        i;
      }
    `);
  });

  it('special-cases variable for-in range loops to avoid creating arrays', () => {
    check(`
      for i in [a..b]
        i
    `, `
      for (var i = a; a < b ? i <= b : i >= b; a < b ? i++ : i--) {
        i;
      }
    `);
  });

  it('saves references to unsafe-to-repeat range bounds in variable for-in loops', () => {
    check(`
      for i in [a()..b()]
        i
    `, `
      var start = a();
      var end = b();
      for (var i = start; start < end ? i <= end : i >= end; start < end ? i++ : i--) {
        i;
      }
    `);
  });

  it('moves the body of a one-line for-of loop to the next line', () => {
    check(`
      k for k of o
    `, `
      for (var k in o) {
        k;
      }
    `);
  });

  it('moves the body of a one-line for-in loop to the next line', () => {
    check(`
      e for e in l
    `, `
      for (var i = 0, e; i < l.length; i++) {
        e = l[i];
        e;
      }
    `);
  });

  it('saves the list of results when for-of loops are used in an expression context', () => {
    check(`
      a(k for k of o)
    `, `
      a((() => {
        var result = [];
        for (var k in o) {
          result.push(k);
        }
        return result;
      })());
    `);
  });

  it('saves the list of results when for-in loops are used in an expression context', () => {
    check(`
      a(e for e in l)
    `, `
      a((() => {
        var result = [];
        for (var i = 0, e; i < l.length; i++) {
          e = l[i];
          result.push(e);
        }
        return result;
      })());
    `);
  });

  it('saves the list of results when for-in loops are used as an implicit return', () => {
    check(`
      ->
        a for a in b
    `, `
      (function() {
        return (() => {
          var result = [];
          for (var i = 0, a; i < b.length; i++) {
            a = b[i];
            result.push(a);
          }
          return result;
        })();
      });
    `);
  });

  it('closes the call to `result.push()` at the right position', () => {
    check(`
      ->
        for a in b
          if a
            b

      # this is here to make the real end of "a" be much later
      stuff
    `, `
      (function() {
        return (() => {
          var result = [];
          for (var i = 0, a; i < b.length; i++) {
            a = b[i];
            result.push((() => {
              if (a) {
                return b;
              }
            })());
          }
          return result;
        })();
      });

      // this is here to make the real end of "a" be much later
      stuff;
    `);
  });

  it('generates counters for nested loops that follow typical convention', () => {
    check(`
      for a in b
        for c in d
          a + c
    `, `
      for (var i = 0, a; i < b.length; i++) {
        a = b[i];
        for (var j = 0, c; j < d.length; j++) {
          c = d[j];
          a + c;
        }
      }
    `);
  });

  it('handles `for own`', () => {
    check(`
      for own key of list
        console.log key
    `, `
      for (var key in list) {
        if (Object.prototype.hasOwnProperty.call(list, key)) {
          console.log(key);
        }
      }
    `);
  });

  it('handles `for own` with an unsafe-to-repeat iterable', () => {
    check(`
      for own key of getList()
        console.log key
    `, `
      var iterable;
      for (var key in (iterable = getList())) {
        if (Object.prototype.hasOwnProperty.call(iterable, key)) {
          console.log(key);
        }
      }
    `);
  });

  it('handles `for own` with both key and value', () => {
    check(`
      for own key, value of list
        console.log key, value
    `, `
      for (var key in list) {
        if (Object.prototype.hasOwnProperty.call(list, key)) {
          var value = list[key];
          console.log(key, value);
        }
      }
    `);
  });

  it('handles `for own` with a filter', () => {
    check(`
      for own key of list when key[0] is '_'
        console.log key
    `, `
      for (var key in list) {
        if (Object.prototype.hasOwnProperty.call(list, key)) {
          if (key[0] === '_') {
            console.log(key);
          }
        }
      }
    `);
  });

  it('handles single-line `for own`', () => {
    check(`
      a for own a of b
    `, `
      for (var a in b) {
        if (Object.prototype.hasOwnProperty.call(b, a)) {
          a;
        }
      }
    `);
  });

  it('does not consider a `for` loop as an implicit return if it returns itself', () => {
    check(`
      ->
        for a in b
          return a
    `, `
      (function() {
        for (var i = 0, a; i < b.length; i++) {
          a = b[i];
          return a;
        }
      });
    `);
  });

  it('considers a `for` loop as an implicit return if it only returns within a function', () => {
    check(`
      ->
        for a in b
          -> return a
    `, `
      (function() {
        return (() => {
          var result = [];
          for (var i = 0, a; i < b.length; i++) {
            a = b[i];
            result.push(function() { return a; });
          }
          return result;
        })();
      });
    `);
  });
});
<commit_msg>Add failing tests for index-less `for` loops.<commit_after>import check from './support/check';

describe('for loops', () => {
  it('transforms basic for-of loops into for-in', () => {
    check(`
      for k of o
        k
    `, `
      for (var k in o) {
        k;
      }
    `);
  });

  it('transforms for-of loops with both key and value into for-in', () => {
    check(`
      for k, v of o
        k
    `, `
      for (var k in o) {
        var v = o[k];
        k;
      }
    `);
  });

  it('transforms for-of loops with both key and value plus unsafe-to-repeat target by saving a reference', () => {
    check(`
      for k, v of object()
        k
    `, `
      var iterable;
      for (var k in (iterable = object())) {
        var v = iterable[k];
        k;
      }
    `);
  });

  it('transforms for-of loops with destructured keys', () => {
    check(`
      for {a, b} of o
        a + b
    `, `
      for (var {a, b} in o) {
        a + b;
      }
    `);
  });

  it('transforms for-of loops with destructured keys and values', () => {
    check(`
      for {a, b}, {x, y} of o
        a + x
    `, `
      for (var key in o) {
        var {a, b} = key;
        var {x, y} = o[key];
        a + x;
      }
    `);
  });

  it('transforms for-of loops with destructured keys and values plus unsafe-to-repeat target', () => {
    check(`
      for {a, b}, {x, y} of object()
        a + x
    `, `
      var iterable;
      for (var key in (iterable = object())) {
        var {a, b} = key;
        var {x, y} = iterable[key];
        a + x;
      }
    `);
  });

  it('transforms for-in loops to typical `for` loops', () => {
    check(`
      for a in b
        a
    `, `
      for (var i = 0, a; i < b.length; i++) {
        a = b[i];
        a;
      }
    `);
  });

  it.skip('gives `for` loops without an index an index', () => {
    check(`
      for [0..1]
        2
    `, `
      for (var i = 0; i <= 1; i++) {
        2;
      }
    `);
  });

  it.skip('gives `for` loops without an index an index that does not collide with existing bindings', () => {
    check(`
      for [0..1]
        i
    `, `
      for (var j = 0; j <= 1; j++) {
        i;
      }
    `);
  });

  it('allows iterating with for-in by a specific step size', () => {
    check(`
      for a in b by 2
        a
    `, `
      for (var i = 0, a; i < b.length; i += 2) {
        a = b[i];
        a;
      }
    `);
  });

  it('allows iterating with for-in in reverse', () => {
    check(`
      for a in b by -1
        a
    `, `
      for (var i = b.length - 1, a; i >= 0; i--) {
        a = b[i];
        a;
      }
    `);
  });

  it('allows iterating with for-in in reverse with a specific step size', () => {
    check(`
      for a in b by -2
        a
    `, `
      for (var i = b.length - 1, a; i >= 0; i -= 2) {
        a = b[i];
        a;
      }
    `);
  });

  it('allows filtering using a `when` clause', () => {
    check(`
      for a in b when a.c
        a
    `, `
      for (var i = 0, a; i < b.length; i++) {
        a = b[i];
        if (a.c) {
          a;
        }
      }
    `);
  });

  it('allows using both `when` and `by` clauses', () => {
    check(`
      for a in b by 2 when a.c
        a
    `, `
      for (var i = 0, a; i < b.length; i += 2) {
        a = b[i];
        if (a.c) {
          a;
        }
      }
    `);
  });

  it('extracts unsafe-to-repeat iteration targets before the for-in loop', () => {
    check(`
      for e in list()
        break
    `, `
      var iterable = list();
      for (var i = 0, e; i < iterable.length; i++) {
        e = iterable[i];
        break;
      }
    `);
  });

  it('special-cases for-in inclusive range loops to avoid creating arrays', () => {
    check(`
      for i in [0..10]
        i
    `, `
      for (var i = 0; i <= 10; i++) {
        i;
      }
    `);
  });

  it('special-cases for-in exclusive range loops to avoid creating arrays', () => {
    check(`
      for i in [0...10]
        i
    `, `
      for (var i = 0; i < 10; i++) {
        i;
      }
    `);
  });

  it('special-cases descending for-in inclusive range loops to avoid creating arrays', () => {
    check(`
      for i in [10..0]
        i
    `, `
      for (var i = 10; i >= 0; i--) {
        i;
      }
    `);
  });

  it('special-cases descending for-in exclusive range loops to avoid creating arrays', () => {
    check(`
      for i in [10...0]
        i
    `, `
      for (var i = 10; i > 0; i--) {
        i;
      }
    `);
  });

  it('special-cases descending for-in range loops with step count to avoid creating arrays', () => {
    check(`
      for i in [100..0] by -2
        i
    `, `
      for (var i = 100; i >= 0; i -= 2) {
        i;
      }
    `);
  });

  it('special-cases variable for-in range loops to avoid creating arrays', () => {
    check(`
      for i in [a..b]
        i
    `, `
      for (var i = a; a < b ? i <= b : i >= b; a < b ? i++ : i--) {
        i;
      }
    `);
  });

  it('saves references to unsafe-to-repeat range bounds in variable for-in loops', () => {
    check(`
      for i in [a()..b()]
        i
    `, `
      var start = a();
      var end = b();
      for (var i = start; start < end ? i <= end : i >= end; start < end ? i++ : i--) {
        i;
      }
    `);
  });

  it('moves the body of a one-line for-of loop to the next line', () => {
    check(`
      k for k of o
    `, `
      for (var k in o) {
        k;
      }
    `);
  });

  it('moves the body of a one-line for-in loop to the next line', () => {
    check(`
      e for e in l
    `, `
      for (var i = 0, e; i < l.length; i++) {
        e = l[i];
        e;
      }
    `);
  });

  it('saves the list of results when for-of loops are used in an expression context', () => {
    check(`
      a(k for k of o)
    `, `
      a((() => {
        var result = [];
        for (var k in o) {
          result.push(k);
        }
        return result;
      })());
    `);
  });

  it('saves the list of results when for-in loops are used in an expression context', () => {
    check(`
      a(e for e in l)
    `, `
      a((() => {
        var result = [];
        for (var i = 0, e; i < l.length; i++) {
          e = l[i];
          result.push(e);
        }
        return result;
      })());
    `);
  });

  it('saves the list of results when for-in loops are used as an implicit return', () => {
    check(`
      ->
        a for a in b
    `, `
      (function() {
        return (() => {
          var result = [];
          for (var i = 0, a; i < b.length; i++) {
            a = b[i];
            result.push(a);
          }
          return result;
        })();
      });
    `);
  });

  it('closes the call to `result.push()` at the right position', () => {
    check(`
      ->
        for a in b
          if a
            b

      # this is here to make the real end of "a" be much later
      stuff
    `, `
      (function() {
        return (() => {
          var result = [];
          for (var i = 0, a; i < b.length; i++) {
            a = b[i];
            result.push((() => {
              if (a) {
                return b;
              }
            })());
          }
          return result;
        })();
      });

      // this is here to make the real end of "a" be much later
      stuff;
    `);
  });

  it('generates counters for nested loops that follow typical convention', () => {
    check(`
      for a in b
        for c in d
          a + c
    `, `
      for (var i = 0, a; i < b.length; i++) {
        a = b[i];
        for (var j = 0, c; j < d.length; j++) {
          c = d[j];
          a + c;
        }
      }
    `);
  });

  it('handles `for own`', () => {
    check(`
      for own key of list
        console.log key
    `, `
      for (var key in list) {
        if (Object.prototype.hasOwnProperty.call(list, key)) {
          console.log(key);
        }
      }
    `);
  });

  it('handles `for own` with an unsafe-to-repeat iterable', () => {
    check(`
      for own key of getList()
        console.log key
    `, `
      var iterable;
      for (var key in (iterable = getList())) {
        if (Object.prototype.hasOwnProperty.call(iterable, key)) {
          console.log(key);
        }
      }
    `);
  });

  it('handles `for own` with both key and value', () => {
    check(`
      for own key, value of list
        console.log key, value
    `, `
      for (var key in list) {
        if (Object.prototype.hasOwnProperty.call(list, key)) {
          var value = list[key];
          console.log(key, value);
        }
      }
    `);
  });

  it('handles `for own` with a filter', () => {
    check(`
      for own key of list when key[0] is '_'
        console.log key
    `, `
      for (var key in list) {
        if (Object.prototype.hasOwnProperty.call(list, key)) {
          if (key[0] === '_') {
            console.log(key);
          }
        }
      }
    `);
  });

  it('handles single-line `for own`', () => {
    check(`
      a for own a of b
    `, `
      for (var a in b) {
        if (Object.prototype.hasOwnProperty.call(b, a)) {
          a;
        }
      }
    `);
  });

  it('does not consider a `for` loop as an implicit return if it returns itself', () => {
    check(`
      ->
        for a in b
          return a
    `, `
      (function() {
        for (var i = 0, a; i < b.length; i++) {
          a = b[i];
          return a;
        }
      });
    `);
  });

  it('considers a `for` loop as an implicit return if it only returns within a function', () => {
    check(`
      ->
        for a in b
          -> return a
    `, `
      (function() {
        return (() => {
          var result = [];
          for (var i = 0, a; i < b.length; i++) {
            a = b[i];
            result.push(function() { return a; });
          }
          return result;
        })();
      });
    `);
  });
});
<|endoftext|>