<commit_before>import React, { Component } from "react";
import SwipeableViews from "react-swipeable-views";
import { Route, matchPath } from "react-router";
import PropTypes from "prop-types";

class SwipeableRoutes extends Component {
  static contextTypes = {
    router: PropTypes.shape({
      route: PropTypes.object.isRequired
    }).isRequired
  };

  handleIndexChange = (index, type) => {
    // Trigger the location change to the route path
    const { router: { history } } = this.context;
    const paths = React.Children.map(
      this.props.children,
      element => element.props.path
    );
    history.push(paths[index]);

    // Call the onChangeIndex if it's set
    if (typeof this.props.onChangeIndex === "function") {
      this.props.onChangeIndex(index, type);
    }
  };

  componentDidMount() {
    const { children } = this.props;
    const { router: { history } } = this.context;
    this.unlistenHistory = history.listen(location => {
      // When the location changes, call onChangeIndex with the route index
      React.Children.forEach(children, (element, index) => {
        const { path: pathProp, exact, strict, from } = element.props;
        const path = pathProp || from;

        if (matchPath(location.pathname, { path, exact, strict })) {
          if (typeof this.props.onChangeIndex === "function") {
            this.props.onChangeIndex(index);
          }
        }
      });
    });
  }

  componentWillUnmount() {
    this.unlistenHistory();
  }

  render() {
    const { children, index, ...rest } = this.props;
    const { history, route, staticContext } = this.context.router;
    const location = this.props.location || route.location;
    const props = { location, history, staticContext };

    let matchedIndex = 0; // If there's no match, render the first route
    if (index) {
      matchedIndex = index;
    } else {
      React.Children.forEach(children, (element, index) => {
        const { path: pathProp, exact, strict, from } = element.props;
        const path = pathProp || from;

        if (matchPath(location.pathname, { path, exact, strict })) {
          matchedIndex = index;
        }
      });
    }

    return (
      <SwipeableViews
        {...rest}
        index={matchedIndex}
        onChangeIndex={this.handleIndexChange}
      >
        {React.Children.map(children, element => {
          const { component, render, children } = element.props;
          // A lot of this code is borrowed from the render method of
          // Route. Why can't I just render the Route then?
          // Because Route only renders the component|render|children
          // if there's a match with the location, while here I render
          // regardless of the location.
          return component
            ? React.createElement(component, props)
            : render
                ? render()
                : children
                    ? typeof children === "function"
                        ? children(props)
                        : !Array.isArray(children) || children.length // Preact defaults to empty children array
                            ? React.Children.only(children)
                            : null
                    : null;
        })}
      </SwipeableViews>
    );
  }
}

export default SwipeableRoutes;
<commit_msg>Now updating location on index change<commit_after>import React, { Component } from "react";
import SwipeableViews from "react-swipeable-views";
import { Route, matchPath } from "react-router";
import PropTypes from "prop-types";

class SwipeableRoutes extends Component {
  static contextTypes = {
    router: PropTypes.shape({
      route: PropTypes.object.isRequired
    }).isRequired
  };

  handleIndexChange = (index, type) => {
    // Trigger the location change to the route path
    const { router: { history } } = this.context;
    const paths = React.Children.map(
      this.props.children,
      element => element.props.path
    );
    history.push(paths[index]);

    // Call the onChangeIndex if it's set
    if (typeof this.props.onChangeIndex === "function") {
      this.props.onChangeIndex(index, type);
    }
  };

  componentDidMount() {
    const { children } = this.props;
    const { router: { history } } = this.context;
    this.unlistenHistory = history.listen(location => {
      // When the location changes, call onChangeIndex with the route index
      React.Children.forEach(children, (element, index) => {
        const { path: pathProp, exact, strict, from } = element.props;
        const path = pathProp || from;

        if (matchPath(location.pathname, { path, exact, strict })) {
          if (typeof this.props.onChangeIndex === "function") {
            this.props.onChangeIndex(index);
          }
        }
      });
    });
  }

  componentWillUnmount() {
    this.unlistenHistory();
  }

  componentDidUpdate(prevProps) {
    const { router: { history } } = this.context;

    // If index prop changed, change the location to the path of that route
    if (prevProps.index !== this.props.index) {
      const paths = React.Children.map(
        this.props.children,
        element => element.props.path
      );
      history.push(paths[this.props.index]);
    }
  }

  render() {
    const { children, index, ...rest } = this.props;
    const { history, route, staticContext } = this.context.router;
    const location = this.props.location || route.location;
    const props = { location, history, staticContext };

    let matchedIndex = 0; // If there's no match, render the first route
    if (index) {
      matchedIndex = index;
    } else {
      React.Children.forEach(children, (element, index) => {
        const { path: pathProp, exact, strict, from } = element.props;
        const path = pathProp || from;

        if (matchPath(location.pathname, { path, exact, strict })) {
          matchedIndex = index;
        }
      });
    }

    return (
      <SwipeableViews
        {...rest}
        index={matchedIndex}
        onChangeIndex={this.handleIndexChange}
      >
        {React.Children.map(children, element => {
          const { component, render, children } = element.props;
          // A lot of this code is borrowed from the render method of
          // Route. Why can't I just render the Route then?
          // Because Route only renders the component|render|children
          // if there's a match with the location, while here I render
          // regardless of the location.
          return component
            ? React.createElement(component, props)
            : render
              ? render()
              : children
                ? typeof children === "function"
                  ? children(props)
                  : !Array.isArray(children) || children.length // Preact defaults to empty children array
                    ? React.Children.only(children)
                    : null
                : null;
        })}
      </SwipeableViews>
    );
  }
}

export default SwipeableRoutes;
<|endoftext|>


<commit_before>var mathf = require("mathf");

var vec2 = module.exports;

vec2.create = function(x, y) {
    var out = new mathf.ArrayType(2);

    out[0] = x !== undefined ? x : 0;
    out[1] = y !== undefined ? y : 0;

    return out;
};

vec2.copy = function(out, a) {

    out[0] = a[0];
    out[1] = a[1];

    return out;
};

vec2.clone = function(a) {
    var out = new mathf.ArrayType(2);

    out[0] = a[0];
    out[1] = a[1];

    return out;
};

vec2.set = function(out, x, y) {

    out[0] = x !== undefined ? x : 0;
    out[1] = y !== undefined ? y : 0;

    return out;
};

vec2.add = function(out, a, b) {

    out[0] = a[0] + b[0];
    out[1] = a[1] + b[1];

    return a;
};

vec2.sub = function(out, a, b) {

    out[0] = a[0] - b[0];
    out[1] = a[1] - b[1];

    return out;
};

vec2.mul = function(out, a, b) {

    out[0] = a[0] * b[0];
    out[1] = a[1] * b[1];

    return out;
};

vec2.div = function(out, a, b) {
    var bx = b[0],
        by = b[1];

    out[0] = a[0] * (bx !== 0 ? 1 / bx : bx);
    out[1] = a[1] * (by !== 0 ? 1 / by : by);

    return out;
};

vec2.sadd = function(out, a, s) {

    out[0] = a[0] + s;
    out[1] = a[1] + s;

    return a;
};

vec2.ssub = function(out, a, s) {

    out[0] = a[0] - s;
    out[1] = a[1] - s;

    return out;
};

vec2.smul = function(out, a, s) {

    out[0] = a[0] * s;
    out[1] = a[1] * s;

    return out;
};

vec2.sdiv = function(out, a, s) {
    s = s !== 0 ? 1 / s : s;

    out[0] = a[0] * s;
    out[1] = a[1] * s;

    return out;
};

vec2.lengthSqValues = function(x, y) {

    return x * x + y * y;
};

vec2.lengthValues = function(x, y) {
    var lsq = vec2.lengthSqValues(x, y);

    return lsq !== 0 ? mathf.sqrt(lsq) : lsq;
};

vec2.invLengthValues = function(x, y) {
    var lsq = vec2.lengthSqValues(x, y);

    return lsq !== 0 ? 1 / mathf.sqrt(lsq) : lsq;
};

vec2.cross = function(a, b) {

    return a[0] * b[1] - a[1] * b[0];
};

vec2.dot = function(a, b) {

    return a[0] * b[0] + a[1] * b[1];
};

vec2.lengthSq = function(a) {

    return vec2.dot(a, a);
};

vec2.length = function(a) {
    var lsq = vec2.lengthSq(a);

    return lsq !== 0 ? mathf.sqrt(lsq) : lsq;
};

vec2.invLength = function(a) {
    var lsq = vec2.lengthSq(a);

    return lsq !== 0 ? 1 / mathf.sqrt(lsq) : lsq;
};

vec2.setLength = function(out, a, length) {
    var x = a[0],
        y = a[1],
        s = length * vec2.invLengthValues(x, y);

    out[0] = x * s;
    out[1] = y * s;

    return out;
};

vec2.normalize = function(out, a) {
    var x = a[0],
        y = a[1],
        invlsq = vec2.invLengthValues(x, y);

    out[0] = x * invlsq;
    out[1] = y * invlsq;

    return out;
};

vec2.inverse = function(out, a) {

    out[0] = a[0] * -1;
    out[1] = a[1] * -1;

    return out;
};

vec2.lerp = function(out, a, b, x) {
    var lerp = mathf.lerp;

    out[0] = lerp(a[0], b[0], x);
    out[1] = lerp(a[1], b[1], x);

    return out;
};

vec2.perp = function(out, a) {

    out[0] = -a[1];
    out[1] = a[0];

    return out;
};

vec2.perpL = function(out, a) {

    out[0] = a[1];
    out[1] = -a[0];

    return out;
};

vec2.min = function(out, a, b) {
    var ax = a[0],
        ay = a[1],
        bx = b[0],
        by = b[1];

    out[0] = bx < ax ? bx : ax;
    out[1] = by < ay ? by : ay;

    return out;
};

vec2.max = function(out, a, b) {
    var ax = a[0],
        ay = a[1],
        bx = b[0],
        by = b[1];

    out[0] = bx > ax ? bx : ax;
    out[1] = by > ay ? by : ay;

    return out;
};

vec2.clamp = function(out, a, min, max) {
    var x = a[0],
        y = a[1],
        minx = min[0],
        miny = min[1],
        maxx = max[0],
        maxy = max[1];

    out[0] = x < minx ? minx : x > maxx ? maxx : x;
    out[1] = y < miny ? miny : y > maxy ? maxy : y;

    return out;
};

vec2.transformAngle = function(out, a, angle) {
    var x = a[0],
        y = a[1],
        c = mathf.cos(angle),
        s = mathf.sin(angle);

    out[0] = x * c - y * s;
    out[1] = x * s + y * c;

    return out;
};

vec2.transformMat2 = function(out, a, m) {
    var x = a[0],
        y = a[1];

    out[0] = x * m[0] + y * m[2];
    out[1] = x * m[1] + y * m[3];

    return out;
};

vec2.transformMat32 = function(out, a, m) {
    var x = a[0],
        y = a[1];

    out[0] = x * m[0] + y * m[2] + m[4];
    out[1] = x * m[1] + y * m[3] + m[5];

    return out;
};

vec2.transformMat3 = function(out, a, m) {
    var x = a[0],
        y = a[1];

    out[0] = x * m[0] + y * m[3] + m[6];
    out[1] = x * m[1] + y * m[4] + m[7];

    return out;
};

vec2.transformMat4 = function(out, a, m) {
    var x = a[0],
        y = a[1];

    out[0] = x * m[0] + y * m[4] + m[12];
    out[1] = x * m[1] + y * m[5] + m[13];

    return out;
};

vec2.transformProjection = function(out, a, m) {
    var x = a[0],
        y = a[1],
        d = x * m[3] + y * m[7] + m[11] + m[15];

    d = d !== 0 ? 1 / d : d;

    out[0] = (x * m[0] + y * m[4] + m[12]) * d;
    out[1] = (x * m[1] + y * m[5] + m[13]) * d;

    return out;
};

