<commit_before>'use strict';

const path = require('path');

const execa = require('execa');
const fs = require('fs-extra');
const Listr = require('listr');

const common = require('./common');

module.exports = function () {
  return {
    title: 'Minor V8 update',
    task: () => {
      return new Listr([
        common.getCurrentV8Version(),
        getLatestV8Version(),
        minorUpdate()
      ]);
    }
  };
};

function getLatestV8Version() {
  return {
    title: 'Get latest V8 version',
    task: async (ctx) => {
      const currentV8Tag = ctx.currentVersion.slice(0, 3).join('.');
      let tags = await execa.stdout('git', ['tag', '-l', `${currentV8Tag}.*`], {
        cwd: ctx.v8CloneDir
      });
      tags = toSortedArray(tags);
      ctx.latestVersion = tags[0];
    }
  };
}

function minorUpdate() {
  return {
    title: 'Do minor update',
    task: (ctx, task) => {
      if (ctx.latestVersion.length === 3) {
        throw new Error('minor update can only be done on release branches');
      }
      const latestStr = ctx.latestVersion.join('.');
      task.title = `Do minor update to ${latestStr}`;
      return doMinorUpdate(ctx, latestStr);
    },
    skip: (ctx) => {
      if (ctx.currentVersion[3] >= ctx.latestVersion[3]) {
        ctx.skipped = true;
        return true;
      }
      return false;
    }
  };
}

async function doMinorUpdate(ctx, latestStr) {
  const currentStr = ctx.currentVersion.join('.');
  const diff = await execa.stdout(
    'git',
    ['format-patch', '--stdout', `${currentStr}...${latestStr}`],
    { cwd: ctx.v8CloneDir }
  );
  try {
    await execa('git', ['apply', '--directory', 'deps/v8'], {
      cwd: ctx.nodeDir,
      input: diff
    });
  } catch (e) {
    const file = path.join(ctx.nodeDir, `${latestStr}.diff`);
    await fs.writeFile(file, diff);
    throw new Error(`Could not apply patch.\n${e}\nDiff was stored in ${file}`);
  }
}

function toSortedArray(tags) {
  return tags
    .split(/[\r\n]+/)
    .filter((tag) => tag !== '')
    .map((tag) => tag.split('.'))
    .sort(sortVersions);
}

function sortVersions(v1, v2) {
  return v2[3] - v1[3];
}
<commit_msg>mention reason for skipping minor update<commit_after>'use strict';

const path = require('path');

const execa = require('execa');
const fs = require('fs-extra');
const Listr = require('listr');

const common = require('./common');

module.exports = function () {
  return {
    title: 'Minor V8 update',
    task: () => {
      return new Listr([
        common.getCurrentV8Version(),
        getLatestV8Version(),
        minorUpdate()
      ]);
    }
  };
};

function getLatestV8Version() {
  return {
    title: 'Get latest V8 version',
    task: async (ctx) => {
      const currentV8Tag = ctx.currentVersion.slice(0, 3).join('.');
      let tags = await execa.stdout('git', ['tag', '-l', `${currentV8Tag}.*`], {
        cwd: ctx.v8CloneDir
      });
      tags = toSortedArray(tags);
      ctx.latestVersion = tags[0];
    }
  };
}

function minorUpdate() {
  return {
    title: 'Do minor update',
    task: (ctx, task) => {
      if (ctx.latestVersion.length === 3) {
        throw new Error('minor update can only be done on release branches');
      }
      const latestStr = ctx.latestVersion.join('.');
      task.title = `Do minor update to ${latestStr}`;
      return doMinorUpdate(ctx, latestStr);
    },
    skip: (ctx) => {
      if (ctx.currentVersion[3] >= ctx.latestVersion[3]) {
        ctx.skipped = 'V8 is up-to-date';
        return ctx.skipped;
      }
      return false;
    }
  };
}

async function doMinorUpdate(ctx, latestStr) {
  const currentStr = ctx.currentVersion.join('.');
  const diff = await execa.stdout(
    'git',
    ['format-patch', '--stdout', `${currentStr}...${latestStr}`],
    { cwd: ctx.v8CloneDir }
  );
  try {
    await execa('git', ['apply', '--directory', 'deps/v8'], {
      cwd: ctx.nodeDir,
      input: diff
    });
  } catch (e) {
    const file = path.join(ctx.nodeDir, `${latestStr}.diff`);
    await fs.writeFile(file, diff);
    throw new Error(`Could not apply patch.\n${e}\nDiff was stored in ${file}`);
  }
}

function toSortedArray(tags) {
  return tags
    .split(/[\r\n]+/)
    .filter((tag) => tag !== '')
    .map((tag) => tag.split('.'))
    .sort(sortVersions);
}

function sortVersions(v1, v2) {
  return v2[3] - v1[3];
}
<|endoftext|>


<commit_before>/*
 * == BSD2 LICENSE ==
 * Copyright (c) 2016, Tidepool Project
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the associated License, which is identical to the BSD 2-Clause
 * License as published by the Open Source Initiative at opensource.org.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the License for more details.
 *
 * You should have received a copy of the License along with this program; if
 * not, you can obtain one from Tidepool Project at tidepool.org.
 * == BSD2 LICENSE ==
 */

const CBG_READINGS_ONE_DAY = 86400000 / (1000 * 60 * 5);

import _ from 'lodash';
import bows from 'bows';
import { extent } from 'd3-array';
import { scaleLinear } from 'd3-scale';
import { utcDay } from 'd3-time';
import React, { PropTypes } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';

import * as actions from '../../actions/';
import CBGTrendsContainer from './CBGTrendsContainer';
import * as datetime from '../../utils/datetime';

export class TrendsContainer extends React.Component {
  static propTypes = {
    activeDays: PropTypes.shape({
      monday: PropTypes.bool.isRequired,
      tuesday: PropTypes.bool.isRequired,
      wednesday: PropTypes.bool.isRequired,
      thursday: PropTypes.bool.isRequired,
      friday: PropTypes.bool.isRequired,
      saturday: PropTypes.bool.isRequired,
      sunday: PropTypes.bool.isRequired,
    }).isRequired,
    bgBounds: PropTypes.shape({
      veryHighThreshold: PropTypes.number.isRequired,
      targetUpperBound: PropTypes.number.isRequired,
      targetLowerBound: PropTypes.number.isRequired,
      veryLowThreshold: PropTypes.number.isRequired,
    }),
    bgClasses: PropTypes.shape({
      'very-high': PropTypes.shape({ boundary: PropTypes.number.isRequired }).isRequired,
      high: PropTypes.shape({ boundary: PropTypes.number.isRequired }).isRequired,
      target: PropTypes.shape({ boundary: PropTypes.number.isRequired }).isRequired,
      low: PropTypes.shape({ boundary: PropTypes.number.isRequired }).isRequired,
      'very-low': PropTypes.shape({ boundary: PropTypes.number.isRequired }).isRequired,
    }).isRequired,
    bgUnits: PropTypes.oneOf(['mg/dL', 'mmol/L']),
    extentSize: PropTypes.oneOf([7, 14, 28]),
    initialDatetimeLocation: PropTypes.string,
    showingSmbg: PropTypes.bool.isRequired,
    showingCbg: PropTypes.bool.isRequired,
    smbgRangeOverlay: PropTypes.bool.isRequired,
    smbgGrouped: PropTypes.bool.isRequired,
    smbgLines: PropTypes.bool.isRequired,
    smbgTrendsComponent: PropTypes.func.isRequired,
    timePrefs: PropTypes.object.isRequired,
    // data (crossfilter dimensions)
    cbgByDate: PropTypes.object.isRequired,
    cbgByDayOfWeek: PropTypes.object.isRequired,
    smbgByDate: PropTypes.object.isRequired,
    smbgByDayOfWeek: PropTypes.object.isRequired,
    // handlers
    onDatetimeLocationChange: PropTypes.func.isRequired,
    onSwitchBgDataSource: PropTypes.func.isRequired,
    // viz state
    viz: PropTypes.shape({
      trends: PropTypes.shape({
        focusedCbgSlice: PropTypes.object,
        focusedCbgSliceKeys: PropTypes.array,
      }).isRequired,
    }).isRequired,
    // actions
    focusTrendsCbgSlice: PropTypes.func.isRequired,
    unfocusTrendsCbgSlice: PropTypes.func.isRequired,
  };

  constructor(props) {
    super(props);
    this.log = bows('TrendsContainer');
    this.state = {
      currentCbgData: [],
      currentSmbgData: [],
      dateDomain: null,
      mostRecent: null,
      xScale: null,
      yScale: null,
    };
  }

  componentWillMount() {
    // find BG domain (for yScale construction)
    const { cbgByDate, cbgByDayOfWeek, smbgByDate, smbgByDayOfWeek } = this.props;
    const allBg = cbgByDate.filterAll().top(Infinity).concat(smbgByDate.filterAll().top(Infinity));
    const bgDomain = extent(allBg, d => d.value);

    const { bgBounds, bgUnits } = this.props;
    const upperBound = (bgUnits === 'mg/dL') ? 400 : 22.5;
    const yScaleDomain = [bgDomain[0], upperBound];
    if (bgDomain[0] > bgBounds.targetLowerBound) {
      yScaleDomain[0] = bgBounds.targetLowerBound;
    }
    const yScale = scaleLinear().domain(yScaleDomain);

    // find initial date domain (based on initialDatetimeLocation or current time)
    const { extentSize, initialDatetimeLocation, timePrefs } = this.props;
    const timezone = datetime.getTimezoneFromTimePrefs(timePrefs);
    const mostRecent = datetime.timezoneAwareCeiling(new Date().valueOf(), timezone);
    const end = initialDatetimeLocation ?
      datetime.timezoneAwareCeiling(initialDatetimeLocation, timezone) : mostRecent;
    const start = utcDay.offset(end, -extentSize);
    const dateDomain = [start.toISOString(), end.toISOString()];

    // filter data according to current activeDays and dateDomain
    this.initialFiltering(cbgByDate, cbgByDayOfWeek, dateDomain);
    this.initialFiltering(smbgByDate, smbgByDayOfWeek, dateDomain);
    this.setState({
      bgDomain: { lo: bgDomain[0], hi: bgDomain[1] },
      currentCbgData: cbgByDate.top(Infinity).reverse(),
      currentSmbgData: smbgByDate.top(Infinity).reverse(),
      dateDomain: { start: dateDomain[0], end: dateDomain[1] },
      mostRecent: mostRecent.toISOString(),
      xScale: scaleLinear().domain([0, 864e5]),
      yScale,
    }, this.determineDataToShow);
    this.props.onDatetimeLocationChange(dateDomain);
  }