vec2.positionFromMat32 = function(out, m) {

    out[0] = m[4];
    out[1] = m[5];

    return out;
};

vec2.positionFromMat4 = function(out, m) {

    out[0] = m[12];
    out[1] = m[13];

    return out;
};

vec2.scaleFromMat2 = function(out, m) {

    out[0] = vec2.lengthValues(m[0], m[2]);
    out[1] = vec2.lengthValues(m[1], m[3]);

    return out;
};

vec2.scaleFromMat32 = vec2.scaleFromMat2;

vec2.scaleFromMat3 = function(out, m) {

    out[0] = vec2.lengthValues(m[0], m[3]);
    out[1] = vec2.lengthValues(m[1], m[4]);

    return out;
};

vec2.scaleFromMat4 = function(out, m) {

    out[0] = vec2.lengthValues(m[0], m[4]);
    out[1] = vec2.lengthValues(m[1], m[5]);

    return out;
};

vec2.equal = function(a, b) {
    return !(
        a[0] !== b[0] ||
        a[1] !== b[1]
    );
};

vec2.notEqual = function(a, b) {
    return (
        a[0] !== b[0] ||
        a[1] !== b[1]
    );
};
<commit_msg>updates<commit_after>var mathf = require("mathf");

var vec2 = module.exports;

vec2.create = function(x, y) {
    var out = new mathf.ArrayType(2);

    out[0] = x !== undefined ? x : 0;
    out[1] = y !== undefined ? y : 0;

    return out;
};

vec2.copy = function(out, a) {

    out[0] = a[0];
    out[1] = a[1];

    return out;
};

vec2.clone = function(a) {
    var out = new mathf.ArrayType(2);

    out[0] = a[0];
    out[1] = a[1];

    return out;
};

vec2.set = function(out, x, y) {

    out[0] = x !== undefined ? x : 0;
    out[1] = y !== undefined ? y : 0;

    return out;
};

vec2.add = function(out, a, b) {

    out[0] = a[0] + b[0];
    out[1] = a[1] + b[1];

    return a;
};

vec2.sub = function(out, a, b) {

    out[0] = a[0] - b[0];
    out[1] = a[1] - b[1];

    return out;
};

vec2.mul = function(out, a, b) {

    out[0] = a[0] * b[0];
    out[1] = a[1] * b[1];

    return out;
};

vec2.div = function(out, a, b) {
    var bx = b[0],
        by = b[1];

    out[0] = a[0] * (bx !== 0 ? 1 / bx : bx);
    out[1] = a[1] * (by !== 0 ? 1 / by : by);

    return out;
};

vec2.sadd = function(out, a, s) {

    out[0] = a[0] + s;
    out[1] = a[1] + s;

    return a;
};

vec2.ssub = function(out, a, s) {

    out[0] = a[0] - s;
    out[1] = a[1] - s;

    return out;
};

vec2.smul = function(out, a, s) {

    out[0] = a[0] * s;
    out[1] = a[1] * s;

    return out;
};

vec2.sdiv = function(out, a, s) {
    s = s !== 0 ? 1 / s : s;

    out[0] = a[0] * s;
    out[1] = a[1] * s;

    return out;
};

vec2.lengthSqValues = function(x, y) {

    return x * x + y * y;
};

vec2.lengthValues = function(x, y) {
    var lsq = vec2.lengthSqValues(x, y);

    return lsq !== 0 ? mathf.sqrt(lsq) : lsq;
};

vec2.invLengthValues = function(x, y) {
    var lsq = vec2.lengthSqValues(x, y);

    return lsq !== 0 ? 1 / mathf.sqrt(lsq) : lsq;
};

vec2.cross = function(a, b) {

    return a[0] * b[1] - a[1] * b[0];
};

vec2.dot = function(a, b) {

    return a[0] * b[0] + a[1] * b[1];
};

vec2.lengthSq = function(a) {

    return vec2.dot(a, a);
};

vec2.length = function(a) {
    var lsq = vec2.lengthSq(a);

    return lsq !== 0 ? mathf.sqrt(lsq) : lsq;
};

vec2.invLength = function(a) {
    var lsq = vec2.lengthSq(a);

    return lsq !== 0 ? 1 / mathf.sqrt(lsq) : lsq;
};

vec2.setLength = function(out, a, length) {
    var x = a[0],
        y = a[1],
        s = length * vec2.invLengthValues(x, y);

    out[0] = x * s;
    out[1] = y * s;

    return out;
};

vec2.normalize = function(out, a) {
    var x = a[0],
        y = a[1],
        invlsq = vec2.invLengthValues(x, y);

    out[0] = x * invlsq;
    out[1] = y * invlsq;

    return out;
};

vec2.inverse = function(out, a) {

    out[0] = a[0] * -1;
    out[1] = a[1] * -1;

    return out;
};

vec2.lerp = function(out, a, b, x) {
    var lerp = mathf.lerp;

    out[0] = lerp(a[0], b[0], x);
    out[1] = lerp(a[1], b[1], x);

    return out;
};

vec2.perp = function(out, a) {

    out[0] = -a[1];
    out[1] = a[0];

    return out;
};

vec2.perpL = function(out, a) {

    out[0] = a[1];
    out[1] = -a[0];

    return out;
};

vec2.min = function(out, a, b) {
    var ax = a[0],
        ay = a[1],
        bx = b[0],
        by = b[1];

    out[0] = bx < ax ? bx : ax;
    out[1] = by < ay ? by : ay;

    return out;
};

vec2.max = function(out, a, b) {
    var ax = a[0],
        ay = a[1],
        bx = b[0],
        by = b[1];

    out[0] = bx > ax ? bx : ax;
    out[1] = by > ay ? by : ay;

    return out;
};

vec2.clamp = function(out, a, min, max) {
    var x = a[0],
        y = a[1],
        minx = min[0],
        miny = min[1],
        maxx = max[0],
        maxy = max[1];

    out[0] = x < minx ? minx : x > maxx ? maxx : x;
    out[1] = y < miny ? miny : y > maxy ? maxy : y;

    return out;
};

vec2.transformAngle = function(out, a, angle) {
    var x = a[0],
        y = a[1],
        c = mathf.cos(angle),
        s = mathf.sin(angle);

    out[0] = x * c - y * s;
    out[1] = x * s + y * c;

    return out;
};

vec2.transformMat2 = function(out, a, m) {
    var x = a[0],
        y = a[1];

    out[0] = x * m[0] + y * m[2];
    out[1] = x * m[1] + y * m[3];

    return out;
};

vec2.transformMat32 = function(out, a, m) {
    var x = a[0],
        y = a[1];

    out[0] = x * m[0] + y * m[2] + m[4];
    out[1] = x * m[1] + y * m[3] + m[5];

    return out;
};

vec2.transformMat3 = function(out, a, m) {
    var x = a[0],
        y = a[1];

    out[0] = x * m[0] + y * m[3] + m[6];
    out[1] = x * m[1] + y * m[4] + m[7];

    return out;
};

vec2.transformMat4 = function(out, a, m) {
    var x = a[0],
        y = a[1];

    out[0] = x * m[0] + y * m[4] + m[12];
    out[1] = x * m[1] + y * m[5] + m[13];

    return out;
};

vec2.transformProjection = function(out, a, m) {
    var x = a[0],
        y = a[1],
        d = x * m[3] + y * m[7] + m[11] + m[15];

    d = d !== 0 ? 1 / d : d;

    out[0] = (x * m[0] + y * m[4] + m[12]) * d;
    out[1] = (x * m[1] + y * m[5] + m[13]) * d;

    return out;
};

vec2.positionFromMat32 = function(out, m) {

    out[0] = m[4];
    out[1] = m[5];

    return out;
};

vec2.positionFromMat4 = function(out, m) {

    out[0] = m[12];
    out[1] = m[13];

    return out;
};

vec2.scaleFromMat2 = function(out, m) {

    out[0] = vec2.lengthValues(m[0], m[2]);
    out[1] = vec2.lengthValues(m[1], m[3]);

    return out;
};

vec2.scaleFromMat32 = vec2.scaleFromMat2;

vec2.scaleFromMat3 = function(out, m) {

    out[0] = vec2.lengthValues(m[0], m[3]);
    out[1] = vec2.lengthValues(m[1], m[4]);

    return out;
};

vec2.scaleFromMat4 = function(out, m) {

    out[0] = vec2.lengthValues(m[0], m[4]);
    out[1] = vec2.lengthValues(m[1], m[5]);

    return out;
};

vec2.equal = function(a, b) {
    return !(
        a[0] !== b[0] ||
        a[1] !== b[1]
    );
};

vec2.notEqual = function(a, b) {
    return (
        a[0] !== b[0] ||
        a[1] !== b[1]
    );
};

vec2.str = function(out) {

    return "Vec2(" + out[0] + ", " + out[1] + ")";
};
<|endoftext|>


<commit_before>import _ from 'lodash';

const Next = async ({
  for: _for,
  each,
  shouldNext,
  next,
  catch: _catch,
} = {}) => {
  try {
    if (_for && each) {
      const results = _.castArray(await _for());
      for (const [index, result] of results.entries()) {
        try {
          const eachNext = await each(result, index);
          await Next(eachNext);
        } catch (err) {
          if (_catch) {
            await _catch(err);
          } else {
            throw err;
          }
        }
      }
    }

    if (shouldNext) {
      const shouldNextOrNot = await shouldNext();
      if (!shouldNextOrNot) {
        return;
      }
    }

    if (next) {
      await Next(await next());
    }
  } catch (err) {
    if (_catch) {
      await _catch(err);
    } else {
      throw err;
    }
  }
}

export default Next;
<commit_msg>add catch nextable<commit_after>import _ from 'lodash';

const Next = async ({
  for: _for,
  each,
  shouldNext,
  next,
  catch: _catch,
} = {}) => {
  try {
    if (_for && each) {
      const results = _.castArray(await _for());
      for (const [index, result] of results.entries()) {
        try {
          const eachNext = await each(result, index);
          await Next(eachNext);
        } catch (err) {
          if (_catch) {
            await Next(await _catch(err));
          } else {
            throw err;
          }
        }
      }
    }

    if (shouldNext) {
      const shouldNextOrNot = await shouldNext();
      if (!shouldNextOrNot) {
        return;
      }
    }

    if (next) {
      await Next(await next());
    }
  } catch (err) {
    if (_catch) {
      await Next(await _catch(err));
    } else {
      throw err;
    }
  }
}

export default Next;
<|endoftext|>


<commit_before>import React from 'react'
import PropTypes from 'prop-types'

const mimeTypeRegexp = /^(application|audio|example|image|message|model|multipart|text|video)\/[a-z0-9\.\+\*-]+$/
const extRegexp = /\.[a-zA-Z0-9]*$/

class Files extends React.Component {
  constructor (props, context) {
    super(props, context)
    this.onDrop = this.onDrop.bind(this)
    this.onDragEnter = this.onDragEnter.bind(this)
    this.onDragLeave = this.onDragLeave.bind(this)
    this.openFileChooser = this.openFileChooser.bind(this)

    this.id = 1

    this.state = {
      files: []
    }
  }