  componentWillReceiveProps(nextProps) {
    if (!_.isEqual(nextProps.activeDays, this.props.activeDays)) {
      const { cbgByDayOfWeek, smbgByDayOfWeek } = this.props;
      this.refilterByDayOfWeek(cbgByDayOfWeek, nextProps.activeDays);
      this.refilterByDayOfWeek(smbgByDayOfWeek, nextProps.activeDays);
      this.setState({
        currentCbgData: cbgByDayOfWeek.top(Infinity).reverse(),
        currentSmbgData: smbgByDayOfWeek.top(Infinity).reverse(),
      });
    }
  }

  getCurrentDay() {
    const { dateDomain: { end } } = this.state;
    // TODO: replace with more robust code for finding noon of the local timezone day
    return new Date(Date.parse(end) - (864e5 / 2));
  }

  setExtent(newDomain) {
    const { cbgByDate, smbgByDate } = this.props;
    this.refilterByDate(cbgByDate, newDomain);
    this.refilterByDate(smbgByDate, newDomain);
    this.setState({
      currentCbgData: cbgByDate.top(Infinity).reverse(),
      currentSmbgData: smbgByDate.top(Infinity).reverse(),
      dateDomain: { start: newDomain[0], end: newDomain[1] },
    });
  }

  goBack() {
    const { dateDomain: { start: newEnd } } = this.state;
    const start = utcDay.offset(new Date(newEnd), -this.props.extentSize).toISOString();
    const newDomain = [start, newEnd];
    this.setExtent(newDomain);
    this.props.onDatetimeLocationChange(newDomain);
  }

  goForward() {
    const { dateDomain: { end: newStart }, mostRecent } = this.state;
    const end = utcDay.offset(new Date(newStart), this.props.extentSize).toISOString();
    const newDomain = [newStart, end];
    this.setExtent(newDomain);
    this.props.onDatetimeLocationChange(newDomain);
    return (end >= mostRecent);
  }

  goToMostRecent() {
    const { mostRecent: end } = this.state;
    const start = utcDay.offset(new Date(end), -this.props.extentSize).toISOString();
    const newDomain = [start, end];
    this.setExtent(newDomain);
    this.props.onDatetimeLocationChange(newDomain);
  }

  refilterByDate(dataByDate, dateDomain) {
    dataByDate.filter(dateDomain);
  }

  refilterByDayOfWeek(dataByDayOfWeek, activeDays) {
    dataByDayOfWeek.filterFunction(this.filterActiveDaysFn(activeDays));
  }

  initialFiltering(dataByDate, dataByDayOfWeek, dateDomain) {
    const { activeDays } = this.props;
    // clear old filters
    dataByDayOfWeek.filterAll();

    // filter by day of week (Monday, Tuesday, etc.)
    dataByDayOfWeek.filterFunction(this.filterActiveDaysFn(activeDays));

    // filter within date domain
    dataByDate.filter(dateDomain);
  }

  filterActiveDaysFn(activeDays) {
    return (d) => (activeDays[d]);
  }

  determineDataToShow() {
    const { currentCbgData } = this.state;
    const { extentSize, showingCbg } = this.props;
    const minimumCbgs = (extentSize * CBG_READINGS_ONE_DAY) / 5;
    if (currentCbgData.length >= minimumCbgs && !showingCbg) {
      this.props.onSwitchBgDataSource();
    }
  }

  render() {
    const { timePrefs } = this.props;
    const timezone = datetime.getTimezoneFromTimePrefs(timePrefs);
    if (this.props.showingSmbg) {
      const { smbgTrendsComponent: SMBGTrendsContainer } = this.props;
      return (
        <SMBGTrendsContainer
          bgClasses={this.props.bgClasses}
          bgDomain={[this.state.bgDomain.lo, this.state.bgDomain.hi]}
          bgUnits={this.props.bgUnits}
          boxOverlay={this.props.smbgRangeOverlay}
          data={this.state.currentSmbgData}
          grouped={this.props.smbgGrouped}
          showingLines={this.props.smbgLines}
          timezone={timezone}
        />
      );
    } else if (this.props.showingCbg) {
      return (
        <CBGTrendsContainer
          bgBounds={this.props.bgBounds}
          bgUnits={this.props.bgUnits}
          data={this.state.currentCbgData}
          focusedSlice={this.props.viz.trends.focusedCbgSlice}
          focusedSliceKeys={this.props.viz.trends.focusedCbgSliceKeys}
          focusSlice={this.props.focusTrendsCbgSlice}
          unfocusSlice={this.props.unfocusTrendsCbgSlice}
          timezone={timezone}
          xScale={this.state.xScale}
          yScale={this.state.yScale}
        />
      );
    }
    return null;
  }
}

export function mapStateToProps(state) {
  return {
    viz: state.viz,
  };
}

export function mapDispatchToProps(dispatch) {
  return bindActionCreators({
    focusTrendsCbgSlice: actions.focusTrendsCbgSlice,
    unfocusTrendsCbgSlice: actions.unfocusTrendsCbgSlice,
  }, dispatch);
}

export default connect(
  mapStateToProps,
  mapDispatchToProps,
  (stateProps, dispatchProps, ownProps) => (Object.assign({}, ownProps, stateProps, dispatchProps)),
  { withRef: true },
)(TrendsContainer);
<commit_msg>add comment re: bgBounds vs. bgClasses from @jhbate's feedback<commit_after>/*
 * == BSD2 LICENSE ==
 * Copyright (c) 2016, Tidepool Project
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the associated License, which is identical to the BSD 2-Clause
 * License as published by the Open Source Initiative at opensource.org.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the License for more details.
 *
 * You should have received a copy of the License along with this program; if
 * not, you can obtain one from Tidepool Project at tidepool.org.
 * == BSD2 LICENSE ==
 */

const CBG_READINGS_ONE_DAY = 86400000 / (1000 * 60 * 5);

import _ from 'lodash';
import bows from 'bows';
import { extent } from 'd3-array';
import { scaleLinear } from 'd3-scale';
import { utcDay } from 'd3-time';
import React, { PropTypes } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';

import * as actions from '../../actions/';
import CBGTrendsContainer from './CBGTrendsContainer';
import * as datetime from '../../utils/datetime';

export class TrendsContainer extends React.Component {
  static propTypes = {
    activeDays: PropTypes.shape({
      monday: PropTypes.bool.isRequired,
      tuesday: PropTypes.bool.isRequired,
      wednesday: PropTypes.bool.isRequired,
      thursday: PropTypes.bool.isRequired,
      friday: PropTypes.bool.isRequired,
      saturday: PropTypes.bool.isRequired,
      sunday: PropTypes.bool.isRequired,
    }).isRequired,
    // new data structure to replace bgClasses
    bgBounds: PropTypes.shape({
      veryHighThreshold: PropTypes.number.isRequired,
      targetUpperBound: PropTypes.number.isRequired,
      targetLowerBound: PropTypes.number.isRequired,
      veryLowThreshold: PropTypes.number.isRequired,
    }),
    // legacy data structure for representing target range, &c
    // needed for passed-in legacy tideline smbg component but will phase out!
    bgClasses: PropTypes.shape({
      'very-high': PropTypes.shape({ boundary: PropTypes.number.isRequired }).isRequired,
      high: PropTypes.shape({ boundary: PropTypes.number.isRequired }).isRequired,
      target: PropTypes.shape({ boundary: PropTypes.number.isRequired }).isRequired,
      low: PropTypes.shape({ boundary: PropTypes.number.isRequired }).isRequired,
      'very-low': PropTypes.shape({ boundary: PropTypes.number.isRequired }).isRequired,
    }).isRequired,
    bgUnits: PropTypes.oneOf(['mg/dL', 'mmol/L']),
    extentSize: PropTypes.oneOf([7, 14, 28]),
    initialDatetimeLocation: PropTypes.string,
    showingSmbg: PropTypes.bool.isRequired,
    showingCbg: PropTypes.bool.isRequired,
    smbgRangeOverlay: PropTypes.bool.isRequired,
    smbgGrouped: PropTypes.bool.isRequired,
    smbgLines: PropTypes.bool.isRequired,
    smbgTrendsComponent: PropTypes.func.isRequired,
    timePrefs: PropTypes.object.isRequired,
    // data (crossfilter dimensions)
    cbgByDate: PropTypes.object.isRequired,
    cbgByDayOfWeek: PropTypes.object.isRequired,
    smbgByDate: PropTypes.object.isRequired,
    smbgByDayOfWeek: PropTypes.object.isRequired,
    // handlers
    onDatetimeLocationChange: PropTypes.func.isRequired,
    onSwitchBgDataSource: PropTypes.func.isRequired,
    // viz state
    viz: PropTypes.shape({
      trends: PropTypes.shape({
        focusedCbgSlice: PropTypes.object,
        focusedCbgSliceKeys: PropTypes.array,
      }).isRequired,
    }).isRequired,
    // actions
    focusTrendsCbgSlice: PropTypes.func.isRequired,
    unfocusTrendsCbgSlice: PropTypes.func.isRequired,
  };

  constructor(props) {
    super(props);
    this.log = bows('TrendsContainer');
    this.state = {
      currentCbgData: [],
      currentSmbgData: [],
      dateDomain: null,
      mostRecent: null,
      xScale: null,
      yScale: null,
    };
  }

  componentWillMount() {
    // find BG domain (for yScale construction)
    const { cbgByDate, cbgByDayOfWeek, smbgByDate, smbgByDayOfWeek } = this.props;
    const allBg = cbgByDate.filterAll().top(Infinity).concat(smbgByDate.filterAll().top(Infinity));
    const bgDomain = extent(allBg, d => d.value);

    const { bgBounds, bgUnits } = this.props;
    const upperBound = (bgUnits === 'mg/dL') ? 400 : 22.5;
    const yScaleDomain = [bgDomain[0], upperBound];
    if (bgDomain[0] > bgBounds.targetLowerBound) {
      yScaleDomain[0] = bgBounds.targetLowerBound;
    }
    const yScale = scaleLinear().domain(yScaleDomain);

    // find initial date domain (based on initialDatetimeLocation or current time)
    const { extentSize, initialDatetimeLocation, timePrefs } = this.props;
    const timezone = datetime.getTimezoneFromTimePrefs(timePrefs);
    const mostRecent = datetime.timezoneAwareCeiling(new Date().valueOf(), timezone);
    const end = initialDatetimeLocation ?
      datetime.timezoneAwareCeiling(initialDatetimeLocation, timezone) : mostRecent;
    const start = utcDay.offset(end, -extentSize);
    const dateDomain = [start.toISOString(), end.toISOString()];

    // filter data according to current activeDays and dateDomain
    this.initialFiltering(cbgByDate, cbgByDayOfWeek, dateDomain);
    this.initialFiltering(smbgByDate, smbgByDayOfWeek, dateDomain);
    this.setState({
      bgDomain: { lo: bgDomain[0], hi: bgDomain[1] },
      currentCbgData: cbgByDate.top(Infinity).reverse(),
      currentSmbgData: smbgByDate.top(Infinity).reverse(),
      dateDomain: { start: dateDomain[0], end: dateDomain[1] },
      mostRecent: mostRecent.toISOString(),
      xScale: scaleLinear().domain([0, 864e5]),
      yScale,
    }, this.determineDataToShow);
    this.props.onDatetimeLocationChange(dateDomain);
  }