  onDrop (event) {
    event.preventDefault()
    this.onDragLeave(event)

    // Collect added files, perform checking, cast pseudo-array to Array,
    // then return to method
    let filesAdded = event.dataTransfer ? event.dataTransfer.files : event.target.files

    // Multiple files dropped when not allowed
    if (this.props.multiple === false && filesAdded.length > 1) {
      filesAdded = [filesAdded[0]]
    }

    let files = []
    for (let i = 0; i < filesAdded.length; i++) {
      let file = filesAdded[i]

      // Assign file an id
      file.id = 'files-' + this.id++

      // Tell file it's own extension
      file.extension = this.fileExtension(file)

      // Tell file it's own readable size
      file.sizeReadable = this.fileSizeReadable(file.size)

      // Add preview, either image or file extension
      if (file.type && this.mimeTypeLeft(file.type) === 'image') {
        file.preview = {
          type: 'image',
          url: window.URL.createObjectURL(file)
        }
      } else {
        file.preview = {
          type: 'file'
        }
      }

      // Check for file max limit
      if (this.state.files.length + files.length >= this.props.maxFiles) {
        this.onError({
          code: 4,
          message: 'maximum file count reached'
        }, file)
        break
      }

      // If file is acceptable, push or replace
      if (this.fileTypeAcceptable(file) && this.fileSizeAcceptable(file)) {
        files.push(file)
      }
    }
    this.setState({
      files: this.props.multiple === false
        ? files
        : [...this.state.files, ...files]
    }, () => {
      this.props.onChange.call(this, this.state.files)
    })
  }

  onDragOver (event) {
    event.preventDefault()
    event.stopPropagation()
  }

  onDragEnter (event) {
    let el = this.dropzone
    el.className += ' ' + this.props.dropActiveClassName
  }

  onDragLeave (event) {
    let el = this.dropzone
    this.dropzone.className = el.className.replace(' ' + this.props.dropActiveClassName, '')
  }

  openFileChooser () {
    this.inputElement.value = null
    this.inputElement.click()
  }

  fileTypeAcceptable (file) {
    let accepts = this.props.accepts
    if (!accepts) {
      return true
    }

    const result = accepts.some(accept => {
      if (file.type && accept.match(mimeTypeRegexp)) {
        let typeLeft = this.mimeTypeLeft(file.type)
        let typeRight = this.mimeTypeRight(file.type)
        let acceptLeft = accept.split('/')[0]
        let acceptRight = accept.split('/')[1]
        if (acceptLeft && acceptRight) {
          if (acceptLeft === typeLeft && acceptRight === '*') {
            return true
          }
          if (acceptLeft === typeLeft && acceptRight === typeRight) {
            return true
          }
        }
      } else if (file.extension && accept.match(extRegexp)) {
        const ext = accept.substr(1)
        return file.extension.toLowerCase() === ext.toLowerCase()
      }
      return false
    })

    if (!result) {
      this.onError({
        code: 1,
        message: file.name + ' is not a valid file type'
      }, file)
    }

    return result
  }

  fileSizeAcceptable (file) {
    if (file.size > this.props.maxFileSize) {
      this.onError({
        code: 2,
        message: file.name + ' is too large'
      }, file)
      return false
    } else if (file.size < this.props.minFileSize) {
      this.onError({
        code: 3,
        message: file.name + ' is too small'
      }, file)
      return false
    } else {
      return true
    }
  }

  mimeTypeLeft (mime) {
    return mime.split('/')[0]
  }

  mimeTypeRight (mime) {
    return mime.split('/')[1]
  }

  fileExtension (file) {
    let extensionSplit = file.name.split('.')
    if (extensionSplit.length > 1) {
      return extensionSplit[extensionSplit.length - 1]
    } else {
      return 'none'
    }
  }

  fileSizeReadable (size) {
    if (size >= 1000000000) {
      return Math.ceil(size / 1000000000) + 'GB'
    } else if (size >= 1000000) {
      return Math.ceil(size / 1000000) + 'MB'
    } else if (size >= 1000) {
      return Math.ceil(size / 1000) + 'kB'
    } else {
      return Math.ceil(size) + 'B'
    }
  }

  onError (error, file) {
    this.props.onError.call(this, error, file)
  }

  removeFile (fileToRemove) {
    this.setState({
      files: this.state.files.filter(file => file.id !== fileToRemove.id)
    }, () => {
      this.props.onChange.call(this, this.state.files)
    })
  }

  removeFiles () {
    this.setState({
      files: []
    }, () => {
      this.props.onChange.call(this, this.state.files)
    })
  }

  render () {
    const inputAttributes = {
      type: 'file',
      accept: this.props.accepts ? this.props.accepts.join() : '',
      multiple: this.props.multiple,
      name: this.props.name,
      style: { display: 'none' },
      ref: (element) => {
        this.inputElement = element
      },
      onChange: this.onDrop
    }

    return (

        <input
          {...inputAttributes}
        />

 { this.dropzone = dropzone }}
          style={this.props.style}
        >
          {this.props.children}

    )
  }
}

Files.propTypes = {
  children: PropTypes.oneOfType([
    PropTypes.arrayOf(PropTypes.node),
    PropTypes.node
  ]),
  className: PropTypes.string.isRequired,
  dropActiveClassName: PropTypes.string,
  onChange: PropTypes.func,
  onError: PropTypes.func,
  accepts: PropTypes.array,
  multiple: PropTypes.bool,
  maxFiles: PropTypes.number,
  maxFileSize: PropTypes.number,
  minFileSize: PropTypes.number,
  clickable: PropTypes.bool,
  name: PropTypes.string,
  style: PropTypes.object
}

Files.defaultProps = {
  onChange: function (files) {
    console.log(files)
  },
  onError: function (error, file) {
    console.log('error code ' + error.code + ': ' + error.message)
  },
  className: 'files-dropzone',
  dropActiveClassName: 'files-dropzone-active',
  accepts: null,
  multiple: true,
  maxFiles: Infinity,
  maxFileSize: Infinity,
  minFileSize: 0,
  name: 'file',
  clickable: true
}

export default Files
<commit_msg>linting<commit_after>import React from 'react'
import PropTypes from 'prop-types'

const mimeTypeRegexp = /^(application|audio|example|image|message|model|multipart|text|video)\/[a-z0-9\.\+\*-]+$/;
const extRegexp = /\.[a-zA-Z0-9]*$/;

class Files extends React.Component {
  constructor (props, context) {
    super(props, context)
    this.onDrop = this.onDrop.bind(this)
    this.onDragEnter = this.onDragEnter.bind(this)
    this.onDragLeave = this.onDragLeave.bind(this)
    this.openFileChooser = this.openFileChooser.bind(this)

    this.id = 1

    this.state = {
      files: []
    }
  }

  onDrop (event) {
    event.preventDefault()
    this.onDragLeave(event)

    // Collect added files, perform checking, cast pseudo-array to Array,
    // then return to method
    let filesAdded = event.dataTransfer ? event.dataTransfer.files : event.target.files

    // Multiple files dropped when not allowed
    if (this.props.multiple === false && filesAdded.length > 1) {
      filesAdded = [filesAdded[0]]
    }

    let files = []
    for (let i = 0; i < filesAdded.length; i++) {
      let file = filesAdded[i]

      // Assign file an id
      file.id = 'files-' + this.id++

      // Tell file it's own extension
      file.extension = this.fileExtension(file)

      // Tell file it's own readable size
      file.sizeReadable = this.fileSizeReadable(file.size)

      // Add preview, either image or file extension
      if (file.type && this.mimeTypeLeft(file.type) === 'image') {
        file.preview = {
          type: 'image',
          url: window.URL.createObjectURL(file)
        }
      } else {
        file.preview = {
          type: 'file'
        }
      }

      // Check for file max limit
      if (this.state.files.length + files.length >= this.props.maxFiles) {
        this.onError({
          code: 4,
          message: 'maximum file count reached'
        }, file)
        break
      }

      // If file is acceptable, push or replace
      if (this.fileTypeAcceptable(file) && this.fileSizeAcceptable(file)) {
        files.push(file)
      }
    }
    this.setState({
      files: this.props.multiple === false
        ? files
        : [...this.state.files, ...files]
    }, () => {
      this.props.onChange.call(this, this.state.files)
    })
  }

  onDragOver (event) {
    event.preventDefault()
    event.stopPropagation()
  }

  onDragEnter (event) {
    let el = this.dropzone
    el.className += ' ' + this.props.dropActiveClassName
  }

  onDragLeave (event) {
    let el = this.dropzone
    this.dropzone.className = el.className.replace(' ' + this.props.dropActiveClassName, '')
  }

  openFileChooser () {
    this.inputElement.value = null
    this.inputElement.click()
  }

  fileTypeAcceptable (file) {
    let accepts = this.props.accepts;
    if (!accepts) {
      return true
    }

    const result = accepts.some(accept => {
      if (file.type && accept.match(mimeTypeRegexp)) {
        let typeLeft = this.mimeTypeLeft(file.type)
        let typeRight = this.mimeTypeRight(file.type)
        let acceptLeft = accept.split('/')[0]
        let acceptRight = accept.split('/')[1]
        if (acceptLeft && acceptRight) {
          if (acceptLeft === typeLeft && acceptRight === '*') {
            return true
          }
          if (acceptLeft === typeLeft && acceptRight === typeRight) {
            return true
          }
        }
      } else if (file.extension && accept.match(extRegexp)) {
        const ext = accept.substr(1);
        return file.extension.toLowerCase() === ext.toLowerCase();
      }
      return false
    });

    if (!result) {
      this.onError({
        code: 1,
        message: file.name + ' is not a valid file type'
      }, file)
    }

    return result
  }

  fileSizeAcceptable (file) {
    if (file.size > this.props.maxFileSize) {
      this.onError({
        code: 2,
        message: file.name + ' is too large'
      }, file)
      return false
    } else if (file.size < this.props.minFileSize) {
      this.onError({
        code: 3,
        message: file.name + ' is too small'
      }, file)
      return false
    } else {
      return true
    }
  }

  mimeTypeLeft (mime) {
    return mime.split('/')[0]
  }

  mimeTypeRight (mime) {
    return mime.split('/')[1]
  }

  fileExtension (file) {
    let extensionSplit = file.name.split('.')
    if (extensionSplit.length > 1) {
      return extensionSplit[extensionSplit.length - 1]
    } else {
      return 'none'
    }
  }

  fileSizeReadable (size) {
    if (size >= 1000000000) {
      return Math.ceil(size / 1000000000) + 'GB'
    } else if (size >= 1000000) {
      return Math.ceil(size / 1000000) + 'MB'
    } else if (size >= 1000) {
      return Math.ceil(size / 1000) + 'kB'
    } else {
      return Math.ceil(size) + 'B'
    }
  }

  onError (error, file) {
    this.props.onError.call(this, error, file)
  }

  removeFile (fileToRemove) {
    this.setState({
      files: this.state.files.filter(file => file.id !== fileToRemove.id)
    }, () => {
      this.props.onChange.call(this, this.state.files)
    })
  }

  removeFiles () {
    this.setState({
      files: []
    }, () => {
      this.props.onChange.call(this, this.state.files)
    })
  }

  render () {
    const inputAttributes = {
      type: 'file',
      accept: this.props.accepts ? this.props.accepts.join() : '',
      multiple: this.props.multiple,
      name: this.props.name,
      style: { display: 'none' },
      ref: (element) => {
        this.inputElement = element
      },
      onChange: this.onDrop
    }

    return (

        <input
          {...inputAttributes}
        />

 { this.dropzone = dropzone }}
          style={this.props.style}
        >
          {this.props.children}

    )
  }
}

Files.propTypes = {
  children: PropTypes.oneOfType([
    PropTypes.arrayOf(PropTypes.node),
    PropTypes.node
  ]),
  className: PropTypes.string.isRequired,
  dropActiveClassName: PropTypes.string,
  onChange: PropTypes.func,
  onError: PropTypes.func,
  accepts: PropTypes.array,
  multiple: PropTypes.bool,
  maxFiles: PropTypes.number,
  maxFileSize: PropTypes.number,
  minFileSize: PropTypes.number,
  clickable: PropTypes.bool,
  name: PropTypes.string,
  style: PropTypes.object
}