  componentWillReceiveProps(nextProps) {
    if (!_.isEqual(nextProps.activeDays, this.props.activeDays)) {
      const { cbgByDayOfWeek, smbgByDayOfWeek } = this.props;
      this.refilterByDayOfWeek(cbgByDayOfWeek, nextProps.activeDays);
      this.refilterByDayOfWeek(smbgByDayOfWeek, nextProps.activeDays);
      this.setState({
        currentCbgData: cbgByDayOfWeek.top(Infinity).reverse(),
        currentSmbgData: smbgByDayOfWeek.top(Infinity).reverse(),
      });
    }
  }

  getCurrentDay() {
    const { dateDomain: { end } } = this.state;
    // TODO: replace with more robust code for finding noon of the local timezone day
    return new Date(Date.parse(end) - (864e5 / 2));
  }

  setExtent(newDomain) {
    const { cbgByDate, smbgByDate } = this.props;
    this.refilterByDate(cbgByDate, newDomain);
    this.refilterByDate(smbgByDate, newDomain);
    this.setState({
      currentCbgData: cbgByDate.top(Infinity).reverse(),
      currentSmbgData: smbgByDate.top(Infinity).reverse(),
      dateDomain: { start: newDomain[0], end: newDomain[1] },
    });
  }

  goBack() {
    const { dateDomain: { start: newEnd } } = this.state;
    const start = utcDay.offset(new Date(newEnd), -this.props.extentSize).toISOString();
    const newDomain = [start, newEnd];
    this.setExtent(newDomain);
    this.props.onDatetimeLocationChange(newDomain);
  }

  goForward() {
    const { dateDomain: { end: newStart }, mostRecent } = this.state;
    const end = utcDay.offset(new Date(newStart), this.props.extentSize).toISOString();
    const newDomain = [newStart, end];
    this.setExtent(newDomain);
    this.props.onDatetimeLocationChange(newDomain);
    return (end >= mostRecent);
  }

  goToMostRecent() {
    const { mostRecent: end } = this.state;
    const start = utcDay.offset(new Date(end), -this.props.extentSize).toISOString();
    const newDomain = [start, end];
    this.setExtent(newDomain);
    this.props.onDatetimeLocationChange(newDomain);
  }

  refilterByDate(dataByDate, dateDomain) {
    dataByDate.filter(dateDomain);
  }

  refilterByDayOfWeek(dataByDayOfWeek, activeDays) {
    dataByDayOfWeek.filterFunction(this.filterActiveDaysFn(activeDays));
  }

  initialFiltering(dataByDate, dataByDayOfWeek, dateDomain) {
    const { activeDays } = this.props;
    // clear old filters
    dataByDayOfWeek.filterAll();

    // filter by day of week (Monday, Tuesday, etc.)
    dataByDayOfWeek.filterFunction(this.filterActiveDaysFn(activeDays));

    // filter within date domain
    dataByDate.filter(dateDomain);
  }

  filterActiveDaysFn(activeDays) {
    return (d) => (activeDays[d]);
  }

  determineDataToShow() {
    const { currentCbgData } = this.state;
    const { extentSize, showingCbg } = this.props;
    const minimumCbgs = (extentSize * CBG_READINGS_ONE_DAY) / 5;
    if (currentCbgData.length >= minimumCbgs && !showingCbg) {
      this.props.onSwitchBgDataSource();
    }
  }

  render() {
    const { timePrefs } = this.props;
    const timezone = datetime.getTimezoneFromTimePrefs(timePrefs);
    if (this.props.showingSmbg) {
      const { smbgTrendsComponent: SMBGTrendsContainer } = this.props;
      return (
        <SMBGTrendsContainer
          bgClasses={this.props.bgClasses}
          bgDomain={[this.state.bgDomain.lo, this.state.bgDomain.hi]}
          bgUnits={this.props.bgUnits}
          boxOverlay={this.props.smbgRangeOverlay}
          data={this.state.currentSmbgData}
          grouped={this.props.smbgGrouped}
          showingLines={this.props.smbgLines}
          timezone={timezone}
        />
      );
    } else if (this.props.showingCbg) {
      return (
        <CBGTrendsContainer
          bgBounds={this.props.bgBounds}
          bgUnits={this.props.bgUnits}
          data={this.state.currentCbgData}
          focusedSlice={this.props.viz.trends.focusedCbgSlice}
          focusedSliceKeys={this.props.viz.trends.focusedCbgSliceKeys}
          focusSlice={this.props.focusTrendsCbgSlice}
          unfocusSlice={this.props.unfocusTrendsCbgSlice}
          timezone={timezone}
          xScale={this.state.xScale}
          yScale={this.state.yScale}
        />
      );
    }
    return null;
  }
}

export function mapStateToProps(state) {
  return {
    viz: state.viz,
  };
}

export function mapDispatchToProps(dispatch) {
  return bindActionCreators({
    focusTrendsCbgSlice: actions.focusTrendsCbgSlice,
    unfocusTrendsCbgSlice: actions.unfocusTrendsCbgSlice,
  }, dispatch);
}

export default connect(
  mapStateToProps,
  mapDispatchToProps,
  (stateProps, dispatchProps, ownProps) => (Object.assign({}, ownProps, stateProps, dispatchProps)),
  { withRef: true },
)(TrendsContainer);
<|endoftext|>


<commit_before>import { searchItem, checkIn, searchModel } from '../../lib/api-client';
import { Dispatcher } from 'consus-core/flux';
import CartStore from '../../store/cart-store';

export default class CartController {

    static checkInItem(id, itemAddress) {
        return checkIn(id, itemAddress).then(data => {
            Dispatcher.handleAction('CHECKIN_SUCCESS', {
                itemAddress: data.itemAddress,
                modelName: data.modelName
            });
        }).catch(error => {
            Dispatcher.handleAction('ERROR', {
                error
            });
        });
    }

    static getItem(address) {
        return searchItem(address).then(item => {
            if (item.status === 'CHECKED_OUT') {
                return Dispatcher.handleAction('ERROR', {
                    error: 'This item is already checked out by another student.'
                });
            } else if(CartStore.getContents().some(ele => ele.address === address)){
                return Dispatcher.handleAction('ERROR', {
                    error: 'This item is already in the cart.'
                });
            }
            Dispatcher.handleAction("CHECKOUT_ITEM_FOUND", item);
        });
    }

    static getModel(address) {
        return searchModel(address).then(model => {
            if (model.inStock <= 0)
                return Dispatcher.handleAction('ERROR', {
                    error: `${model.name} is out of stock.`
                });
            if(!model.allowCheckout)
                return Dispatcher.handleAction('ERROR', {
                    error: `${model.name} is not available for checkout.`
                });
            Dispatcher.handleAction("CHECKOUT_MODEL_FOUND", model);
        });
    }

    static incrementModel(address) {
        return searchModel(address).then(model => {
            let storeModel = CartStore.getContents().find(content => {
                return content.address === model.address;
            });

            if(storeModel.quantity < model.inStock) {
                Dispatcher.handleAction("CHECKOUT_DUPLICATE_MODEL", model);
            } else {
                return Dispatcher.handleAction('ERROR', {
                    error: `${model.name} is out of stock.`
                });
            }
        });
    }

    static throwError(error){
        Dispatcher.handleAction("ERROR", { error });
    }
}
<commit_msg>fixed if statement formatting<commit_after>import { searchItem, checkIn, searchModel } from '../../lib/api-client';
import { Dispatcher } from 'consus-core/flux';
import CartStore from '../../store/cart-store';

export default class CartController {

    static checkInItem(id, itemAddress) {
        return checkIn(id, itemAddress).then(data => {
            Dispatcher.handleAction('CHECKIN_SUCCESS', {
                itemAddress: data.itemAddress,
                modelName: data.modelName
            });
        }).catch(error => {
            Dispatcher.handleAction('ERROR', {
                error
            });
        });
    }

    static getItem(address) {
        return searchItem(address).then(item => {
            if (item.status === 'CHECKED_OUT') {
                return Dispatcher.handleAction('ERROR', {
                    error: 'This item is already checked out by another student.'
                });
            } else if(CartStore.getContents().some(ele => ele.address === address)){
                return Dispatcher.handleAction('ERROR', {
                    error: 'This item is already in the cart.'
                });
            }
            Dispatcher.handleAction("CHECKOUT_ITEM_FOUND", item);
        });
    }

    static getModel(address) {
        return searchModel(address).then(model => {
            if (model.inStock <= 0) {
                return Dispatcher.handleAction('ERROR', {
                    error: `${model.name} is out of stock.`
                });
            }
            if(!model.allowCheckout) {
                return Dispatcher.handleAction('ERROR', {
                    error: `${model.name} is not available for checkout.`
                });
            }
            Dispatcher.handleAction("CHECKOUT_MODEL_FOUND", model);
        });
    }

    static incrementModel(address) {
        return searchModel(address).then(model => {
            let storeModel = CartStore.getContents().find(content => {
                return content.address === model.address;
            });

            if(storeModel.quantity < model.inStock) {
                Dispatcher.handleAction("CHECKOUT_DUPLICATE_MODEL", model);
            } else {
                return Dispatcher.handleAction('ERROR', {
                    error: `${model.name} is out of stock.`
                });
            }
        });
    }

    static throwError(error){
        Dispatcher.handleAction("ERROR", { error });
    }
}
<|endoftext|>


<commit_before>var ObjectRenderer = require('../../renderers/webgl/utils/ObjectRenderer'),
    WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'),
    createIndicesForQuads = require('../../utils/createIndicesForQuads'),
    generateMultiTextureShader = require('./generateMultiTextureShader'),
    checkMaxIfStatmentsInShader = require('../../renderers/webgl/utils/checkMaxIfStatmentsInShader'),
    Buffer = require('./BatchBuffer'),
    CONST = require('../../const'),
    glCore = require('pixi-gl-core'),
    bitTwiddle = require('bit-twiddle');

/**
 * Renderer dedicated to drawing and batching sprites.
 *
 * @class
 * @private
 * @memberof PIXI
 * @extends PIXI.ObjectRenderer
 * @param renderer {PIXI.WebGLRenderer} The renderer this sprite batch works for.
 */