Files.defaultProps = {
  onChange: function (files) {
    console.log(files)
  },
  onError: function (error, file) {
    console.log('error code ' + error.code + ': ' + error.message)
  },
  className: 'files-dropzone',
  dropActiveClassName: 'files-dropzone-active',
  accepts: null,
  multiple: true,
  maxFiles: Infinity,
  maxFileSize: Infinity,
  minFileSize: 0,
  name: 'file',
  clickable: true
}

export default Files
<|endoftext|>


<commit_before>'use strict';

export default {

  Container : require('./container'),
  Row : require('./row'),
  Col : require('./col'),

  IconButton : require('./icon-button'),

  AppHeader : require('./layout/side-nav'),
  TopNav : require('./layout/top-nav'),
  SideNav : require('./layout/side-nav'),

  Modal : require('./modal'),

  Collapsible : require('./collapsible/collapsible'),

  // Utilities
  LayeredComponentMixin : require('./mixins/layered-component'),
  TransitionIn : require('./transition-in'),

};
<commit_msg>Add table component to components index<commit_after>'use strict';

export default {

  Container : require('./container'),
  Row : require('./row'),
  Col : require('./col'),

  IconButton : require('./icon-button'),

  AppHeader : require('./layout/side-nav'),
  TopNav : require('./layout/top-nav'),
  SideNav : require('./layout/side-nav'),

  Modal : require('./modal'),

  Table : require('./table'),

  Collapsible : require('./collapsible/collapsible'),

  // Utilities
  LayeredComponentMixin : require('./mixins/layered-component'),
  TransitionIn : require('./transition-in'),

};
<|endoftext|>


<commit_before>import React from 'react';
import withSideEffect from 'react-side-effect';
import {default as _} from 'lodash';
import {default as immutable} from 'seamless-immutable';

const DocumentModifier = React.createClass({
	displayName: 'DocumentModifier',

	propTypes: {
		children: React.PropTypes.oneOfType([React.PropTypes.element, React.PropTypes.arrayOf(React.PropTypes.element)]),
		properties: React.PropTypes.object.isRequired,
	},

	render: function render() {
		return this.props.children;
	},
});

function reducePropsToState(propsList = []) {
	const finalProps = {}; //our goal is return the final document.`props` tree for the current application state - this is a "pure" function

	//This will traverse the tree, starting with the highest-most component and working its way down through all the children.
	_.forEach(propsList, (props) => {
		//At each level, we will merge into `props` from the `properties` attribute - overwriting any parent-ly set values
		//This means that the further nested component's properties will win,
		//whilst still preserving parent properties that never get overwritten
		_.merge(finalProps, props.properties);
	});

	return finalProps;
}

//UTILITIES
function deepDOMUpdate(target, source, domNode = document, defaultClear = '') {
	//This function will update the `domNode` (and its child properties), as described by the `target`
	//The `source` is used to mark (and clear) props that were removed in the last reduce

	const result = target.asMutable(); //we will return the new `virtualDocumentProps`

	_.forEach(target, (value, key) => {
		if (_.isObject(value)) {
			deepDOMUpdate(target[key], source[key], domNode[key]); //if it's an object, then recurse
		} else {
			result[key] = source ? source[key] : undefined; //clear out a now null property
			domNode[key] = result[key] || defaultClear; //update the DOM
		}
	});

	return result;
}
//END UTILITIES

//Let's hold (in memory) an immutable map of all the properties currently on the DOM
let virtualDocumentProps = immutable({});

function handleStateChangeOnClient(props) {
	virtualDocumentProps = virtualDocumentProps.merge(props, {deep: true}); //merge in the final props for current state into our virtual model
	virtualDocumentProps = immutable(deepDOMUpdate(virtualDocumentProps, props, document)); //Update the DOM to reflect the current state, clearing out invalid props
}

export default withSideEffect(
	reducePropsToState,
	handleStateChangeOnClient
)(DocumentModifier);
<commit_msg>Wrap render in <div> element - I should really run the tests before updating - whoops<commit_after>import React from 'react';
import withSideEffect from 'react-side-effect';
import {default as _} from 'lodash';
import {default as immutable} from 'seamless-immutable';

const DocumentModifier = React.createClass({
	displayName: 'DocumentModifier',

	propTypes: {
		children: React.PropTypes.oneOfType([React.PropTypes.element, React.PropTypes.arrayOf(React.PropTypes.element)]),
		properties: React.PropTypes.object.isRequired,
	},

	render: function render() {
		return <div>{ this.props.children }</div>;
	},
});

function reducePropsToState(propsList = []) {
	const finalProps = {}; //our goal is return the final document.`props` tree for the current application state - this is a "pure" function

	//This will traverse the tree, starting with the highest-most component and working its way down through all the children.
	_.forEach(propsList, (props) => {
		//At each level, we will merge into `props` from the `properties` attribute - overwriting any parent-ly set values
		//This means that the further nested component's properties will win,
		//whilst still preserving parent properties that never get overwritten
		_.merge(finalProps, props.properties);
	});

	return finalProps;
}

//UTILITIES
function deepDOMUpdate(target, source, domNode = document, defaultClear = '') {
	//This function will update the `domNode` (and its child properties), as described by the `target`
	//The `source` is used to mark (and clear) props that were removed in the last reduce

	const result = target.asMutable(); //we will return the new `virtualDocumentProps`

	_.forEach(target, (value, key) => {
		if (_.isObject(value)) {
			deepDOMUpdate(target[key], source[key], domNode[key]); //if it's an object, then recurse
		} else {
			result[key] = source ? source[key] : undefined; //clear out a now null property
			domNode[key] = result[key] || defaultClear; //update the DOM
		}
	});

	return result;
}
//END UTILITIES

//Let's hold (in memory) an immutable map of all the properties currently on the DOM
let virtualDocumentProps = immutable({});

function handleStateChangeOnClient(props) {
	virtualDocumentProps = virtualDocumentProps.merge(props, {deep: true}); //merge in the final props for current state into our virtual model
	virtualDocumentProps = immutable(deepDOMUpdate(virtualDocumentProps, props, document)); //Update the DOM to reflect the current state, clearing out invalid props
}

export default withSideEffect(
	reducePropsToState,
	handleStateChangeOnClient
)(DocumentModifier);
<|endoftext|>


<commit_before>/**
 *
 * @module fun-test
 */
;(function () {
  'use strict'

  /* imports */
  var Task = require('data.task')
  var funTry = require('fun-try')
  var compose = require('fun-compose')
  var object = require('fun-object')
  var fn = require('fun-function')

  var merge = object.concat(function (a, b) {
    return b === undefined ? a : b
  })

  var defaultConfig = {
    mapData: id,
    mapSubject: id,
    action: function (options) {
      return Task.of(options.data)
    },
    update: function (options) {
      return options.result
    },
    assertion: id
  }

  /* exports */
  module.exports = {
    concat: concat,
    of: compose(of, merge(defaultConfig)),
    empty: empty,
    sync: sync
  }

  /**
   *
   * @function module:fun-test.sync
   *
   * @param {Object} options - all input parameters
   * @param {Function} options.predicate - to check result
   * @param {Array} options.inputs - to feed to test function
   * @param {Function} options.action - to use instead of apply
   * @param {Function} options.contra - preapplied ot subject
   *
   * @return {Function} subject -> Task(Boolean)
   */
  function sync (options) {
    return fn.composeAll([
      Task.of,
      options.predicate,
      (options.action || fn.apply)(options.inputs),
      (options.contra || fn.id)
    ])
  }

  function concat (t1, t2) {
    return function (options) {
      return Task.of(options)
        .chain(t1)
        .map(object.of('data'))
        .map(merge(options))
        .chain(t2)
    }
  }

  /**
   *
   * @function module:fun-test.of
   *
   * @param {Object} config - all function parameters
   * @param {Function} config.mapData - Data -> Data
   * @param {Function} config.mapSubject - Subject -> Subject
   * @param {Function} config.action - ({ Data, Subject }) -> Task Result
   * @param {Function} config.assertion - Data -> Data | throws
   * @param {Function} config.update - ({ Data, Result }) -> Data
   * @param {String} config.comment - for the reporter
   * @param {Number} config.timeout - in ms
   *
   * @return {Function} ({data, subject, reporter}) -> Task Data
   */
  function of (config) {
    return function (options) {
      return Task.of(options)
        .map(object.keep(['data', 'subject']))
        .map(object.ap({ data: config.mapData, subject: config.mapSubject }))
        .chain(funTry(config.action))
        .chain(id)
        .chain(funTry(config.assertion))
        .map(object.of('result'))
        .orElse(compose(Task.of, object.of('error')))
        .map(merge({ data: options.data, subject: options.subject }))
        .map(merge(config))
        .chain(options.reporter)
        .map(object.keep(['data', 'result', 'error']))
        .map(config.update)
    }
  }

  function empty () {
    return function (options) {
      return Task.of()
    }
  }

  function id (x) {
    return x
  }
})()

<commit_msg>[PATCH][REMOVE] dependency on fun-compose<commit_after>/**
 *
 * @module fun-test
 */
;(function () {
  'use strict'

  /* imports */
  var Task = require('data.task')
  var funTry = require('fun-try')
  var object = require('fun-object')
  var fn = require('fun-function')

  var merge = object.concat(function (a, b) {
    return b === undefined ? a : b
  })

  var defaultConfig = {
    mapData: id,
    mapSubject: id,
    action: function (options) {
      return Task.of(options.data)
    },
    update: function (options) {
      return options.result
    },
    assertion: id
  }

  /* exports */
  module.exports = {
    concat: concat,
    of: fn.compose(of, merge(defaultConfig)),
    empty: empty,
    sync: sync
  }

  /**
   *
   * @function module:fun-test.sync
   *
   * @param {Object} options - all input parameters
   * @param {Function} options.predicate - to check result
   * @param {Array} options.inputs - to feed to test function
   * @param {Function} options.action - to use instead of apply
   * @param {Function} options.contra - preapplied ot subject
   *
   * @return {Function} subject -> Task(Boolean)
   */
  function sync (options) {
    return fn.composeAll([
      Task.of,
      options.predicate,
      (options.action || fn.apply)(options.inputs),
      (options.contra || fn.id)
    ])
  }

  function concat (t1, t2) {
    return function (options) {
      return Task.of(options)
        .chain(t1)
        .map(object.of('data'))
        .map(merge(options))
        .chain(t2)
    }
  }

  /**
   *
   * @function module:fun-test.of
   *
   * @param {Object} config - all function parameters
   * @param {Function} config.mapData - Data -> Data
   * @param {Function} config.mapSubject - Subject -> Subject
   * @param {Function} config.action - ({ Data, Subject }) -> Task Result
   * @param {Function} config.assertion - Data -> Data | throws
   * @param {Function} config.update - ({ Data, Result }) -> Data
   * @param {String} config.comment - for the reporter
   * @param {Number} config.timeout - in ms
   *
   * @return {Function} ({data, subject, reporter}) -> Task Data
   */
  function of (config) {
    return function (options) {
      return Task.of(options)
        .map(object.keep(['data', 'subject']))
        .map(object.ap({ data: config.mapData, subject: config.mapSubject }))
        .chain(funTry(config.action))
        .chain(id)
        .chain(funTry(config.assertion))
        .map(object.of('result'))
        .orElse(fn.compose(Task.of, object.of('error')))
        .map(merge({ data: options.data, subject: options.subject }))
        .map(merge(config))
        .chain(options.reporter)
        .map(object.keep(['data', 'result', 'error']))
        .map(config.update)
    }
  }

  function empty () {
    return function (options) {
      return Task.of()
    }
  }

  function id (x) {
    return x
  }
})()

<|endoftext|>


<commit_before>import deepstream from 'deepstream.io-client-js';

export const statuses = deepstream.CONSTANTS.CONNECTION_STATE;

// TODO: Make Symbols out of data model parts/tags ?

/**
 * Simple object check.
 * @param item
 * @returns {boolean}
 */
export function isObject(item) {
  return item && typeof item === 'object' && !Array.isArray(item);
}

/**
 * Deep merge two objects.
 * @param target
 * @param ...sources
 */
export function mergeDeep(target, ...sources) {
  if (!sources.length) return target;
  const source = sources.shift();

  if (isObject(target) && isObject(source)) {
    for (const key in source) {
      if (isObject(source[key])) {
        if (!target[key]) Object.assign(target, { [key]: {} });
        mergeDeep(target[key], source[key]);
      } else {
        Object.assign(target, { [key]: source[key] });
      }
    }
  }
  return mergeDeep(target, ...sources);
}

export function addEntry(list, str) {
  if (list.getEntries().indexOf(str) > -1) return;
  return list.addEntry(str);
}

function getExistingP(type, pathStr) {
  return new Promise((resolve, reject) =>
    this.record.has(pathStr, (error, hasRecord) => {
      if (error) reject(new Error(error));
      if (hasRecord) this.record[`get${type}`](pathStr).whenReady(r => resolve(r));
      else reject(new Error(`${type} does not exist`));
    }),
  );
}

function getRecordP(name) {
  return new Promise(resolve => this.record.getRecord(name).whenReady(r => resolve(r)));
}

function snapshotP(name) {
  return new Promise((resolve, reject) =>
    this.record.snapshot(name, (error, data) => {
      if (error) reject(new Error(error));
      else resolve(data);
    }),
  );
}

function listedRecordP(listPath, recordId, obj, overwrite) {
  const id = recordId || this.getUid();
  const rPath = `${listPath}/${id}`;
  return Promise.all([
    this.record.getRecordP(rPath),
    this.record.getListP(listPath),
  ]).then(([record, list]) => {
    try {
      // Update list:
      addEntry(list, rPath);
      // Update record:
      let created = false;
      const r = record.get();
      if (Object.keys(r).length === 0) {
        record.set(obj);
        created = true;
      } else if (overwrite) {
        Object.keys(obj).forEach(key => record.set(key, obj[key]));
      } else {
        record.set(mergeDeep(r, obj));
      }
      Promise.resolve(id, created);
    } catch (err) {
      Promise.reject(err);
    }
  });
}

export default function getClient(url, options) {
  const c = deepstream(url, options);
  c.record.getRecordP = getRecordP.bind(c);
  c.record.getExistingRecordP = getExistingP.bind(c, 'Record');
  c.record.getExistingListP = getExistingP.bind(c, 'List');
  c.record.snapshotP = snapshotP.bind(c);
  c.record.listedRecordP = listedRecordP.bind(c);
  return c;
}

function withTenant(func, name, ...args) {
  return this[func](`${this.getTenant()}/${name}`, ...args);
}

export function getClientWithTenant(url, options, tenant = 'demo') {
  const c = getClient(url, options);
  c.getTenant = function () {
    return this;
  }.bind(tenant); // non-closure getter
  c.record.getRecordPT = withTenant.bind(c, 'getRecordP');
  c.record.getExistingRecordPT = withTenant.bind(c, 'getExistingRecordP');
  c.record.getExistingListPT = withTenant.bind(c, 'getExistingListP');
  c.record.snapshotPT = withTenant.bind(c, 'snapshotP');
  c.record.listedRecordPT = withTenant.bind(c, 'listedRecordP');
  return c;
}

// ------------------------------------------------------------
//  FOR SINGLETON USE
export const ds = { client: undefined };
<commit_msg>withTenant bug fix<commit_after>import deepstream from 'deepstream.io-client-js';

export const statuses = deepstream.CONSTANTS.CONNECTION_STATE;

// TODO: Make Symbols out of data model parts/tags ?

/**
 * Simple object check.
 * @param item
 * @returns {boolean}
 */
export function isObject(item) {
  return item && typeof item === 'object' && !Array.isArray(item);
}

/**
 * Deep merge two objects.
 * @param target
 * @param ...sources
 */
export function mergeDeep(target, ...sources) {
  if (!sources.length) return target;
  const source = sources.shift();

  if (isObject(target) && isObject(source)) {
    for (const key in source) {
      if (isObject(source[key])) {
        if (!target[key]) Object.assign(target, { [key]: {} });
        mergeDeep(target[key], source[key]);
      } else {
        Object.assign(target, { [key]: source[key] });
      }
    }
  }
  return mergeDeep(target, ...sources);
}

export function addEntry(list, str) {
  if (list.getEntries().indexOf(str) > -1) return;
  return list.addEntry(str);
}

function getExistingP(type, pathStr) {
  return new Promise((resolve, reject) =>
    this.record.has(pathStr, (error, hasRecord) => {
      if (error) reject(new Error(error));
      if (hasRecord) this.record[`get${type}`](pathStr).whenReady(r => resolve(r));
      else reject(new Error(`${type} does not exist`));
    }),
  );
}

function getRecordP(name) {
  return new Promise(resolve => this.record.getRecord(name).whenReady(r => resolve(r)));
}

function snapshotP(name) {
  return new Promise((resolve, reject) =>
    this.record.snapshot(name, (error, data) => {
      if (error) reject(new Error(error));
      else resolve(data);
    }),
  );
}

function listedRecordP(listPath, recordId, obj, overwrite) {
  const id = recordId || this.getUid();
  const rPath = `${listPath}/${id}`;
  return Promise.all([
    this.record.getRecordP(rPath),
    this.record.getListP(listPath),
  ]).then(([record, list]) => {
    try {
      // Update list:
      addEntry(list, rPath);
      // Update record:
      let created = false;
      const r = record.get();
      if (Object.keys(r).length === 0) {
        record.set(obj);
        created = true;
      } else if (overwrite) {
        Object.keys(obj).forEach(key => record.set(key, obj[key]));
      } else {
        record.set(mergeDeep(r, obj));
      }
      Promise.resolve(id, created);
    } catch (err) {
      Promise.reject(err);
    }
  });
}

export default function getClient(url, options) {
  const c = deepstream(url, options);
  c.record.getRecordP = getRecordP.bind(c);
  c.record.getExistingRecordP = getExistingP.bind(c, 'Record');
  c.record.getExistingListP = getExistingP.bind(c, 'List');
  c.record.snapshotP = snapshotP.bind(c);
  c.record.listedRecordP = listedRecordP.bind(c);
  return c;
}

function withTenant(func, name, ...args) {
  return this.record[func](`${this.getTenant()}/${name}`, ...args);
}

export function getClientWithTenant(url, options, tenant = 'demo') {
  const c = getClient(url, options);
  c.getTenant = function () {
    return this;
  }.bind(tenant); // non-closure getter
  c.record.getRecordPT = withTenant.bind(c, 'getRecordP');
  c.record.getExistingRecordPT = withTenant.bind(c, 'getExistingRecordP');
  c.record.getExistingListPT = withTenant.bind(c, 'getExistingListP');
  c.record.snapshotPT = withTenant.bind(c, 'snapshotP');
  c.record.listedRecordPT = withTenant.bind(c, 'listedRecordP');
  return c;
}

// ------------------------------------------------------------
//  FOR SINGLETON USE
export const ds = { client: undefined };
<|endoftext|>


<commit_before>import assign from 'object-assign'

import { serializeParams, isFunction, getUrlQueryParamByName, updateQueryStringParamByName } from './lib'
import store from './store'

const win = window

const canUsePromise = (function () {
  return 'Promise' in win &&
    typeof isFunction(Promise)
})()

const noop = function () {}

const encodeC = encodeURIComponent
const doc = document
const head = doc.head || doc.getElementsByTagName('head')[0]

const TIMEOUT_CONST = 2000

const defaultConfig = {
  timeout: TIMEOUT_CONST,
  retryTimes: 2,
  backup: null,
  params: {},
  jsonp: 'callback',
  name: null,
  cache: false,
  useStore: false,
  storeCheck: null,
  storeSign: null,
  storeCheckKey: null,
  dataCheck: null,
  charset: 'UTF-8'
}

let script
let timestamp = new Date().getTime()

function jsonp (url, opts, cb) {
  if (isFunction(url)) {
    cb = url
    opts = {}
  } else if (url && typeof url === 'object') {
    cb = opts
    opts = url || {}
    url = opts.url
  }
  if (isFunction(opts)) {
    cb = opts
    opts = {}
  }
  if (!opts) {
    opts = {}
  }
  opts = assign({}, defaultConfig, opts)
  url = url || opts.url
  cb = cb || noop
  if (!url || typeof url !== 'string') {
    cb(new Error('Param url is needed!'))
    if (!jsonp.promiseClose && canUsePromise) {
      return new Promise((resolve, reject) => {
        return reject(new Error('Param url is needed!'))
      })
    }
    return
  }
  const urlWithParams = generateJsonpUrlWithParams(url, opts.params)
  // first get data from store
  const datafromStore = getDataFromStore({
    useStore: opts.useStore,
    storeKey: urlWithParams,
    storeCheck: opts.storeCheck,
    storeCheckKey: opts.storeCheckKey,
    storeSign: opts.storeSign,
    dataCheck: opts.dataCheck
  })
  if (datafromStore) {
    cb(null, datafromStore)
    if (!jsonp.promiseClose && canUsePromise) {
      return new Promise(resolve => {
        return resolve(datafromStore)
      })
    }
    return
  }
  opts.originalUrl = urlWithParams
  if (!jsonp.promiseClose && canUsePromise) {
    return new Promise((resolve, reject) => {
      fetchData(urlWithParams, opts, (err, data) => {
        if (err) {
          cb(err)
          return reject(err)
        }
        cb(null, data)
        resolve(data)
      })
    })
  }
  fetchData(urlWithParams, opts, cb)
}

function generateJsonpUrlWithParams (url, params) {
  params = typeof params === 'string' ? params : serializeParams(params)
  url += (~url.indexOf('?') ? '&' : '?') + `${params}`
  url = url.replace('?&', '?')
  return url
}

function fetchData (url, opts, cb) {
  const originalUrl = opts.originalUrl
  const charset = opts.charset
  const funcId = opts.name || `__jsonp${timestamp++}`
  const jsonpUrlQueryParam = getUrlQueryParamByName(url, opts.jsonp)
  if (jsonpUrlQueryParam) {
    if (jsonpUrlQueryParam === '?') {
      url = updateQueryStringParamByName(url, opts.jsonp, encodeC(funcId))
    }
  } else {
    url += (url.split('').pop() === '&' ? '' : '&') + `${opts.jsonp}=${encodeC(funcId)}`
  }
  if (!opts.cache) {
    url += `&_=${new Date().getTime()}`
  }

  const timeout = opts.timeout != null ? opts.timeout : TIMEOUT_CONST
  // when timeout, will try to retry
  const timer = setTimeout(() => {
    cleanup(funcId)
    // no retryTimes left, go to backup
    if (typeof opts.retryTimes === 'number' && opts.retryTimes > 0) {
      opts.retryTimes--
      return fetchData(originalUrl, opts, cb)
    }
    if (fallback(originalUrl, opts, cb) === false) {
      return cb(new Error('Timeout and no data return'))
    }
  }, timeout)

  function cleanup (funcId) {
    if (script.parentNode) {
      script.parentNode.removeChild(script)
    }
    win[funcId] = noop
    if (timer) {
      clearTimeout(timer)
    }
  }

  win[funcId] = function (data) {
    cleanup(funcId)
    if (opts.dataCheck) {
      if (opts.dataCheck(data) !== false) {
        // write data to store
        setDataToStore({
          useStore: opts.useStore,
          storeKey: originalUrl,
          data
        })
        return cb(null, data)
      }
      if (fallback(originalUrl, opts, cb) === false) {
        cb(new Error('Data check error, and no fallback'))
      }
    } else {
      // write data to store
      setDataToStore({
        useStore: opts.useStore,
        storeKey: originalUrl,
        data
      })
      cb(null, data)
    }
  }
  appendScriptTagToHead({
    url,
    charset
  })
}