function SpriteRenderer(renderer)
{
    ObjectRenderer.call(this, renderer);

    /**
     * Number of values sent in the vertex buffer.
     * positionX, positionY, colorR, colorG, colorB = 5
     *
     * @member {number}
     */
    this.vertSize = 5;

    /**
     * The size of the vertex information in bytes.
     *
     * @member {number}
     */
    this.vertByteSize = this.vertSize * 4;

    /**
     * The number of images in the SpriteBatch before it flushes.
     *
     * @member {number}
     */
    this.size = CONST.SPRITE_BATCH_SIZE; // 2000 is a nice balance between mobile / desktop

    // the total number of bytes in our batch
    // var numVerts = this.size * 4 * this.vertByteSize;

    this.buffers = [];
    for (var i = 1; i <= bitTwiddle.nextPow2(this.size); i*=2) {
        var numVertsTemp = i * 4 * this.vertByteSize;
        this.buffers.push(new Buffer(numVertsTemp));
    }

    /**
     * Holds the indices of the geometry (quads) to draw
     *
     * @member {Uint16Array}
     */
    this.indices = createIndicesForQuads(this.size);

    /**
     * The default shaders that is used if a sprite doesn't have a more specific one.
     * there is a shader for each number of textures that can be rendererd.
     * These shaders will also be generated on the fly as required.
     * @member {PIXI.Shader[]}
     */
    this.shaders = null;

    this.currentIndex = 0;
    this.tick =0;
    this.groups = [];

    for (var k = 0; k < this.size; k++)
    {
        this.groups[k] = {textures:[], textureCount:0, ids:[], size:0, start:0, blend:0};
    }

    this.sprites = [];

    this.vertexBuffers = [];
    this.vaos = [];

    this.vaoMax = 2;
    this.vertexCount = 0;

    this.renderer.on('prerender', this.onPrerender, this);
}

SpriteRenderer.prototype = Object.create(ObjectRenderer.prototype);
SpriteRenderer.prototype.constructor = SpriteRenderer;
module.exports = SpriteRenderer;

WebGLRenderer.registerPlugin('sprite', SpriteRenderer);

/**
 * Sets up the renderer context and necessary buffers.
 *
 * @private
 */
SpriteRenderer.prototype.onContextChange = function ()
{
    var gl = this.renderer.gl;

    // step 1: first check max textures the GPU can handle.
    this.MAX_TEXTURES = Math.min(gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS), CONST.SPRITE_MAX_TEXTURES);

    // step 2: check the maximum number of if statements the shader can have too..
    this.MAX_TEXTURES = checkMaxIfStatmentsInShader( this.MAX_TEXTURES, gl );

    this.shaders = new Array(this.MAX_TEXTURES);
    this.shaders[0] = generateMultiTextureShader(gl, 1);
    this.shaders[1] = generateMultiTextureShader(gl, 2);

    // create a couple of buffers
    this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);

    // we use the second shader as the first one depending on your browser may omit aTextureId
    // as it is not used by the shader so is optimized out.
    var shader = this.shaders[1];

    for (var i = 0; i < this.vaoMax; i++) {
        this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);

        // build the vao object that will render..
        this.vaos[i] = this.renderer.createVao()
        .addIndex(this.indexBuffer)
        .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
        .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
        .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4)
        .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
    }

    this.vao = this.vaos[0];
    this.currentBlendMode = 99999;
};

SpriteRenderer.prototype.onPrerender = function ()
{
    this.vertexCount = 0;
};

/**
 * Renders the sprite object.
 *
 * @param sprite {PIXI.Sprite} the sprite to render when using this spritebatch
 */
SpriteRenderer.prototype.render = function (sprite)
{
    //TODO set blend modes..
    // check texture..
    if (this.currentIndex >= this.size)
    {
        this.flush();
    }

    // get the uvs for the texture

    // if the uvs have not updated then no point rendering just yet!
    if (!sprite.texture._uvs)
    {
        return;
    }

    // push a texture.
    // increment the batchsize
    this.sprites[this.currentIndex++] = sprite;
};

/**
 * Renders the content and empties the current batch.
 *
 */
SpriteRenderer.prototype.flush = function ()
{
    if (this.currentIndex === 0) {
      return;
    }

    var gl = this.renderer.gl;

    var np2 = bitTwiddle.nextPow2(this.currentIndex);
    var log2 = bitTwiddle.log2(np2);
    var buffer = this.buffers[log2];

    var sprites = this.sprites;
    var groups = this.groups;

    var float32View = buffer.float32View;
    var uint32View = buffer.uint32View;

    var index = 0;
    var nextTexture;
    var currentTexture;
    var groupCount = 1;
    var textureCount = 0;
    var currentGroup = groups[0];
    var vertexData;
    var tint;
    var uvs;
    var textureId;
    var blendMode = sprites[0].blendMode;
    var shader;

    currentGroup.textureCount = 0;
    currentGroup.start = 0;
    currentGroup.blend = blendMode;

    this.tick++;

    for (var i = 0; i < this.currentIndex; i++)
    {
        // upload the sprite elemetns...
        // they have all ready been calculated so we just need to push them into the buffer.
        var sprite = sprites[i];

        nextTexture = sprite._texture.baseTexture;

        if(blendMode !== sprite.blendMode)
        {
            blendMode = sprite.blendMode;

            // force the batch to break!
            currentTexture = null;
            textureCount = this.MAX_TEXTURES;
            this.tick++;
        }

        if(currentTexture !== nextTexture)
        {
            currentTexture = nextTexture;

            if(nextTexture._enabled !== this.tick)
            {
                if(textureCount === this.MAX_TEXTURES)
                {
                    this.tick++;

                    textureCount = 0;

                    currentGroup.size = i - currentGroup.start;

                    currentGroup = groups[groupCount++];
                    currentGroup.textureCount = 0;
                    currentGroup.blend = blendMode;
                    currentGroup.start = i;
                }

                nextTexture._enabled = this.tick;
                nextTexture._id = textureCount;

                currentGroup.textures[currentGroup.textureCount++] = nextTexture;
                textureCount++;
            }

        }

        vertexData = sprite.vertexData;

        //TODO this sum does not need to be set each frame..
        tint = sprite._tintRGB + (sprite.worldAlpha * 255 << 24);
        uvs = sprite._texture._uvs.uvsUint32;
        textureId = nextTexture._id;

        if (this.renderer.roundPixels)
        {
            var resolution = this.renderer.resolution;

            //xy
            float32View[index] = ((vertexData[0] * resolution) | 0) / resolution;
            float32View[index+1] = ((vertexData[1] * resolution) | 0) / resolution;

            // xy
            float32View[index+5] = ((vertexData[2] * resolution) | 0) / resolution;
            float32View[index+6] = ((vertexData[3] * resolution) | 0) / resolution;

             // xy
            float32View[index+10] = ((vertexData[4] * resolution) | 0) / resolution;
            float32View[index+11] = ((vertexData[5] * resolution) | 0) / resolution;

            // xy
            float32View[index+15] = ((vertexData[6] * resolution) | 0) / resolution;
            float32View[index+16] = ((vertexData[7] * resolution) | 0) / resolution;

        }
        else
        {
            //xy
            float32View[index] = vertexData[0];
            float32View[index+1] = vertexData[1];

            // xy
            float32View[index+5] = vertexData[2];
            float32View[index+6] = vertexData[3];

             // xy
            float32View[index+10] = vertexData[4];
            float32View[index+11] = vertexData[5];

            // xy
            float32View[index+15] = vertexData[6];
            float32View[index+16] = vertexData[7];
        }

        uint32View[index+2] = uvs[0];
        uint32View[index+7] = uvs[1];
        uint32View[index+12] = uvs[2];
        uint32View[index+17] = uvs[3];

        uint32View[index+3] = uint32View[index+8] = uint32View[index+13] = uint32View[index+18] = tint;
        float32View[index+4] = float32View[index+9] = float32View[index+14] = float32View[index+19] = textureId;

        index += 20;
    }

    currentGroup.size = i - currentGroup.start;

    this.vertexCount++;

    if(this.vaoMax <= this.vertexCount)
    {
        this.vaoMax++;
        shader = this.shaders[1];
        this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
        // build the vao object that will render..
        this.vaos[this.vertexCount] = this.renderer.createVao()
        .addIndex(this.indexBuffer)
        .addAttribute(this.vertexBuffers[this.vertexCount], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
        .addAttribute(this.vertexBuffers[this.vertexCount], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
        .addAttribute(this.vertexBuffers[this.vertexCount], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4)
        .addAttribute(this.vertexBuffers[this.vertexCount], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
    }

    this.vertexBuffers[this.vertexCount].upload(buffer.vertices, 0);
    this.vao = this.vaos[this.vertexCount].bind();

    /// render the groups..
    for (i = 0; i < groupCount; i++) {

        var group = groups[i];
        var groupTextureCount = group.textureCount;
        shader = this.shaders[groupTextureCount-1];

        if(!shader)
        {
            shader = this.shaders[groupTextureCount-1] = generateMultiTextureShader(gl, groupTextureCount);
            //console.log("SHADER generated for " + textureCount + " textures")
        }

        this.renderer.bindShader(shader);

        for (var j = 0; j < groupTextureCount; j++)
        {
            this.renderer.bindTexture(group.textures[j], j);
        }

        // set the blend mode..
        this.renderer.state.setBlendMode( group.blend );

        gl.drawElements(gl.TRIANGLES, group.size * 6, gl.UNSIGNED_SHORT, group.start * 6 * 2);
    }

    // reset elements for the next flush
    this.currentIndex = 0;
};

/**
 * Starts a new sprite batch.
 *
 */
SpriteRenderer.prototype.start = function ()
{
 //   this.renderer.bindShader(this.shader);
    this.tick %= 1000;
};

SpriteRenderer.prototype.stop = function ()
{
    this.flush();
    this.vao.unbind();
};
/**
 * Destroys the SpriteBatch.
 *
 */
SpriteRenderer.prototype.destroy = function ()
{
    for (var i = 0; i < this.vaoMax; i++) {
        this.vertexBuffers[i].destroy();
        this.vaos[i].destroy();
    }

    this.indexBuffer.destroy();

    this.renderer.off('prerender', this.onPrerender, this);
    ObjectRenderer.prototype.destroy.call(this);

    for (i = 0; i < this.shaders.length; i++) {

        if(this.shaders[i])
        {
            this.shaders[i].destroy();
        }
    }

    this.vertexBuffers = null;
    this.vaos = null;
    this.indexBuffer = null;
    this.indices = null;

    this.sprites = null;

    for (i = 0; i < this.buffers.length; i++) {
        this.buffers[i].destroy();
    }

};
<commit_msg>made tick global in sprite renderer<commit_after>var ObjectRenderer = require('../../renderers/webgl/utils/ObjectRenderer'),
    WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'),
    createIndicesForQuads = require('../../utils/createIndicesForQuads'),
    generateMultiTextureShader = require('./generateMultiTextureShader'),
    checkMaxIfStatmentsInShader = require('../../renderers/webgl/utils/checkMaxIfStatmentsInShader'),
    Buffer = require('./BatchBuffer'),
    CONST = require('../../const'),
    glCore = require('pixi-gl-core'),
    bitTwiddle = require('bit-twiddle');

    var TICK = 0;
/**
 * Renderer dedicated to drawing and batching sprites.
 *
 * @class
 * @private
 * @memberof PIXI
 * @extends PIXI.ObjectRenderer
 * @param renderer {PIXI.WebGLRenderer} The renderer this sprite batch works for.
 */
function SpriteRenderer(renderer)
{
    ObjectRenderer.call(this, renderer);

    /**
     * Number of values sent in the vertex buffer.
     * positionX, positionY, colorR, colorG, colorB = 5
     *
     * @member {number}
     */
    this.vertSize = 5;

    /**
     * The size of the vertex information in bytes.
     *
     * @member {number}
     */
    this.vertByteSize = this.vertSize * 4;

    /**
     * The number of images in the SpriteBatch before it flushes.
     *
     * @member {number}
     */
    this.size = CONST.SPRITE_BATCH_SIZE; // 2000 is a nice balance between mobile / desktop

    // the total number of bytes in our batch
    // var numVerts = this.size * 4 * this.vertByteSize;

    this.buffers = [];
    for (var i = 1; i <= bitTwiddle.nextPow2(this.size); i*=2) {
        var numVertsTemp = i * 4 * this.vertByteSize;
        this.buffers.push(new Buffer(numVertsTemp));
    }

    /**
     * Holds the indices of the geometry (quads) to draw
     *
     * @member {Uint16Array}
     */
    this.indices = createIndicesForQuads(this.size);

    /**
     * The default shaders that is used if a sprite doesn't have a more specific one.
     * there is a shader for each number of textures that can be rendererd.
     * These shaders will also be generated on the fly as required.
     * @member {PIXI.Shader[]}
     */
    this.shaders = null;

    this.currentIndex = 0;
    TICK =0;
    this.groups = [];

    for (var k = 0; k < this.size; k++)
    {
        this.groups[k] = {textures:[], textureCount:0, ids:[], size:0, start:0, blend:0};
    }

    this.sprites = [];

    this.vertexBuffers = [];
    this.vaos = [];

    this.vaoMax = 2;
    this.vertexCount = 0;

    this.renderer.on('prerender', this.onPrerender, this);
}

SpriteRenderer.prototype = Object.create(ObjectRenderer.prototype);
SpriteRenderer.prototype.constructor = SpriteRenderer;
module.exports = SpriteRenderer;

WebGLRenderer.registerPlugin('sprite', SpriteRenderer);

/**
 * Sets up the renderer context and necessary buffers.
 *
 * @private
 */
SpriteRenderer.prototype.onContextChange = function ()
{
    var gl = this.renderer.gl;

    // step 1: first check max textures the GPU can handle.
    this.MAX_TEXTURES = Math.min(gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS), CONST.SPRITE_MAX_TEXTURES);

    // step 2: check the maximum number of if statements the shader can have too..
    this.MAX_TEXTURES = checkMaxIfStatmentsInShader( this.MAX_TEXTURES, gl );

    this.shaders = new Array(this.MAX_TEXTURES);
    this.shaders[0] = generateMultiTextureShader(gl, 1);
    this.shaders[1] = generateMultiTextureShader(gl, 2);

    // create a couple of buffers
    this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);

    // we use the second shader as the first one depending on your browser may omit aTextureId
    // as it is not used by the shader so is optimized out.
    var shader = this.shaders[1];

    for (var i = 0; i < this.vaoMax; i++) {
        this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);

        // build the vao object that will render..
        this.vaos[i] = this.renderer.createVao()
        .addIndex(this.indexBuffer)
        .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
        .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
        .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4)
        .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
    }

    this.vao = this.vaos[0];
    this.currentBlendMode = 99999;
};