function storeCheckFn (storeData, storeCheckKey, storeSign) {
  if (storeData && storeCheckKey && storeSign) {
    return storeData[storeCheckKey] && storeData[storeCheckKey] === storeSign
  }
  return false
}

function getDataFromStore ({ useStore, storeKey, storeCheck, storeCheckKey, storeSign, dataCheck }) {
  useStore = useStore ? store.enabled : false
  if (useStore) {
    const storeData = store.get(storeKey)
    storeCheck = storeCheck || storeCheckFn
    if (storeCheck(storeData, storeCheckKey, storeSign)) {
      if (!dataCheck || (storeData && dataCheck && dataCheck(storeData) !== false)) {
        return storeData
      }
    }
  }
  return null
}

function getDataFromStoreWithoutCheck ({ useStore, storeKey, dataCheck }) {
  useStore = useStore ? store.enabled : false
  if (useStore) {
    const storeData = store.get(storeKey)
    if (!dataCheck || (storeData && dataCheck && dataCheck(storeData) !== false)) {
      return storeData
    }
  }
  return null
}

function setDataToStore ({ useStore, storeKey, data }) {
  useStore = useStore ? store.enabled : false
  if (useStore) {
    store.set(storeKey, data)
  }
}

function fallback (url, opts, cb) {
  const backup = opts.backup
  let backupWithParams
  if (backup) {
    if (typeof backup === 'string') {
      delete opts.backup
      backupWithParams = generateJsonpUrlWithParams(backup, opts.params)
      return fetchData(backupWithParams, opts, cb)
    } else if (Array.isArray(backup)) {
      if (backup.length) {
        backupWithParams = generateJsonpUrlWithParams(backup.shift(), opts.params)
        return fetchData(backupWithParams, opts, cb)
      }
    }
  }
  // no backup to use, try to get data from store
  const dataFromStoreWithoutCheck = getDataFromStoreWithoutCheck({
    useStore: opts.useStore,
    storeKey: url,
    dataCheck: opts.dataCheck
  })
  if (dataFromStoreWithoutCheck) {
    cb(null, dataFromStoreWithoutCheck)
    return true
  }
  return false
}

export function appendScriptTagToHead ({ url, charset }) {
  script = doc.createElement('script')
  script.type = 'text/javascript'
  if (charset) {
    script.charset = charset
  }
  script.src = url
  head.appendChild(script)
}

export default jsonp
<commit_msg>update:add backup url request tag<commit_after>import assign from 'object-assign'

import { serializeParams, isFunction, getUrlQueryParamByName, updateQueryStringParamByName } from './lib'
import store from './store'

const win = window

const canUsePromise = (function () {
  return 'Promise' in win &&
    typeof isFunction(Promise)
})()

const noop = function () {}

const encodeC = encodeURIComponent
const doc = document
const head = doc.head || doc.getElementsByTagName('head')[0]

const TIMEOUT_CONST = 2000

const defaultConfig = {
  timeout: TIMEOUT_CONST,
  retryTimes: 2,
  backup: null,
  params: {},
  jsonp: 'callback',
  name: null,
  cache: false,
  useStore: false,
  storeCheck: null,
  storeSign: null,
  storeCheckKey: null,
  dataCheck: null,
  charset: 'UTF-8'
}

let script
let timestamp = new Date().getTime()

function jsonp (url, opts, cb) {
  if (isFunction(url)) {
    cb = url
    opts = {}
  } else if (url && typeof url === 'object') {
    cb = opts
    opts = url || {}
    url = opts.url
  }
  if (isFunction(opts)) {
    cb = opts
    opts = {}
  }
  if (!opts) {
    opts = {}
  }
  opts = assign({}, defaultConfig, opts)
  url = url || opts.url
  cb = cb || noop
  if (!url || typeof url !== 'string') {
    cb(new Error('Param url is needed!'))
    if (!jsonp.promiseClose && canUsePromise) {
      return new Promise((resolve, reject) => {
        return reject(new Error('Param url is needed!'))
      })
    }
    return
  }
  const urlWithParams = generateJsonpUrlWithParams(url, opts.params)
  // first get data from store
  const datafromStore = getDataFromStore({
    useStore: opts.useStore,
    storeKey: urlWithParams,
    storeCheck: opts.storeCheck,
    storeCheckKey: opts.storeCheckKey,
    storeSign: opts.storeSign,
    dataCheck: opts.dataCheck
  })
  if (datafromStore) {
    cb(null, datafromStore)
    if (!jsonp.promiseClose && canUsePromise) {
      return new Promise(resolve => {
        return resolve(datafromStore)
      })
    }
    return
  }
  opts.originalUrl = urlWithParams
  if (!jsonp.promiseClose && canUsePromise) {
    return new Promise((resolve, reject) => {
      fetchData(urlWithParams, opts, (err, data) => {
        if (err) {
          cb(err)
          return reject(err)
        }
        cb(null, data)
        resolve(data)
      })
    })
  }
  fetchData(urlWithParams, opts, cb)
}

function generateJsonpUrlWithParams (url, params) {
  params = typeof params === 'string' ? params : serializeParams(params)
  url += (~url.indexOf('?') ? '&' : '?') + `${params}`
  url = url.replace('?&', '?')
  return url
}

function fetchData (url, opts, cb) {
  const originalUrl = opts.originalUrl
  const charset = opts.charset
  const funcId = opts.name || `__jsonp${timestamp++}`
  const jsonpUrlQueryParam = getUrlQueryParamByName(url, opts.jsonp)
  const gotoBackupInfo = arguments[3] || null
  if (jsonpUrlQueryParam) {
    if (jsonpUrlQueryParam === '?') {
      url = updateQueryStringParamByName(url, opts.jsonp, encodeC(funcId))
    }
  } else {
    url += (url.split('').pop() === '&' ? '' : '&') + `${opts.jsonp}=${encodeC(funcId)}`
  }
  if (!opts.cache) {
    url += `&_=${new Date().getTime()}`
  }

  const timeout = opts.timeout != null ? opts.timeout : TIMEOUT_CONST
  // when timeout, will try to retry
  const timer = setTimeout(() => {
    cleanup(funcId)
    // no retryTimes left, go to backup
    if (typeof opts.retryTimes === 'number' && opts.retryTimes > 0) {
      opts.retryTimes--
      return fetchData(originalUrl, opts, cb)
    }
    if (fallback(originalUrl, opts, cb) === false) {
      return cb(new Error('Timeout and no data return'))
    }
  }, timeout)

  function cleanup (funcId) {
    if (script.parentNode) {
      script.parentNode.removeChild(script)
    }
    win[funcId] = noop
    if (timer) {
      clearTimeout(timer)
    }
  }

  win[funcId] = function (data) {
    cleanup(funcId)
    if (gotoBackupInfo) {
      data.__$$backupCall = gotoBackupInfo
    }
    if (opts.dataCheck) {
      if (opts.dataCheck(data) !== false) {
        // write data to store
        setDataToStore({
          useStore: opts.useStore,
          storeKey: originalUrl,
          data
        })
        return cb(null, data)
      }
      if (fallback(originalUrl, opts, cb) === false) {
        cb(new Error('Data check error, and no fallback'))
      }
    } else {
      // write data to store
      setDataToStore({
        useStore: opts.useStore,
        storeKey: originalUrl,
        data
      })
      cb(null, data)
    }
  }
  appendScriptTagToHead({
    url,
    charset
  })
}

function storeCheckFn (storeData, storeCheckKey, storeSign) {
  if (storeData && storeCheckKey && storeSign) {
    return storeData[storeCheckKey] && storeData[storeCheckKey] === storeSign
  }
  return false
}

function getDataFromStore ({ useStore, storeKey, storeCheck, storeCheckKey, storeSign, dataCheck }) {
  useStore = useStore ? store.enabled : false
  if (useStore) {
    const storeData = store.get(storeKey)
    storeCheck = storeCheck || storeCheckFn
    if (storeCheck(storeData, storeCheckKey, storeSign)) {
      if (!dataCheck || (storeData && dataCheck && dataCheck(storeData) !== false)) {
        return storeData
      }
    }
  }
  return null
}

function getDataFromStoreWithoutCheck ({ useStore, storeKey, dataCheck }) {
  useStore = useStore ? store.enabled : false
  if (useStore) {
    const storeData = store.get(storeKey)
    if (!dataCheck || (storeData && dataCheck && dataCheck(storeData) !== false)) {
      return storeData
    }
  }
  return null
}

function setDataToStore ({ useStore, storeKey, data }) {
  useStore = useStore ? store.enabled : false
  if (useStore) {
    store.set(storeKey, data)
  }
}

function fallback (url, opts, cb) {
  const backup = opts.backup
  let backupWithParams
  if (backup) {
    if (typeof backup === 'string') {
      delete opts.backup
      backupWithParams = generateJsonpUrlWithParams(backup, opts.params)
      return fetchData(backupWithParams, opts, cb, {
        backup
      })
    } else if (Array.isArray(backup)) {
      if (backup.length) {
        const backupUrl = backup.shift()
        backupWithParams = generateJsonpUrlWithParams(backupUrl, opts.params)
        return fetchData(backupWithParams, opts, cb, {
          backup: backupUrl
        })
      }
    }
  }
  // no backup to use, try to get data from store
  const dataFromStoreWithoutCheck = getDataFromStoreWithoutCheck({
    useStore: opts.useStore,
    storeKey: url,
    dataCheck: opts.dataCheck
  })
  if (dataFromStoreWithoutCheck) {
    cb(null, dataFromStoreWithoutCheck)
    return true
  }
  return false
}

export function appendScriptTagToHead ({ url, charset }) {
  script = doc.createElement('script')
  script.type = 'text/javascript'
  if (charset) {
    script.charset = charset
  }
  script.src = url
  head.appendChild(script)
}

export default jsonp
<|endoftext|>


<commit_before>import React from 'react';
import { Button, Grid, Row, Col, FormGroup, FormControl, ControlLabel, ListGroup, ListGroupItem, Glyphicon, HelpBlock } from 'react-bootstrap';
import Utility from '../../../../shared/util/Utility';

import * as Lessons from '../../../../shared/reducers/Lessons';
import * as Security from '../../../../shared/reducers/Security';

export const Reducers = [Lessons, Security];

export class selectScreen extends React.Component {
  constructor(props) {
    super(props);
    this.handleSubmit = this.handleSubmit.bind(this);
    this.handleLanguageSelection = this.handleLanguageSelection.bind(this);
    this.handleStarredClick = this.handleStarredClick.bind(this);
    this.handleSpacedRepetition = this.handleSpacedRepetition.bind(this);

    this.onKeys = this.onKeys.bind(this);

  }

  componentWillMount() {
    this.props.fetchLessons(this.props.params.type)
      .catch(() => this.props.verifyUserLoggedIn());

    this.props.fetchUserStarredLessons()
      .catch(() => this.props.verifyUserLoggedIn());

    this.props.fetchaddressedQuestionsInLessons()
      .catch(() => this.props.verifyUserLoggedIn());

    if (this.props.params.type === 'kanji') {
      this.props.setQuestionLanguage('reading');
    }
  }

  componentDidMount() {
    window.addEventListener('keydown', this.onKeys);
  }

  // Triggers when we change between play types but remain in "selection" page
  componentWillReceiveProps(nextProps) {
    if (this.props.params.type !== nextProps.params.type) {
      this.props.fetchLessons(nextProps.params.type)
        .catch(() => this.props.verifyUserLoggedIn());
    }
  }

  componentWillUnmount() {
    window.removeEventListener('keydown', this.onKeys);
  }

  onKeys(event) {
    if (event.keyCode === 13) {
      this.handleSubmit(event);
    }
  }

  getPageHeader() {
    switch (this.props.params.type) {
      case 'quiz':
        return
Quiz
;
      case 'guess':
        return
Gissa ordet
;
      case 'translate':
        return
Översätt ordet
;
      case 'flashcards':
        return
Bildkort
;
      case 'kanji':
        return
Skriv Kanji
;
      case 'grammar':
        return
Böj verb
;
      default:
        throw new Error('No play type specified');
    }
  }

  getPageDescription() {
    switch (this.props.params.type) {
      case 'quiz':
        return (Sätt dina kunskaper om Japan på prov genom att välja en av 4 svarsalternativ);
      case 'guess':
        return Välj mellan 4 svarsalternativ för den korrekta översättningen.;
      case 'translate':
        return Översätt det visade ordet i fritext.;
      case 'flashcards':
        return (Träna dig själv genom att använda kort,
        med frågan på ena sidan och rätta svaret på den andra.);
      case 'kanji':
        return (Försök rita kanji-tecken med korrekta drag och i rätt ordning.);
      case 'grammar':
        return Böj det visade ordet i fritext på angiven verbform.;
      default:
        throw new Error('No play type specified');
    }
  }

  handleLanguageSelection(event) {
    switch (event.target.name) {
      case 'selectedLesson':
        this.props.setSelectedLesson(event.target.value);
        break;
      case 'questionType':
        this.props.setQuestionLanguage(event.target.value);
        break;
      case 'answerType':
        this.props.setAnswerLanguage(event.target.value);
        break;
      default:
        break;
    }
  }

  isLessonStartable() {
    if (this.props.spacedRepetition) {
      if (this.props.selectedLesson.name && this.props.addressedQuestionsInLessons) {
        return this.props.addressedQuestionsInLessons[this.props.selectedLesson.name].retention
        + this.props.addressedQuestionsInLessons[this.props.selectedLesson.name].unanswered >= 4;
      }
    }
    return true;
  }

  handleSubmit(event) {
    event.preventDefault();
    try {
      this.props.fetchLesson(this.props.params.type)
          .then(() => {
            this.props.setPageByName(`/play/${this.props.params.type}`);
          });
    } catch (err) {
      this.props.verifyUserLoggedIn();
    }
  }

  handleSpacedRepetition(event) {
    this.props.toggleSpacedRepetition();
  }

  handleStarredClick(lesson) {
    return this.props.starredLessons.map(userLesson => userLesson.lesson.name).includes(lesson.name) ?
      this.props.removeStarredLesson(lesson.name) :
      this.props.addStarredLesson(lesson.name);
  }

  render() {
    const options = this.props.lessons.map(lesson =>
      <Row key={lesson.name}>
        <Col
          xs={this.props.params.type === 'quiz' ? 12 : 10}
          md={this.props.params.type === 'quiz' ? 12 : 11}
          lg={this.props.params.type === 'quiz' ? 12 : 11}>
          <ListGroupItem
            key={lesson.name}
            onClick={() => this.props.setSelectedLesson(lesson)}
            value={lesson.name}
            header={lesson.name}
            bsStyle={lesson.name === this.props.selectedLesson.name ? 'info' : null}
          />
        </Col>
        {this.props.params.type === 'quiz' ?
          null
          :
          <Col xs={2} md={1} lg={1}>
            <Button
              bsStyle={this.props.starredLessons.map(userLesson => userLesson.lesson.name).includes(lesson.name) ? 'warning' : null}
              onClick={() => this.handleStarredClick(lesson)}
            >
              <Glyphicon glyph="star" />
            </Button>
          </Col>
        }
      </Row>);

    const answerLanguages = [];
    let questionLanguages = [];
    answerLanguages.push(<option key={'reading'} value={'reading'}>Japanska</option>);
    answerLanguages.push(<option key={'swedish'} value={'swedish'}>Svenska</option>);
    /* devcode: start */
    answerLanguages.push(<option key={'english'} value={'english'}>Engelska</option>);
    /* devcode: end */

    if (this.props.params.type === 'kanji') {
      questionLanguages = answerLanguages.shift();
    } else {
      questionLanguages = answerLanguages;
    }

    let languageSelection;
    if (this.props.params.type === 'quiz' || this.props.params.type === 'grammar') {
      languageSelection =
;
    } else {
      languageSelection = (
        <FormGroup>
          <Row>
            <Col xs={12} sm={6}>
              <HelpBlock>Frågespråk</HelpBlock>
              <FormControl
                componentClass="select"
                name="questionType"
                id="questionLanguageSelection"
                onChange={this.handleLanguageSelection}
                value={this.props.questionType}
                disabled={this.props.params.type === 'kanji'}
              >
                {questionLanguages}
              </FormControl>
            </Col>
            <Col xs={12} sm={6}>
              <HelpBlock>Svarspråk</HelpBlock>
              <FormControl
                componentClass="select"
                name="answerType"
                id="answerLanguageSelection"
                onChange={this.handleLanguageSelection}
                value={this.props.answerType}
              >
                {answerLanguages}
              </FormControl>

            </Col>
          </Row>
        </FormGroup>);
    }
    return (
      <Grid className="text-center">
        <Col xs={11} lg={8} lgOffset={2}>
          <form href="#" onSubmit={this.handleSubmit}>
            <ControlLabel>{this.getPageHeader()}</ControlLabel>

            <FormGroup>
              <ControlLabel>{this.getPageDescription()}</ControlLabel>
            </FormGroup>

            <FormGroup>
              <HelpBlock>Välj ordsamlingar i listan nedan</HelpBlock>
              <ListGroup>
                {options}
              </ListGroup>
              {languageSelection}
            </FormGroup>
            <FormGroup>
              <FormControl
                type="hidden"
                onKeyPress={this.handleKeyPress}
              />
              <Button type="submit" bsStyle="primary" disabled={!this.isLessonStartable()}> Starta </Button>
              <ControlLabel> Spaced Repetition: <input name="spacedRepetition" type="checkbox" defaultChecked={this.props.spacedRepetition} onClick={this.handleSpacedRepetition} /></ControlLabel>
            </FormGroup>

          </form>
        </Col>
      </Grid>
    );
  }
}

selectScreen.defaultProps = Utility.reduxEnabledDefaultProps({

}, Reducers);

selectScreen.propTypes = Utility.reduxEnabledPropTypes({

}, Reducers);

export default Utility.superConnect(this, Reducers)(selectScreen);
<commit_msg>now button changes text<commit_after>import React from 'react';
import { Button, Grid, Row, Col, FormGroup, FormControl, ControlLabel, ListGroup, ListGroupItem, Glyphicon, HelpBlock } from 'react-bootstrap';
import Utility from '../../../../shared/util/Utility';

import * as Lessons from '../../../../shared/reducers/Lessons';
import * as Security from '../../../../shared/reducers/Security';

export const Reducers = [Lessons, Security];

export class selectScreen extends React.Component {
  constructor(props) {
    super(props);
    this.handleSubmit = this.handleSubmit.bind(this);
    this.handleLanguageSelection = this.handleLanguageSelection.bind(this);
    this.handleStarredClick = this.handleStarredClick.bind(this);
    this.handleSpacedRepetition = this.handleSpacedRepetition.bind(this);

    this.onKeys = this.onKeys.bind(this);

  }

  componentWillMount() {
    this.props.Lessons(this.props.params.type)
      .catch(() => this.props.verifyUserLoggedIn());

    this.props.UserStarredLessons()
      .catch(() => this.props.verifyUserLoggedIn());

    this.props.addressedQuestionsInLessons()
      .catch(() => this.props.verifyUserLoggedIn());

    if (this.props.params.type === 'kanji') {
      this.props.setQuestionLanguage('reading');
    }
  }

  componentDidMount() {
    window.addEventListener('keydown', this.onKeys);
  }

  // Triggers when we change between play types but remain in "selection" page
  componentWillReceiveProps(nextProps) {
    if (this.props.params.type !== nextProps.params.type) {
      this.props.Lessons(nextProps.params.type)
        .catch(() => this.props.verifyUserLoggedIn());
    }
  }

  componentWillUnmount() {
    window.removeEventListener('keydown', this.onKeys);
  }

  onKeys(event) {
    if (event.keyCode === 13) {
      this.handleSubmit(event);
    }
  }

  getPageHeader() {
    switch (this.props.params.type) {
      case 'quiz':
        return
Quiz
;
      case 'guess':
        return
Gissa ordet
;
      case 'translate':
        return
Översätt ordet
;
      case 'flashcards':
        return
Bildkort
;
      case 'kanji':
        return
Skriv Kanji
;
      case 'grammar':
        return
Böj verb
;
      default:
        throw new Error('No play type specified');
    }
  }

  getPageDescription() {
    switch (this.props.params.type) {
      case 'quiz':
        return (Sätt dina kunskaper om Japan på prov genom att välja en av 4 svarsalternativ);
      case 'guess':
        return Välj mellan 4 svarsalternativ för den korrekta översättningen.;
      case 'translate':
        return Översätt det visade ordet i fritext.;
      case 'flashcards':
        return (Träna dig själv genom att använda kort,
        med frågan på ena sidan och rätta svaret på den andra.);
      case 'kanji':
        return (Försök rita kanji-tecken med korrekta drag och i rätt ordning.);
      case 'grammar':
        return Böj det visade ordet i fritext på angiven verbform.;
      default:
        throw new Error('No play type specified');
    }
  }

  handleLanguageSelection(event) {
    switch (event.target.name) {
      case 'selectedLesson':
        this.props.setSelectedLesson(event.target.value);
        break;
      case 'questionType':
        this.props.setQuestionLanguage(event.target.value);
        break;
      case 'answerType':
        this.props.setAnswerLanguage(event.target.value);
        break;
      default:
        break;
    }
  }

  isLessonStartable() {
    if (this.props.spacedRepetition) {
      if (this.props.selectedLesson.name && this.props.addressedQuestionsInLessons) {
        return this.props.addressedQuestionsInLessons[this.props.selectedLesson.name].retention
        + this.props.addressedQuestionsInLessons[this.props.selectedLesson.name].unanswered >= 4;
      }
    }
    return true;
  }

  getButtonText() {
    if (this.isLessonStartable()) {
      return 'Starta';
    }
    return 'Ej startbar';
  }

  handleSubmit(event) {
    event.preventDefault();
    try {
      this.props.Lesson(this.props.params.type)
          .then(() => {
            this.props.setPageByName(`/play/${this.props.params.type}`);
          });
    } catch (err) {
      this.props.verifyUserLoggedIn();
    }
  }

  handleSpacedRepetition(event) {
    this.props.toggleSpacedRepetition();
  }

  handleStarredClick(lesson) {
    return this.props.starredLessons.map(userLesson => userLesson.lesson.name).includes(lesson.name) ?
      this.props.removeStarredLesson(lesson.name) :
      this.props.addStarredLesson(lesson.name);
  }