SpriteRenderer.prototype.onPrerender = function ()
{
    this.vertexCount = 0;
};

/**
 * Renders the sprite object.
 *
 * @param sprite {PIXI.Sprite} the sprite to render when using this spritebatch
 */
SpriteRenderer.prototype.render = function (sprite)
{
    //TODO set blend modes..
    // check texture..
    if (this.currentIndex >= this.size)
    {
        this.flush();
    }

    // get the uvs for the texture

    // if the uvs have not updated then no point rendering just yet!
    if (!sprite.texture._uvs)
    {
        return;
    }

    // push a texture.
    // increment the batchsize
    this.sprites[this.currentIndex++] = sprite;
};

/**
 * Renders the content and empties the current batch.
 *
 */
SpriteRenderer.prototype.flush = function ()
{
    if (this.currentIndex === 0) {
      return;
    }

    var gl = this.renderer.gl;

    var np2 = bitTwiddle.nextPow2(this.currentIndex);
    var log2 = bitTwiddle.log2(np2);
    var buffer = this.buffers[log2];

    var sprites = this.sprites;
    var groups = this.groups;

    var float32View = buffer.float32View;
    var uint32View = buffer.uint32View;

    var index = 0;
    var nextTexture;
    var currentTexture;
    var groupCount = 1;
    var textureCount = 0;
    var currentGroup = groups[0];
    var vertexData;
    var tint;
    var uvs;
    var textureId;
    var blendMode = sprites[0].blendMode;
    var shader;

    currentGroup.textureCount = 0;
    currentGroup.start = 0;
    currentGroup.blend = blendMode;

    TICK++;

    for (var i = 0; i < this.currentIndex; i++)
    {
        // upload the sprite elemetns...
        // they have all ready been calculated so we just need to push them into the buffer.
        var sprite = sprites[i];

        nextTexture = sprite._texture.baseTexture;

        if(blendMode !== sprite.blendMode)
        {
            blendMode = sprite.blendMode;

            // force the batch to break!
            currentTexture = null;
            textureCount = this.MAX_TEXTURES;
            TICK++;
        }

        if(currentTexture !== nextTexture)
        {
            currentTexture = nextTexture;

            if(nextTexture._enabled !== TICK)
            {
                if(textureCount === this.MAX_TEXTURES)
                {
                    TICK++;

                    textureCount = 0;

                    currentGroup.size = i - currentGroup.start;

                    currentGroup = groups[groupCount++];
                    currentGroup.textureCount = 0;
                    currentGroup.blend = blendMode;
                    currentGroup.start = i;
                }

                nextTexture._enabled = TICK;
                nextTexture._id = textureCount;

                currentGroup.textures[currentGroup.textureCount++] = nextTexture;
                textureCount++;
            }

        }

        vertexData = sprite.vertexData;

        //TODO this sum does not need to be set each frame..
        tint = sprite._tintRGB + (sprite.worldAlpha * 255 << 24);
        uvs = sprite._texture._uvs.uvsUint32;
        textureId = nextTexture._id;

        if (this.renderer.roundPixels)
        {
            var resolution = this.renderer.resolution;

            //xy
            float32View[index] = ((vertexData[0] * resolution) | 0) / resolution;
            float32View[index+1] = ((vertexData[1] * resolution) | 0) / resolution;

            // xy
            float32View[index+5] = ((vertexData[2] * resolution) | 0) / resolution;
            float32View[index+6] = ((vertexData[3] * resolution) | 0) / resolution;

             // xy
            float32View[index+10] = ((vertexData[4] * resolution) | 0) / resolution;
            float32View[index+11] = ((vertexData[5] * resolution) | 0) / resolution;

            // xy
            float32View[index+15] = ((vertexData[6] * resolution) | 0) / resolution;
            float32View[index+16] = ((vertexData[7] * resolution) | 0) / resolution;

        }
        else
        {
            //xy
            float32View[index] = vertexData[0];
            float32View[index+1] = vertexData[1];

            // xy
            float32View[index+5] = vertexData[2];
            float32View[index+6] = vertexData[3];

             // xy
            float32View[index+10] = vertexData[4];
            float32View[index+11] = vertexData[5];

            // xy
            float32View[index+15] = vertexData[6];
            float32View[index+16] = vertexData[7];
        }

        uint32View[index+2] = uvs[0];
        uint32View[index+7] = uvs[1];
        uint32View[index+12] = uvs[2];
        uint32View[index+17] = uvs[3];

        uint32View[index+3] = uint32View[index+8] = uint32View[index+13] = uint32View[index+18] = tint;
        float32View[index+4] = float32View[index+9] = float32View[index+14] = float32View[index+19] = textureId;

        index += 20;
    }

    currentGroup.size = i - currentGroup.start;

    this.vertexCount++;

    if(this.vaoMax <= this.vertexCount)
    {
        this.vaoMax++;
        shader = this.shaders[1];
        this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
        // build the vao object that will render..
        this.vaos[this.vertexCount] = this.renderer.createVao()
        .addIndex(this.indexBuffer)
        .addAttribute(this.vertexBuffers[this.vertexCount], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
        .addAttribute(this.vertexBuffers[this.vertexCount], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
        .addAttribute(this.vertexBuffers[this.vertexCount], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4)
        .addAttribute(this.vertexBuffers[this.vertexCount], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
    }

    this.vertexBuffers[this.vertexCount].upload(buffer.vertices, 0);
    this.vao = this.vaos[this.vertexCount].bind();

    /// render the groups..
    for (i = 0; i < groupCount; i++) {

        var group = groups[i];
        var groupTextureCount = group.textureCount;
        shader = this.shaders[groupTextureCount-1];

        if(!shader)
        {
            shader = this.shaders[groupTextureCount-1] = generateMultiTextureShader(gl, groupTextureCount);
            //console.log("SHADER generated for " + textureCount + " textures")
        }

        this.renderer.bindShader(shader);

        for (var j = 0; j < groupTextureCount; j++)
        {
            this.renderer.bindTexture(group.textures[j], j);
        }

        // set the blend mode..
        this.renderer.state.setBlendMode( group.blend );

        gl.drawElements(gl.TRIANGLES, group.size * 6, gl.UNSIGNED_SHORT, group.start * 6 * 2);
    }

    // reset elements for the next flush
    this.currentIndex = 0;
};

/**
 * Starts a new sprite batch.
 *
 */
SpriteRenderer.prototype.start = function ()
{
    //this.renderer.bindShader(this.shader);
    //TICK %= 1000;
};

SpriteRenderer.prototype.stop = function ()
{
    this.flush();
    this.vao.unbind();
};
/**
 * Destroys the SpriteBatch.
 *
 */
SpriteRenderer.prototype.destroy = function ()
{
    for (var i = 0; i < this.vaoMax; i++) {
        this.vertexBuffers[i].destroy();
        this.vaos[i].destroy();
    }

    this.indexBuffer.destroy();

    this.renderer.off('prerender', this.onPrerender, this);
    ObjectRenderer.prototype.destroy.call(this);

    for (i = 0; i < this.shaders.length; i++) {

        if(this.shaders[i])
        {
            this.shaders[i].destroy();
        }
    }

    this.vertexBuffers = null;
    this.vaos = null;
    this.indexBuffer = null;
    this.indices = null;

    this.sprites = null;

    for (i = 0; i < this.buffers.length; i++) {
        this.buffers[i].destroy();
    }

};
<|endoftext|>


<commit_before>import React from 'react';
import reactGA from 'react-ga';
import amountModifier from '../lib/amount-modifier';
import listener from '../lib/listener.js';
import form from '../lib/form.js';

var NOT_SUBMITTING = 0;
var SIGNUP_SUBMITTING = 1;
var STRIPE_SUBMITTING = 2;
var PAYPAL_SUBMITTING = 3;

var formMixin = {
  contextTypes: {
    intl: React.PropTypes.object
  },
  propTypes: {
    currency: React.PropTypes.object,
    presets: React.PropTypes.array,
    amount: React.PropTypes.string,
    frequency: React.PropTypes.string
  },
  getInitialState: function() {
    return {
      paymentType: "",
      submitting: NOT_SUBMITTING,
      currency: this.props.currency,
      frequency: this.props.frequency || "",
      amount: ""
    };
  },
  componentDidMount: function() {
    form.updateState("currency", this.state.currency);
    form.updateState("presets", this.props.presets);
    form.updateField("amount", this.props.amount);
    form.updateField("frequency", this.props.frequency);

    listener.on("fieldUpdated", this.onFieldUpdated);
    listener.on("stateUpdated", this.onStateUpdated);
  },
  componentWillUnmount: function() {
    listener.off("stateUpdated", this.onStateUpdated);
    listener.off("fieldUpdated", this.onFieldUpdated);
  },
  onFieldUpdated: function(e) {
    var detail = e.detail;
    var field = detail.field;
    var value = detail.value;
    if (field === "frequency") {
      this.setState({
        frequency: value
      });
    }
    if (field === "amount") {
      this.setState({
        amount: value
      });
    }
  },
  onStateUpdated: function(e) {
    var detail = e.detail;
    var state = detail.state;
    var value = detail.value;
    if (state === "currency") {
      this.setState({
        currency: value
      });
    }
  },
  submit: function(action, props, success, error) {
    props.locale = this.context.intl.locale;
    var currency = this.state.currency;
    if (currency) {
      props.currency = currency.code;
    }

    fetch(action, {
      method: 'post',
      credentials: 'same-origin',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(props)
    }).then(function(response) {
      var responseContent;
      var callback = success;
      if (!response.headers.get("content-type")) {
        responseContent = response.text();
      } else {
        responseContent = response.json();
      }
      if (!response.ok) {
        callback = error;
      }
      responseContent.then(function(result) {
        if (callback) {
          callback(result);
        }
      });
    });
  },
  stripeSuccess: function(data) {
    this.doStripeSuccess(data, "thank-you");
  },
  thunderbirdStripeSuccess: function(data) {
    this.doStripeSuccess(data, "thunderbird/thank-you");
  },
  doStripeSuccess: function(data, location) {
    var transactionId = data.id;
    var amount;
    var currency;
    var email = data.email || "";
    var country = data.country || "";
    var donationFrequency = data.frequency;
    this.setState({
      submitting: NOT_SUBMITTING
    });
    if (donationFrequency === "monthly") {
      currency = data.currency;
      // Stripe plans are a multiple of the currencies equivilent of Cents
      // e.g. £5/month = 500 £0.01 subscriptions
      amount = data.quantity;
    } else {
      amount = data.amount;
      currency = data.currency;
    }

    location = location || "thank-you";
    // If we are already signed up, send to share.
    if (data.signup) {
      location = "share";
      email = "";
      country = "";
    }

    var params = '?payment=Stripe&str_amount=' + amount + '&str_currency=' + currency + '&str_id=' +transactionId + '&str_frequency=' + donationFrequency;

    if (email) {
      params += "&email=" + email;
    }
    if (country) {
      params += "&country=" + country;
    }
    var page = '/' + this.context.intl.locale + '/' + location + '/';
    window.location = page + params;
  },
  stripeError: function(error) {
    form.error("other", this.context.intl.formatMessage({id: 'could_not_complete'}) + " [" + error + "]");
    this.setState({
      submitting: NOT_SUBMITTING
    });
  },
  stripeCheckout: function(validate, props) {
    var submit = this.submit;
    var success = this.stripeSuccess;
    var error = this.stripeError;
    var valid = form.validate(validate);
    var description = this.context.intl.formatMessage({id: "mozilla_donation"});
    var handlerDesc = this.context.intl.formatMessage({id: "donate_now"});
    var appName = this.props.appName;
    var submitProps= {};
    if (!valid || this.state.submitting) {
      return;
    }
    this.setState({
      submitting: STRIPE_SUBMITTING
    });

    submitProps = form.buildProps(props);
    if (appName === "thunderbird") {
      description = "Thunderbird";
      success = this.thunderbirdStripeSuccess;
    } else if (appName === "glassroomnyc") {
      description = "glassroomnyc";
    }
    if (submitProps.frequency === "monthly") {
      description = this.context.intl.formatMessage({id: "mozilla_monthly_donation"});
      handlerDesc = this.context.intl.formatMessage({id: "donate_monthly"});
      if (appName === "thunderbird") {
        description = "Thunderbird monthly";
      } else if (appName === "glassroomnyc") {
        description = "glassroomnyc monthly";
      }
    }

    var locale = this.context.intl.locale;
    var currency = this.state.currency && this.state.currency.code;
    var handler = StripeCheckout.configure({
      // Need to get this from .env
      key: process.env.STRIPE_PUBLIC_KEY,
      image: process.env.APPLICATION_URI + '/assets/images/mozilla-circular.911f4f7f4e6682c9893b8441d2e09df40cea80e2.png',
      zipCode: true,
      allowRememberMe: false,
      billingAddress: true,
      locale: locale,
      closed: () => {
        this.setState({
          submitting: NOT_SUBMITTING
        });
      },
      token: function(response) {
        var checkoutProps = {
          frequency: submitProps.frequency,
          amount: submitProps.amount,
          stripeToken: response.id,
          currency: currency,
          locale: submitProps.locale,
          email: response.email,
          code: response.card.address_zip,
          description: description
        };

        checkoutProps.country = response.card.address_country;
        checkoutProps.address = response.card.address_line1;
        checkoutProps.city = response.card.address_city;
        checkoutProps.first = response.card.name;

        submit("/api/stripe-checkout", checkoutProps, success, function(response) {
          if (response.stripe) {
            error(response.stripe.rawType);
          } else {
            error(response.error);
          }
        });
      }
    });

    // Open Checkout with further options
    handler.open({
      name: appName || this.context.intl.formatMessage({id: "mozilla_foundation"}),
      description: handlerDesc,
      currency: currency,
      // Stripe wants cents.
      amount: amountModifier.stripe(submitProps.amount, currency)
    });
  },
  paypal: function(validate, props) {
    var valid = form.validate(validate);
    var submitProps = {};
    var description = this.context.intl.formatMessage({id: "mozilla_donation"});
    var appName = this.props.appName;
    if (valid) {
      this.setState({
        submitting: PAYPAL_SUBMITTING
      });
      submitProps = form.buildProps(props);

      if (appName === "thunderbird") {
        description = "Thunderbird";
      } else if (appName === "glassroomnyc") {
        description = "glassroomnyc";
      }
      if (submitProps.frequency === "monthly") {
        description = this.context.intl.formatMessage({id: "mozilla_monthly_donation"});
        if (appName === "thunderbird") {
          description = "Thunderbird monthly";
        } else if (appName === "glassroomnyc") {
          description = "glassroomnyc monthly";
        }
      }
      submitProps.description = description;
      submitProps.appName = appName || "mozillafoundation";
      this.submit("/api/paypal", submitProps, function(json) {
        window.location = json.endpoint + "/cgi-bin/webscr?cmd=_express-checkout&useraction=commit&token=" + json.token;
      });
    }
  },
  doSignupSuccess: function(result, location) {
    this.setState({
      submitting: NOT_SUBMITTING
    });
    reactGA.event({
      category: "Signup",
      action: "Submitted the form",
      label: "donate"
    });
    var page = '/' + this.context.intl.locale + location;
    window.location = page;
  },
  signupSuccess: function(result) {
    this.doSignupSuccess(result, '/share/');
  },
  thundebirdSignupSuccess: function(result) {
    this.doSignupSuccess(result, '/thunderbird/share/');
  },
  signupError: function(result) {
    this.setState({
      submitting: NOT_SUBMITTING
    });
    form.error("other", this.context.intl.formatMessage({id: 'try_again_later'}));
  },
  doSignup: function(url, validate, props, success, error) {
    var valid = form.validate(validate);
    var submitProps = {};
    if (valid) {
      this.setState({
        submitting: SIGNUP_SUBMITTING
      });
      submitProps = form.buildProps(props);
      this.submit(url, submitProps, success, error);
    }
  },
  signup: function(validate, props) {
    this.doSignup("/api/signup/basket", validate, props, this.signupSuccess, this.signupError);
  },
  mailchimp: function(validate, props) {
    this.doSignup("/api/signup/mailchimp", validate, props, this.thundebirdSignupSuccess, this.signupError);
  }
};

module.exports = formMixin;
<commit_msg>adding ga event on error<commit_after>import React from 'react';
import reactGA from 'react-ga';
import amountModifier from '../lib/amount-modifier';
import listener from '../lib/listener.js';
import form from '../lib/form.js';

var NOT_SUBMITTING = 0;
var SIGNUP_SUBMITTING = 1;
var STRIPE_SUBMITTING = 2;
var PAYPAL_SUBMITTING = 3;

var formMixin = {
  contextTypes: {
    intl: React.PropTypes.object
  },
  propTypes: {
    currency: React.PropTypes.object,
    presets: React.PropTypes.array,
    amount: React.PropTypes.string,
    frequency: React.PropTypes.string
  },
  getInitialState: function() {
    return {
      paymentType: "",
      submitting: NOT_SUBMITTING,
      currency: this.props.currency,
      frequency: this.props.frequency || "",
      amount: ""
    };
  },
  componentDidMount: function() {
    form.updateState("currency", this.state.currency);
    form.updateState("presets", this.props.presets);
    form.updateField("amount", this.props.amount);
    form.updateField("frequency", this.props.frequency);

    listener.on("fieldUpdated", this.onFieldUpdated);
    listener.on("stateUpdated", this.onStateUpdated);
  },
  componentWillUnmount: function() {
    listener.off("stateUpdated", this.onStateUpdated);
    listener.off("fieldUpdated", this.onFieldUpdated);
  },
  onFieldUpdated: function(e) {
    var detail = e.detail;
    var field = detail.field;
    var value = detail.value;
    if (field === "frequency") {
      this.setState({
        frequency: value
      });
    }
    if (field === "amount") {
      this.setState({
        amount: value
      });
    }
  },
  onStateUpdated: function(e) {
    var detail = e.detail;
    var state = detail.state;
    var value = detail.value;
    if (state === "currency") {
      this.setState({
        currency: value
      });
    }
  },
  submit: function(action, props, success, error) {
    props.locale = this.context.intl.locale;
    var currency = this.state.currency;
    if (currency) {
      props.currency = currency.code;
    }

    fetch(action, {
      method: 'post',
      credentials: 'same-origin',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(props)
    }).then(function(response) {
      var responseContent;
      var callback = success;
      if (!response.headers.get("content-type")) {
        responseContent = response.text();
      } else {
        responseContent = response.json();
      }
      if (!response.ok) {
        callback = error;
      }
      responseContent.then(function(result) {
        if (callback) {
          callback(result);
        }
      });
    });
  },
  stripeSuccess: function(data) {
    this.doStripeSuccess(data, "thank-you");
  },
  thunderbirdStripeSuccess: function(data) {
    this.doStripeSuccess(data, "thunderbird/thank-you");
  },
  doStripeSuccess: function(data, location) {
    var transactionId = data.id;
    var amount;
    var currency;
    var email = data.email || "";
    var country = data.country || "";
    var donationFrequency = data.frequency;
    this.setState({
      submitting: NOT_SUBMITTING
    });
    if (donationFrequency === "monthly") {
      currency = data.currency;
      // Stripe plans are a multiple of the currencies equivilent of Cents
      // e.g. £5/month = 500 £0.01 subscriptions
      amount = data.quantity;
    } else {
      amount = data.amount;
      currency = data.currency;
    }

    location = location || "thank-you";
    // If we are already signed up, send to share.
    if (data.signup) {
      location = "share";
      email = "";
      country = "";
    }

    var params = '?payment=Stripe&str_amount=' + amount + '&str_currency=' + currency + '&str_id=' +transactionId + '&str_frequency=' + donationFrequency;

    if (email) {
      params += "&email=" + email;
    }
    if (country) {
      params += "&country=" + country;
    }
    var page = '/' + this.context.intl.locale + '/' + location + '/';
    window.location = page + params;
  },
  stripeError: function(error) {
    form.error("other", this.context.intl.formatMessage({id: 'could_not_complete'}) + " [" + error + "]");
    reactGA.event({
      category: "User Flow",
      action: "Card Error",
      label: error
    });
    this.setState({
      submitting: NOT_SUBMITTING
    });
  },
  stripeCheckout: function(validate, props) {
    var submit = this.submit;
    var success = this.stripeSuccess;
    var error = this.stripeError;
    var valid = form.validate(validate);
    var description = this.context.intl.formatMessage({id: "mozilla_donation"});
    var handlerDesc = this.context.intl.formatMessage({id: "donate_now"});
    var appName = this.props.appName;
    var submitProps= {};
    if (!valid || this.state.submitting) {
      return;
    }
    this.setState({
      submitting: STRIPE_SUBMITTING
    });

    submitProps = form.buildProps(props);
    if (appName === "thunderbird") {
      description = "Thunderbird";
      success = this.thunderbirdStripeSuccess;
    } else if (appName === "glassroomnyc") {
      description = "glassroomnyc";
    }
    if (submitProps.frequency === "monthly") {
      description = this.context.intl.formatMessage({id: "mozilla_monthly_donation"});
      handlerDesc = this.context.intl.formatMessage({id: "donate_monthly"});
      if (appName === "thunderbird") {
        description = "Thunderbird monthly";
      } else if (appName === "glassroomnyc") {
        description = "glassroomnyc monthly";
      }
    }

    var locale = this.context.intl.locale;
    var currency = this.state.currency && this.state.currency.code;
    var handler = StripeCheckout.configure({
      // Need to get this from .env
      key: process.env.STRIPE_PUBLIC_KEY,
      image: process.env.APPLICATION_URI + '/assets/images/mozilla-circular.911f4f7f4e6682c9893b8441d2e09df40cea80e2.png',
      zipCode: true,
      allowRememberMe: false,
      billingAddress: true,
      locale: locale,
      closed: () => {
        this.setState({
          submitting: NOT_SUBMITTING
        });
      },
      token: function(response) {
        var checkoutProps = {
          frequency: submitProps.frequency,
          amount: submitProps.amount,
          stripeToken: response.id,
          currency: currency,
          locale: submitProps.locale,
          email: response.email,
          code: response.card.address_zip,
          description: description
        };

        checkoutProps.country = response.card.address_country;
        checkoutProps.address = response.card.address_line1;
        checkoutProps.city = response.card.address_city;
        checkoutProps.first = response.card.name;

        submit("/api/stripe-checkout", checkoutProps, success, function(response) {
          if (response.stripe) {
            error(response.stripe.rawType);
          } else {
            error(response.error);
          }
        });
      }
    });

    // Open Checkout with further options
    handler.open({
      name: appName || this.context.intl.formatMessage({id: "mozilla_foundation"}),
      description: handlerDesc,
      currency: currency,
      // Stripe wants cents.
      amount: amountModifier.stripe(submitProps.amount, currency)
    });
  },
  paypal: function(validate, props) {
    var valid = form.validate(validate);
    var submitProps = {};
    var description = this.context.intl.formatMessage({id: "mozilla_donation"});
    var appName = this.props.appName;
    if (valid) {
      this.setState({
        submitting: PAYPAL_SUBMITTING
      });
      submitProps = form.buildProps(props);

      if (appName === "thunderbird") {
        description = "Thunderbird";
      } else if (appName === "glassroomnyc") {
        description = "glassroomnyc";
      }
      if (submitProps.frequency === "monthly") {
        description = this.context.intl.formatMessage({id: "mozilla_monthly_donation"});
        if (appName === "thunderbird") {
          description = "Thunderbird monthly";
        } else if (appName === "glassroomnyc") {
          description = "glassroomnyc monthly";
        }
      }
      submitProps.description = description;
      submitProps.appName = appName || "mozillafoundation";
      this.submit("/api/paypal", submitProps, function(json) {
        window.location = json.endpoint + "/cgi-bin/webscr?cmd=_express-checkout&useraction=commit&token=" + json.token;
      });
    }
  },
  doSignupSuccess: function(result, location) {
    this.setState({
      submitting: NOT_SUBMITTING
    });
    reactGA.event({
      category: "Signup",
      action: "Submitted the form",
      label: "donate"
    });
    var page = '/' + this.context.intl.locale + location;
    window.location = page;
  },
  signupSuccess: function(result) {
    this.doSignupSuccess(result, '/share/');
  },
  thundebirdSignupSuccess: function(result) {
    this.doSignupSuccess(result, '/thunderbird/share/');
  },
  signupError: function(result) {
    this.setState({
      submitting: NOT_SUBMITTING
    });
    form.error("other", this.context.intl.formatMessage({id: 'try_again_later'}));
  },
  doSignup: function(url, validate, props, success, error) {
    var valid = form.validate(validate);
    var submitProps = {};
    if (valid) {
      this.setState({
        submitting: SIGNUP_SUBMITTING
      });
      submitProps = form.buildProps(props);
      this.submit(url, submitProps, success, error);
    }
  },
  signup: function(validate, props) {
    this.doSignup("/api/signup/basket", validate, props, this.signupSuccess, this.signupError);
  },
  mailchimp: function(validate, props) {
    this.doSignup("/api/signup/mailchimp", validate, props, this.thundebirdSignupSuccess, this.signupError);
  }
};

module.exports = formMixin;
<|endoftext|>


<commit_before>// Provides basic search functionality for juxta
// Must be included _after_ the dropdown selector on the web page

searchConfig = {
    defaultSearchImage: true,
    defaultSearchPath: false,
    defaultSearchMeta: true,
    defaultSearchMode: 'infix', // Possible values: prefix, infix
    defaultCaseSensitive: false,
    maxMatches: 1000,
}

// https://stackoverflow.com/questions/610406/javascript-equivalent-to-printf-string-format
String.prototype.format = function() {
    var formatted = this;
    for (var i = 0; i < arguments.length; i++) {
        var regexp = new RegExp('\\{'+i+'\\}', 'gi');
        formatted = formatted.replace(regexp, arguments[i]);
    }
    return formatted;
};

var svg = null;
var diffusor = null;
var svgString = '';
var homeBounds = null;
function createSVGOverlay() {
    homeBounds = myDragon.viewport.getHomeBounds();
    /* Must be before the svg so that it is positioned underneath */
    diffusor = document.createElement("div");
    diffusor.id = "diffusor-overlay";
    myDragon.addOverlay({
        element: diffusor,
        location: homeBounds
    });

    svg = document.createElement("div");
    svg.id = "svg-overlay";
    myDragon.addOverlay({
        element: svg,
//        location: new OpenSeadragon.Rect(0, 0, 1.05, 1)
        location: homeBounds
    });
    svgString = '';
}
createSVGOverlay();

function clearSVGOverlay() {
    if (diffusor) {
        svgString = '';
        updateSVGOverlay('');
        diffusor.style.opacity = 0.0;
    } else {
        createSVGOverlay();
    }
}
console.log("HomeBounds: " + JSON.stringify(myDragon.viewport.getHomeBounds()));

var jprops = overlays.jprops;
console.log(JSON.stringify(jprops));
function updateSVGOverlay(svgXML) {

    svgString += svgXML;
    svg.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" style="position:absolute;z-index:10;margin:0;padding:0;top:0;left:0;width:100%;height:100%" viewBox="{0} {1} {2} {3}">{4}</svg>'.format(homeBounds.x, homeBounds.y, homeBounds.width, homeBounds.height, svgString);

    //    svg.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" style="position:absolute;z-index:10;margin:0;padding:0;top:0;left:0;width:100%;height:100%" viewBox="{0} {1} {2} {3}">{4}</svg>'.format(0, 0, 1, 1, svgString);

//    svg.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" style="position:absolute;z-index:10;margin:0;padding:0;top:0;left:0;width:100%;height:100%" viewBox="0 0 ' + jprops.colCount + ' ' + jprops.rowCount + '">' + svgString + '</svg>';
    diffusor.style.opacity = 0.8;
}

function addBox(boxID) {

    x = boxID % jprops.colCount;
    y = Math.floor(boxID / jprops.colCount);
    boxW = 1 / jprops.colCount;
    boxH = 1 / jprops.rowCount;
    lineWidth = 0.01 * 1 / Math.min(jprops.colCount, jprops.rowCount);

   xFactor = homeBounds.width+homeBounds.x;
    yFactor = homeBounds.height+homeBounds.y;

//    console.log("box={0], x={1}, y={2}, xFactor={3}, yFactor={4}".format(boxID, x, y, xFactor, yFactor));
    //boxW *= xFactor;
    //boxH *= yFactor;
    x = x / jprops.colCount;
    y = y / jprops.rowCount * 0.923;
    boxH *= 0.923;

    updateSVGOverlay('<rect x="{0}" y="{1}" width="{2}" height="{3}" style="fill:transparent;stroke-width:{4};stroke:{5}" />\n'.format(x, y, boxW, boxH, lineWidth, "#8888ff"));
}
for (var i = 0 ; i < 15 ; i++) {
    addBox(i*14);
    addBox(i*14+3);
    addBox(i*14+6);
    addBox(i*14+9);
}

//updateSVGOverlay('<rect x="-2.125" y="0" width="4" height="4" style="fill:transparent;stroke-width:0.1;stroke:#ffffff" />\n')
//updateSVGOverlay('<rect x="-1.46" y="0" width="13" height="19" style="fill:transparent;stroke-width:0.1;stroke:#00ffff" />\n')

function clearSearch() {
    console.log("Clearing previous search result")
}

// Returns { matchCount: int, matches: [index*] }
function simpleSearch(query, searchImage = searchConfig.defaultSearchImage, searchPath = searchConfig.defaultSearchPath, searchMeta = searchConfig.defaultSearchMeta, searchMode = searchConfig.defaultSearchMode, caseSensitive = searchConfig.defaultCaseSensitive) {
    var metaCache = overlays.metaCache[0];
    if (metaCache.status != 'ready') {
        console.error("Error: Cannot perform search for '" + query + "' as no overlays.metaCache is available");
        return
    }
    if (metaCache.source != "0_0.json") {
        console.error("Error: overlays.metaCache[0].source == '" + metaCache.source + "'. It should be '0_0.json'. Make sure to set ASYNC_META_SIDE to more than the number of horizontal and vertical images in the collage when rendering");
        return;
    }
    console.log("Searching for '" + query + "'");

    var matches = [];
    var matchCount = 0;
    var queryL = caseSensitive ? query : query.toLowerCase();
    for (var index = 0 ; index < metaCache.meta.length ; index++) {
        var full = metaCache.meta[index];
        if (jprops.metaIncludesOrigin) {
            // TODO: Move pre- and post-fix to the json metadata files
            var path = metaCache.prefix + full.split('|')[0] + metaCache.postfix;
            var last = path.lastIndexOf("/");
            var image = last == -1 ? path : path.substr(last+1);

            // TODO: Create a better splitter that handles multiple |
            var meta = full.split('|')[1];
        } else {
            meta = full;
        }
        if (typeof(result.meta) == 'undefined') { // Happens for single images without metadata
            meta = '';
        }
        if (!caseSensitive) {
            path = path.toLowerCase();
            image = image.toLowerCase();
            meta = meta.toLowerCase();
        }

        if ((searchImage && (searchMode == 'prefix' && image.startsWith(query)) || (searchMode == 'infix' && image.includes(query))) ||
            (searchPath && (searchMode == 'prefix' && path.startsWith(query)) || (searchMode == 'infix' && path.includes(query))) ||
            (searchMeta && (searchMode == 'prefix' && meta.startsWith(query)) || (searchMode == 'infix' && meta.includes(query)))) {
            matchCount++;
            if (matchCount <= searchConfig.maxMatches) {
                matches.push(index);
             }
        }
    }
    console.log("Got " + matchCount + " matches for query '" + query + "'");
    return { matchCount: matchCount, matches: matches };
}

function enableSearch() {
    // Optional drop down with predefined searches
    if (document.getElementById('fixed_search')) {
        console.log("Enabling predefined searches");
        document.getElementById('fixed_search').onchange = function() {
            if (this.value == 'clear') {
                clearSearch();
            } else {
                simpleSearch(this.value);
            }
        }
    } else {
        console.log("Unable to locate select-element 'fixed_search': Predefined searches will not be available");
    }
}
enableSearch();

<commit_msg>Connected search and display<commit_after>// Provides basic search functionality for juxta
// Must be included _after_ the dropdown selector on the web page

searchConfig = {
    defaultSearchImage: true,
    defaultSearchPath: false,
    defaultSearchMeta: true,
    defaultSearchMode: 'infix', // Possible values: prefix, infix
    defaultCaseSensitive: false,
    maxMatches: 1000,
}

// https://stackoverflow.com/questions/610406/javascript-equivalent-to-printf-string-format
String.prototype.format = function() {
    var formatted = this;
    for (var i = 0; i < arguments.length; i++) {
        var regexp = new RegExp('\\{'+i+'\\}', 'gi');
        formatted = formatted.replace(regexp, arguments[i]);
    }
    return formatted;
};

var svg = null;
var diffusor = null;
var svgString = '';
var homeBounds = null;
function createSVGOverlay() {
    homeBounds = myDragon.viewport.getHomeBounds();
    /* Must be before the svg so that it is positioned underneath */
    diffusor = document.createElement("div");
    diffusor.id = "diffusor-overlay";
    myDragon.addOverlay({
        element: diffusor,
        location: homeBounds
    });

    svg = document.createElement("div");
    svg.id = "svg-overlay";
    myDragon.addOverlay({
        element: svg,
//        location: new OpenSeadragon.Rect(0, 0, 1.05, 1)
        location: homeBounds
    });
    svgString = '';
}
createSVGOverlay();

function clearSVGOverlay() {
    if (diffusor) {
        svgString = '';
        updateSVGOverlay('');
        diffusor.style.opacity = 0.0;
    } else {
        createSVGOverlay();
    }
}
console.log("HomeBounds: " + JSON.stringify(myDragon.viewport.getHomeBounds()));

var jprops = overlays.jprops;
console.log("jprops: " + JSON.stringify(jprops));
function updateSVGOverlay(svgXML) {

    svgString += svgXML;
    svg.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" style="position:absolute;z-index:10;margin:0;padding:0;top:0;left:0;width:100%;height:100%" viewBox="{0} {1} {2} {3}">{4}</svg>'.format(homeBounds.x, homeBounds.y, homeBounds.width, homeBounds.height, svgString);

    //    svg.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" style="position:absolute;z-index:10;margin:0;padding:0;top:0;left:0;width:100%;height:100%" viewBox="{0} {1} {2} {3}">{4}</svg>'.format(0, 0, 1, 1, svgString);

//    svg.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" style="position:absolute;z-index:10;margin:0;padding:0;top:0;left:0;width:100%;height:100%" viewBox="0 0 ' + jprops.colCount + ' ' + jprops.rowCount + '">' + svgString + '</svg>';
    diffusor.style.opacity = 0.8;
}

function addBoxes(boxIDs) {
    var svgBoxes = '';
    for (var i = 0 ; i < boxIDs.length; i++) {
        svgBoxes += getBox(boxIDs[i]);
    }
    updateSVGOverlay(svgBoxes);
}
function addBox(boxID) {
    updateSVGOverlay(getBox(boxID));
}
function getBox(boxID) {
    x = boxID % jprops.colCount;
    y = Math.floor(boxID / jprops.colCount);
    boxW = 1 / jprops.colCount;
    boxH = 1 / jprops.rowCount;
    lineWidth = 0.01 * 1 / Math.min(jprops.colCount, jprops.rowCount);

   xFactor = homeBounds.width+homeBounds.x;
    yFactor = homeBounds.height+homeBounds.y;

//    console.log("box={0], x={1}, y={2}, xFactor={3}, yFactor={4}".format(boxID, x, y, xFactor, yFactor));
    //boxW *= xFactor;
    //boxH *= yFactor;
    x = x / jprops.colCount;
    y = y / jprops.rowCount * 0.923;
    boxH *= 0.923;

    return'<rect x="{0}" y="{1}" width="{2}" height="{3}" style="fill:transparent;stroke-width:{4};stroke:{5}" />\n'.format(x, y, boxW, boxH, lineWidth, "#8888ff");
}

for (var i = 0 ; i < 15 ; i++) {
    for (var j = 0 ; j < 15 ; j++) {
     //   addBox(i*14+(j*3));
    }
}

//updateSVGOverlay('<rect x="-2.125" y="0" width="4" height="4" style="fill:transparent;stroke-width:0.1;stroke:#ffffff" />\n')
//updateSVGOverlay('<rect x="-1.46" y="0" width="13" height="19" style="fill:transparent;stroke-width:0.1;stroke:#00ffff" />\n')

function clearSearch() {
    console.log("Clearing previous search result")
    clearSVGOverlay();
}

// Returns { matchCount: int, matches: [index*] }
function searchAndDisplay(query, searchImage = searchConfig.defaultSearchImage, searchPath = searchConfig.defaultSearchPath, searchMeta = searchConfig.defaultSearchMeta, searchMode = searchConfig.defaultSearchMode, caseSensitive = searchConfig.defaultCaseSensitive) {
    clearSearch();
    var result = simpleSearch(query, searchImage, searchPath, searchMeta, searchMode, caseSensitive);
    addBoxes(result.matches);
    return result;
}

// Returns { matchCount: int, matches: [index*] }
function simpleSearch(query, searchImage = searchConfig.defaultSearchImage, searchPath = searchConfig.defaultSearchPath, searchMeta = searchConfig.defaultSearchMeta, searchMode = searchConfig.defaultSearchMode, caseSensitive = searchConfig.defaultCaseSensitive) {
    var metaCache = overlays.metaCache[0];
    if (metaCache.status != 'ready') {
        console.error("Error: Cannot perform search for '" + query + "' as no overlays.metaCache is available");
        return
    }
    if (metaCache.source != "0_0.json") {
        console.error("Error: overlays.metaCache[0].source == '" + metaCache.source + "'. It should be '0_0.json'. Make sure to set ASYNC_META_SIDE to more than the number of horizontal and vertical images in the collage when rendering");
        return;
    }
    console.log("Searching for '" + query + "'");

    var matches = [];
    var matchCount = 0;
    var queryL = caseSensitive ? query : query.toLowerCase();
    for (var index = 0 ; index < metaCache.meta.length ; index++) {
        var full = metaCache.meta[index];
        if (jprops.metaIncludesOrigin) {
            // TODO: Move pre- and post-fix to the json metadata files
            var path = metaCache.prefix + full.split('|')[0] + metaCache.postfix;
            var last = path.lastIndexOf("/");
            var image = last == -1 ? path : path.substr(last+1);

            // TODO: Create a better splitter that handles multiple |
            var meta = full.split('|')[1];
        } else {
            meta = full;
        }
        if (typeof(result.meta) == 'undefined') { // Happens for single images without metadata
            meta = '';
        }
        if (!caseSensitive) {
            path = path.toLowerCase();
            image = image.toLowerCase();
            meta = meta.toLowerCase();
        }

        if ((searchImage && (searchMode == 'prefix' && image.startsWith(query)) || (searchMode == 'infix' && image.includes(query))) ||
            (searchPath && (searchMode == 'prefix' && path.startsWith(query)) || (searchMode == 'infix' && path.includes(query))) ||
            (searchMeta && (searchMode == 'prefix' && meta.startsWith(query)) || (searchMode == 'infix' && meta.includes(query)))) {
            matchCount++;
            if (matchCount <= searchConfig.maxMatches) {
                matches.push(index);
             }
        }
    }
    console.log("Got " + matchCount + " matches for query '" + query + "'");
    return { matchCount: matchCount, matches: matches };
}

function enableSearch() {
    // Optional drop down with predefined searches
    if (document.getElementById('fixed_search')) {
        console.log("Enabling predefined searches");
        document.getElementById('fixed_search').onchange = function() {
            if (this.value == 'clear') {
                clearSearch();
            } else {
                searchAndDisplay(this.value);
            }
        }
    } else {
        console.log("Unable to locate select-element 'fixed_search': Predefined searches will not be available");
    }
}
enableSearch();

<|endoftext|>