  render() {
    const options = this.props.lessons.map(lesson =>
      <Row key={lesson.name}>
        <Col
          xs={this.props.params.type === 'quiz' ? 12 : 10}
          md={this.props.params.type === 'quiz' ? 12 : 11}
          lg={this.props.params.type === 'quiz' ? 12 : 11}>
          <ListGroupItem
            key={lesson.name}
            onClick={() => this.props.setSelectedLesson(lesson)}
            value={lesson.name}
            header={lesson.name}
            bsStyle={lesson.name === this.props.selectedLesson.name ? 'info' : null}
          />
        </Col>
        {this.props.params.type === 'quiz' ?
          null
          :
          <Col xs={2} md={1} lg={1}>
            <Button
              bsStyle={this.props.starredLessons.map(userLesson => userLesson.lesson.name).includes(lesson.name) ? 'warning' : null}
              onClick={() => this.handleStarredClick(lesson)}
            >
              <Glyphicon glyph="star" />
            </Button>
          </Col>
        }
      </Row>);

    const answerLanguages = [];
    let questionLanguages = [];
    answerLanguages.push(<option key={'reading'} value={'reading'}>Japanska</option>);
    answerLanguages.push(<option key={'swedish'} value={'swedish'}>Svenska</option>);
    /* devcode: start */
    answerLanguages.push(<option key={'english'} value={'english'}>Engelska</option>);
    /* devcode: end */

    if (this.props.params.type === 'kanji') {
      questionLanguages = answerLanguages.shift();
    } else {
      questionLanguages = answerLanguages;
    }

    let languageSelection;
    if (this.props.params.type === 'quiz' || this.props.params.type === 'grammar') {
      languageSelection =
;
    } else {
      languageSelection = (
        <FormGroup>
          <Row>
            <Col xs={12} sm={6}>
              <HelpBlock>Frågespråk</HelpBlock>
              <FormControl
                componentClass="select"
                name="questionType"
                id="questionLanguageSelection"
                onChange={this.handleLanguageSelection}
                value={this.props.questionType}
                disabled={this.props.params.type === 'kanji'}
              >
                {questionLanguages}
              </FormControl>
            </Col>
            <Col xs={12} sm={6}>
              <HelpBlock>Svarspråk</HelpBlock>
              <FormControl
                componentClass="select"
                name="answerType"
                id="answerLanguageSelection"
                onChange={this.handleLanguageSelection}
                value={this.props.answerType}
              >
                {answerLanguages}
              </FormControl>

            </Col>
          </Row>
        </FormGroup>);
    }
    return (
      <Grid className="text-center">
        <Col xs={11} lg={8} lgOffset={2}>
          <form href="#" onSubmit={this.handleSubmit}>
            <ControlLabel>{this.getPageHeader()}</ControlLabel>

            <FormGroup>
              <ControlLabel>{this.getPageDescription()}</ControlLabel>
            </FormGroup>

            <FormGroup>
              <HelpBlock>Välj ordsamlingar i listan nedan</HelpBlock>
              <ListGroup>
                {options}
              </ListGroup>
              {languageSelection}
            </FormGroup>
            <FormGroup>
              <FormControl
                type="hidden"
                onKeyPress={this.handleKeyPress}
              />
              <Button type="submit" bsStyle="primary" disabled={!this.isLessonStartable()}>{this.getButtonText()}</Button>
              <ControlLabel> Spaced Repetition: <input name="spacedRepetition" type="checkbox" defaultChecked={this.props.spacedRepetition} onClick={this.handleSpacedRepetition} /></ControlLabel>
            </FormGroup>

          </form>
        </Col>
      </Grid>
    );
  }
}

selectScreen.defaultProps = Utility.reduxEnabledDefaultProps({

}, Reducers);

selectScreen.propTypes = Utility.reduxEnabledPropTypes({

}, Reducers);

export default Utility.superConnect(this, Reducers)(selectScreen);
<|endoftext|>


<commit_before>(function () {
    angular
        .module('dpMap')
        .directive('dpMap', dpMapDirective);

    dpMapDirective.$inject = [
        'L',
        'mapConfig',
        'layers',
        'highlight',
        'panning',
        'zoom',
        'variableWidth',
        'searchByClick'
    ];

    function dpMapDirective (L, mapConfig, layers, highlight, panning, zoom, variableWidth, searchByClick) {
        return {
            restrict: 'E',
            scope: {
                mapState: '=',
                markers: '='
            },
            templateUrl: 'modules/map/components/map/map.html',
            link: linkFunction
        };

        function linkFunction (scope, element) {
            var leafletMap,
                container,
                options;

            container = element[0].querySelector('.js-leaflet-map');
            options = angular.merge(mapConfig.MAP_OPTIONS, {
                center: scope.mapState.viewCenter,
                zoom: scope.mapState.zoom
            });

            leafletMap = L.map(container, options);

            panning.initialize(leafletMap);
            zoom.initialize(leafletMap);
            variableWidth.initialize(container, leafletMap);
            searchByClick.initialize(leafletMap);

            scope.$watch('mapState.viewCenter', function (viewCenter) {
                panning.panTo(leafletMap, viewCenter);
            });

            scope.$watch('mapState.zoom', function (zoomLevel) {
                zoom.setZoom(leafletMap, zoomLevel);
            });

            scope.$watch('mapState.baseLayer', function (baseLayer) {
                layers.setBaseLayer(leafletMap, baseLayer);
            });

            scope.$watch('mapState.overlays', function (newOverlays, oldOverlays) {
                getRemovedOverlays(newOverlays, oldOverlays).forEach(function (overlay) {
                    layers.removeOverlay(leafletMap, overlay);
                });
                console.log(getAddedOverlays(newOverlays, oldOverlays));
                getAddedOverlays(newOverlays, oldOverlays).forEach(function (overlay) {
                    layers.addOverlay(leafletMap, overlay);
                });
            });

            scope.$watch('markers', function (newCollection, oldCollection) {
                if (angular.equals(newCollection, oldCollection)) {
                    //Initialisation
                    newCollection.forEach(function (item) {
                        highlight.add(leafletMap, item);
                    });
                } else {
                    //Change detected
                    getRemovedGeojson(newCollection, oldCollection).forEach(function (item) {
                        highlight.remove(leafletMap, item);
                    });

                    getAddedGeojson(newCollection, oldCollection).forEach(function (item) {
                        highlight.add(leafletMap, item);
                    });
                }
            }, true);
        }

        function getAddedOverlays (newOverlays, oldOverlays) {
            if (newOverlays === oldOverlays) {
                //scope.$watch is triggered on initialization with the new value equal to the old value
                return Object.keys(newOverlays);
            } else {
                var keys = [];
                for (var key in newOverlays) {
                    if (newOverlays.hasOwnProperty(key) && !(key in oldOverlays)) {
                        keys.push(key);
                    }
                }
                return keys;
            }
        }

        function getRemovedOverlays (newOverlays, oldOverlays) {
            var keys = [];
            for (var key in oldOverlays) {
                if (oldOverlays.hasOwnProperty(key) && !(key in newOverlays)) {
                    keys.push(key);
                }
            }
            return keys;
        }

        function getAddedGeojson (newCollection, oldCollection) {
            return newCollection.filter(function (newItem) {
                var hasBeenAdded,
                    hasChanged,
                    linkedOldItems;

                linkedOldItems = oldCollection.filter(function (oldItem) {
                    return oldItem.id === newItem.id;
                });

                hasBeenAdded = linkedOldItems.length === 0;
                hasChanged = !angular.equals(linkedOldItems[0], newItem);

                return hasBeenAdded || hasChanged;
            });
        }

        function getRemovedGeojson (newCollection, oldCollection) {
            return oldCollection.filter(function (oldItem) {
                var hasBeenRemoved,
                    hasChanged,
                    linkedNewItems;

                linkedNewItems = newCollection.filter(function (newItem) {
                    return newItem.id === oldItem.id;
                });

                hasBeenRemoved = linkedNewItems.length === 0;
                hasChanged = !angular.equals(linkedNewItems[0], oldItem);

                return hasBeenRemoved || hasChanged;
            });
        }
    }
})();
<commit_msg>Removing debug logging<commit_after>(function () {
    angular
        .module('dpMap')
        .directive('dpMap', dpMapDirective);

    dpMapDirective.$inject = [
        'L',
        'mapConfig',
        'layers',
        'highlight',
        'panning',
        'zoom',
        'variableWidth',
        'searchByClick'
    ];

    function dpMapDirective (L, mapConfig, layers, highlight, panning, zoom, variableWidth, searchByClick) {
        return {
            restrict: 'E',
            scope: {
                mapState: '=',
                markers: '='
            },
            templateUrl: 'modules/map/components/map/map.html',
            link: linkFunction
        };

        function linkFunction (scope, element) {
            var leafletMap,
                container,
                options;

            container = element[0].querySelector('.js-leaflet-map');
            options = angular.merge(mapConfig.MAP_OPTIONS, {
                center: scope.mapState.viewCenter,
                zoom: scope.mapState.zoom
            });

            leafletMap = L.map(container, options);

            panning.initialize(leafletMap);
            zoom.initialize(leafletMap);
            variableWidth.initialize(container, leafletMap);
            searchByClick.initialize(leafletMap);

            scope.$watch('mapState.viewCenter', function (viewCenter) {
                panning.panTo(leafletMap, viewCenter);
            });

            scope.$watch('mapState.zoom', function (zoomLevel) {
                zoom.setZoom(leafletMap, zoomLevel);
            });

            scope.$watch('mapState.baseLayer', function (baseLayer) {
                layers.setBaseLayer(leafletMap, baseLayer);
            });

            scope.$watch('mapState.overlays', function (newOverlays, oldOverlays) {
                getRemovedOverlays(newOverlays, oldOverlays).forEach(function (overlay) {
                    layers.removeOverlay(leafletMap, overlay);
                });
                getAddedOverlays(newOverlays, oldOverlays).forEach(function (overlay) {
                    layers.addOverlay(leafletMap, overlay);
                });
            });

            scope.$watch('markers', function (newCollection, oldCollection) {
                if (angular.equals(newCollection, oldCollection)) {
                    //Initialisation
                    newCollection.forEach(function (item) {
                        highlight.add(leafletMap, item);
                    });
                } else {
                    //Change detected
                    getRemovedGeojson(newCollection, oldCollection).forEach(function (item) {
                        highlight.remove(leafletMap, item);
                    });

                    getAddedGeojson(newCollection, oldCollection).forEach(function (item) {
                        highlight.add(leafletMap, item);
                    });
                }
            }, true);
        }

        function getAddedOverlays (newOverlays, oldOverlays) {
            if (newOverlays === oldOverlays) {
                //scope.$watch is triggered on initialization with the new value equal to the old value
                return Object.keys(newOverlays);
            } else {
                var keys = [];
                for (var key in newOverlays) {
                    if (newOverlays.hasOwnProperty(key) && !(key in oldOverlays)) {
                        keys.push(key);
                    }
                }
                return keys;
            }
        }

        function getRemovedOverlays (newOverlays, oldOverlays) {
            var keys = [];
            for (var key in oldOverlays) {
                if (oldOverlays.hasOwnProperty(key) && !(key in newOverlays)) {
                    keys.push(key);
                }
            }
            return keys;
        }

        function getAddedGeojson (newCollection, oldCollection) {
            return newCollection.filter(function (newItem) {
                var hasBeenAdded,
                    hasChanged,
                    linkedOldItems;

                linkedOldItems = oldCollection.filter(function (oldItem) {
                    return oldItem.id === newItem.id;
                });

                hasBeenAdded = linkedOldItems.length === 0;
                hasChanged = !angular.equals(linkedOldItems[0], newItem);

                return hasBeenAdded || hasChanged;
            });
        }

        function getRemovedGeojson (newCollection, oldCollection) {
            return oldCollection.filter(function (oldItem) {
                var hasBeenRemoved,
                    hasChanged,
                    linkedNewItems;

                linkedNewItems = newCollection.filter(function (newItem) {
                    return newItem.id === oldItem.id;
                });

                hasBeenRemoved = linkedNewItems.length === 0;
                hasChanged = !angular.equals(linkedNewItems[0], oldItem);

                return hasBeenRemoved || hasChanged;
            });
        }
    }
})();
<|endoftext|>