<commit_before>import React, { Component } from 'react';
import Relay from 'react-relay/classic';
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
import Favicon from 'react-favicon';
import isEqual from 'lodash.isequal';
import styled from 'styled-components';
import Intercom from 'react-intercom';
import { MuiPickersUtilsProvider } from '@material-ui/pickers';
import MomentUtils from '@date-io/moment';
import config from 'config'; // eslint-disable-line require-path-exists/exists
import { Header } from './Header';
import LoginContainer from './LoginContainer';
import InviteNewAccount from './InviteNewAccount';
import BrowserSupport from './BrowserSupport';
import CheckContext from '../CheckContext';
import DrawerNavigation from './DrawerNavigation';
import { FlashMessageContext, FlashMessage, withSetFlashMessage } from './FlashMessage';
import UserTos from './UserTos';
import { withClientSessionId } from '../ClientSessionId';
import { stringHelper } from '../customHelpers';
import { bemClass } from '../helpers';
import { FormattedGlobalMessage } from './MappedMessage';
import MeRoute from '../relay/MeRoute';

const Wrapper = styled.div`
  display: flex;
`;

const Main = styled.main`
  flex: 1 1 auto;
`;

const StyledContent = styled.div`
  background-color: white;
`;

function buildLoginContainerMessage(flashMessage, error, childRoute, queryString) {
  let message = null;
  if (error) {
    message = flashMessage;

    // TODO Don't parse error messages because they may be l10n'd - use error codes instead.
    if (
      error &&
      message &&
      message.match && // Test `message` is a String, not a <FormattedMessage>
      message.match(/\{ \[Error: Request has been terminated/)
    ) {
      message = (
        <FormattedMessage
          id="home.somethingWrong"
          defaultMessage="Sorry, an error occurred. Please refresh your browser and contact {supportEmail} if the condition persists."
          values={{ supportEmail: stringHelper('SUPPORT_EMAIL') }}
        />
      );
    }
  }

  if ('invitation_response' in queryString) {
    if (queryString.invitation_response === 'success') {
      if (queryString.msg === 'yes') {
        message = (
          <FormattedGlobalMessage messageKey="appNameHuman">
            {appName => (
              <FormattedMessage
                id="home.successInvitation"
                defaultMessage="Welcome to {appName}. Please login with the password that you received in the welcome email."
                values={{ appName }}
              />
            )}
          </FormattedGlobalMessage>
        );
      }
    } else {
      const values = { supportEmail: stringHelper('SUPPORT_EMAIL') };
      switch (queryString.invitation_response) {
      case 'invalidTeamInvitation': return (
        <FormattedMessage
          id="home.invalidTeamInvitation"
          defaultMessage="Sorry, the workspace to which you were invited was not found. Please contact {supportEmail} if you think this is an error."
          values={values}
        />
      );
      case 'invalidNoInvitation': return (
        <FormattedMessage
          id="home.invalidNoInvitation"
          defaultMessage="Sorry, the invitation you received was not found. Please contact {supportEmail} if you think this is an error."
          values={values}
        />
      );
      case 'invalidExpiredInvitation': return (
        <FormattedMessage
          id="home.invalidExpiredInvitation"
          defaultMessage="Sorry, the invitation you received was expired. Please contact {supportEmail} if you think this is an error."
          values={values}
        />
      );
      default: return (
        <FormattedMessage
          id="home.invalidInvitation"
          defaultMessage="Sorry, an error occurred while processing your invitation. Please contact {supportEmail}."
          values={values}
        />
      );
      }
    }
  }
  return message;
}

class HomeComponent extends Component {
  static routeSlug(children) {
    if (!(children && children.props.route)) {
      return null;
    }
    if (/\/trends\/cluster\/:clusterId/.test(children.props.route.path)) {
      return 'trend-item';
    }
    if (/\/media\/:mediaId/.test(children.props.route.path)) {
      return 'media';
    }
    if (/\/source\/:sourceId/.test(children.props.route.path)) {
      return 'source';
    }
    if (/^:team$/.test(children.props.route.path)) {
      return 'team';
    }
    if (/^check\/me\(\/:tab\)/.test(children.props.route.path)) {
      return 'me';
    }
    if (/^:team\/project\/:projectId\(\/:query\)/.test(children.props.route.path)) {
      return 'project';
    }
    return null;
  }

  constructor(props) {
    super(props);

    this.state = {
      token: null,
      error: false,
      sessionStarted: false,
      path: window.location.pathname,
    };
  }

  componentWillMount() {
    this.setContext();
  }

  shouldComponentUpdate(nextProps, nextState) {
    return !isEqual(this.state, nextState) ||
    !isEqual(this.props, nextProps);
  }

  componentWillUpdate() {
    this.setContext();
  }

  setContext() {
    const { clientSessionId, setFlashMessage } = this.props;
    const context = new CheckContext(this);
    if (!this.state.token && !this.state.error) {
      context.startSession(this.props.user, clientSessionId, setFlashMessage);
    }
    context.setContext();
    context.startNetwork(this.state.token, clientSessionId, setFlashMessage);
  }

  getContext() {
    return new CheckContext(this).getContextStore();
  }

  loginCallback() {
    if (
      this.state.path === '/check/user/password-change' ||
      /^\/check\/user\/confirm/.test(this.state.path)
    ) {
      window.location.assign('/');
    } else {
      window.location.assign(this.state.path);
    }
    this.setState({ error: false, path: null });
  }

  render() {
    if (!this.state.sessionStarted) {
      return null;
    }

    const { children, location } = this.props;
    const routeSlug = HomeComponent.routeSlug(children);

    const routeIsPublic = children && children.props.route.public;

    if (!routeIsPublic && !this.state.token) {
      if (this.state.error) {
        return (
          <FlashMessageContext.Consumer>
            {flashMessage => (
              <LoginContainer
                loginCallback={this.loginCallback.bind(this)}
                message={buildLoginContainerMessage(
                  flashMessage,
                  this.state.error,
                  children.props.route,
                  location.query,
                )}
              />
            )}
          </FlashMessageContext.Consumer>
        );
      }
      return null;
    }

    const user = this.props.user || {};
    const loggedIn = !!this.state.token;
    const teamSlugFromUrl = window.location.pathname.match(/^\/([^/]+)/);
    const userTeamSlug = ((user.current_team && user.current_team.slug) ?
      user.current_team.slug : null);
    const teamSlug = (teamSlugFromUrl && teamSlugFromUrl[1] !== 'check' ? teamSlugFromUrl[1] : null) || userTeamSlug;
    const inTeamContext = Boolean(teamSlug);

    const currentUserIsMember = (() => {
      if (inTeamContext && loggedIn) {
        if (user.is_admin) {
          return true;
        }
        const teams = JSON.parse(user.user_teams);
        const team = teams[teamSlug] || {};
        return team.status === 'member';
      }
      return false;
    })();

    if (loggedIn && !user.completed_signup) {
      return (
        <InviteNewAccount teamSlug={teamSlug} />
      );
    }

    const isMediaPage = /\/media\/[0-9]+/.test(window.location.pathname);
    const isTrendsPage = /\/trends\//.test(window.location.pathname);

    let userTiplines = '';
    if (user && user.current_team && user.current_team.team_bot_installation && user.current_team.team_bot_installation.smooch_enabled_integrations) {
      userTiplines = Object.keys(user.current_team.team_bot_installation.smooch_enabled_integrations).join(', ');
    }

    return (
      <React.Fragment>
        <MuiPickersUtilsProvider utils={MomentUtils}>
          {config.intercomAppId && user.dbid && window.parent === window ?
            <Intercom
              appID={config.intercomAppId}
              user_id={user.dbid}
              email={user.email}
              name={user.name}
              check_workspace={teamSlug}
              tipline={userTiplines}
              data={(user && user.current_team) ? user.current_team.get_data_report_url : ''}
            /> : null
          }
          <Favicon url={`/images/logo/${config.appName}.ico`} animated={false} />
          <BrowserSupport />
          <UserTos user={user} />
          <Wrapper className={bemClass('home', routeSlug, `--${routeSlug}`)}>
            {!isMediaPage && !isTrendsPage && loggedIn ? (
              <DrawerNavigation
                loggedIn={loggedIn}
                teamSlug={teamSlug}
                inTeamContext={inTeamContext}
                currentUserIsMember={currentUserIsMember}
                {...this.props}
              />
            ) : null}
            <Main>
              <Header
                loggedIn={loggedIn}
                pageType={routeSlug}
                inTeamContext={inTeamContext}
                currentUserIsMember={currentUserIsMember}
                {...this.props}
              />
              <FlashMessage />
              <StyledContent className="content-wrapper">
                {children}
              </StyledContent>
            </Main>
          </Wrapper>
        </MuiPickersUtilsProvider>
      </React.Fragment>
    );
  }
}

HomeComponent.propTypes = {
  // https://github.com/yannickcr/eslint-plugin-react/issues/1389
  // eslint-disable-next-line react/no-typos
  clientSessionId: PropTypes.string.isRequired,
  setFlashMessage: PropTypes.func.isRequired,
};

HomeComponent.contextTypes = {
  store: PropTypes.object,
};

const ConnectedHomeComponent = withSetFlashMessage(withClientSessionId(HomeComponent));

const HomeContainer = Relay.createContainer(ConnectedHomeComponent, {
  fragments: {
    user: () => Relay.QL`
      fragment on User {
        id
        dbid
        name
        token
        email
        is_admin
        accepted_terms
        last_accepted_terms_at
        completed_signup
        name
        login
        permissions
        profile_image
        settings
        source_id
        team_ids
        user_teams
        current_project {
          dbid
          id
          title
          team {
            id
            dbid
            avatar
            name
            slug
          }
        }
        current_team {
          id
          dbid
          avatar
          name
          slug
          get_data_report_url
          team_bot_installation(bot_identifier: "smooch") {
            smooch_enabled_integrations
          }
          projects(first: 1) {
            edges {
              node {
                id
                dbid
                title
                team {
                  id
                  dbid
                  avatar
                  name
                  slug
                }
              }
            }
          }
        }
      }
    `,
  },
});

// eslint-disable-next-line react/no-multi-comp
class Home extends Component {
  componentWillMount() {
    const context = new CheckContext(this);
    const { clientSessionId, setFlashMessage } = this.props;
    context.startNetwork(null, clientSessionId, setFlashMessage);
  }

  // eslint-disable-next-line class-methods-use-this
  render() {
    const route = new MeRoute();

    return (
      <Relay.RootContainer
        Component={HomeContainer}
        route={route}
        renderFetched={data => (
          <HomeContainer
            location={this.props.location}
            params={this.props.params}
            {...data}
          >
            {this.props.children}
          </HomeContainer>
        )}
      />
    );
  }
}

Home.propTypes = {
  clientSessionId: PropTypes.string.isRequired,
  setFlashMessage: PropTypes.func.isRequired,
};

Home.contextTypes = {
  store: PropTypes.object,
};

export default withSetFlashMessage(withClientSessionId(Home));
<commit_msg>CHECK-1420: hide DrawerNavigation for cluster items<commit_after>import React, { Component } from 'react';
import Relay from 'react-relay/classic';
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
import Favicon from 'react-favicon';
import isEqual from 'lodash.isequal';
import styled from 'styled-components';
import Intercom from 'react-intercom';
import { MuiPickersUtilsProvider } from '@material-ui/pickers';
import MomentUtils from '@date-io/moment';
import config from 'config'; // eslint-disable-line require-path-exists/exists
import { Header } from './Header';
import LoginContainer from './LoginContainer';
import InviteNewAccount from './InviteNewAccount';
import BrowserSupport from './BrowserSupport';
import CheckContext from '../CheckContext';
import DrawerNavigation from './DrawerNavigation';
import { FlashMessageContext, FlashMessage, withSetFlashMessage } from './FlashMessage';
import UserTos from './UserTos';
import { withClientSessionId } from '../ClientSessionId';
import { stringHelper } from '../customHelpers';
import { bemClass } from '../helpers';
import { FormattedGlobalMessage } from './MappedMessage';
import MeRoute from '../relay/MeRoute';

const Wrapper = styled.div`
  display: flex;
`;

const Main = styled.main`
  flex: 1 1 auto;
`;

const StyledContent = styled.div`
  background-color: white;
`;

function buildLoginContainerMessage(flashMessage, error, childRoute, queryString) {
  let message = null;
  if (error) {
    message = flashMessage;

    // TODO Don't parse error messages because they may be l10n'd - use error codes instead.
    if (
      error &&
      message &&
      message.match && // Test `message` is a String, not a <FormattedMessage>
      message.match(/\{ \[Error: Request has been terminated/)
    ) {
      message = (
        <FormattedMessage
          id="home.somethingWrong"
          defaultMessage="Sorry, an error occurred. Please refresh your browser and contact {supportEmail} if the condition persists."
          values={{ supportEmail: stringHelper('SUPPORT_EMAIL') }}
        />
      );
    }
  }

  if ('invitation_response' in queryString) {
    if (queryString.invitation_response === 'success') {
      if (queryString.msg === 'yes') {
        message = (
          <FormattedGlobalMessage messageKey="appNameHuman">
            {appName => (
              <FormattedMessage
                id="home.successInvitation"
                defaultMessage="Welcome to {appName}. Please login with the password that you received in the welcome email."
                values={{ appName }}
              />
            )}
          </FormattedGlobalMessage>
        );
      }
    } else {
      const values = { supportEmail: stringHelper('SUPPORT_EMAIL') };
      switch (queryString.invitation_response) {
      case 'invalidTeamInvitation': return (
        <FormattedMessage
          id="home.invalidTeamInvitation"
          defaultMessage="Sorry, the workspace to which you were invited was not found. Please contact {supportEmail} if you think this is an error."
          values={values}
        />
      );
      case 'invalidNoInvitation': return (
        <FormattedMessage
          id="home.invalidNoInvitation"
          defaultMessage="Sorry, the invitation you received was not found. Please contact {supportEmail} if you think this is an error."
          values={values}
        />
      );
      case 'invalidExpiredInvitation': return (
        <FormattedMessage
          id="home.invalidExpiredInvitation"
          defaultMessage="Sorry, the invitation you received was expired. Please contact {supportEmail} if you think this is an error."
          values={values}
        />
      );
      default: return (
        <FormattedMessage
          id="home.invalidInvitation"
          defaultMessage="Sorry, an error occurred while processing your invitation. Please contact {supportEmail}."
          values={values}
        />
      );
      }
    }
  }
  return message;
}

class HomeComponent extends Component {
  static routeSlug(children) {
    if (!(children && children.props.route)) {
      return null;
    }
    if (/\/trends\/cluster\/:clusterId/.test(children.props.route.path)) {
      return 'trend-item';
    }
    if (/\/media\/:mediaId/.test(children.props.route.path)) {
      return 'media';
    }
    if (/\/source\/:sourceId/.test(children.props.route.path)) {
      return 'source';
    }
    if (/^:team$/.test(children.props.route.path)) {
      return 'team';
    }
    if (/^check\/me\(\/:tab\)/.test(children.props.route.path)) {
      return 'me';
    }
    if (/^:team\/project\/:projectId\(\/:query\)/.test(children.props.route.path)) {
      return 'project';
    }
    return null;
  }

  constructor(props) {
    super(props);

    this.state = {
      token: null,
      error: false,
      sessionStarted: false,
      path: window.location.pathname,
    };
  }

  componentWillMount() {
    this.setContext();
  }

  shouldComponentUpdate(nextProps, nextState) {
    return !isEqual(this.state, nextState) ||
    !isEqual(this.props, nextProps);
  }

  componentWillUpdate() {
    this.setContext();
  }

  setContext() {
    const { clientSessionId, setFlashMessage } = this.props;
    const context = new CheckContext(this);
    if (!this.state.token && !this.state.error) {
      context.startSession(this.props.user, clientSessionId, setFlashMessage);
    }
    context.setContext();
    context.startNetwork(this.state.token, clientSessionId, setFlashMessage);
  }

  getContext() {
    return new CheckContext(this).getContextStore();
  }

  loginCallback() {
    if (
      this.state.path === '/check/user/password-change' ||
      /^\/check\/user\/confirm/.test(this.state.path)
    ) {
      window.location.assign('/');
    } else {
      window.location.assign(this.state.path);
    }
    this.setState({ error: false, path: null });
  }

  render() {
    if (!this.state.sessionStarted) {
      return null;
    }

    const { children, location } = this.props;
    const routeSlug = HomeComponent.routeSlug(children);

    const routeIsPublic = children && children.props.route.public;

    if (!routeIsPublic && !this.state.token) {
      if (this.state.error) {
        return (
          <FlashMessageContext.Consumer>
            {flashMessage => (
              <LoginContainer
                loginCallback={this.loginCallback.bind(this)}
                message={buildLoginContainerMessage(
                  flashMessage,
                  this.state.error,
                  children.props.route,
                  location.query,
                )}
              />
            )}
          </FlashMessageContext.Consumer>
        );
      }
      return null;
    }

    const user = this.props.user || {};
    const loggedIn = !!this.state.token;
    const teamSlugFromUrl = window.location.pathname.match(/^\/([^/]+)/);
    const userTeamSlug = ((user.current_team && user.current_team.slug) ?
      user.current_team.slug : null);
    const teamSlug = (teamSlugFromUrl && teamSlugFromUrl[1] !== 'check' ? teamSlugFromUrl[1] : null) || userTeamSlug;
    const inTeamContext = Boolean(teamSlug);

    const currentUserIsMember = (() => {
      if (inTeamContext && loggedIn) {
        if (user.is_admin) {
          return true;
        }
        const teams = JSON.parse(user.user_teams);
        const team = teams[teamSlug] || {};
        return team.status === 'member';
      }
      return false;
    })();

    if (loggedIn && !user.completed_signup) {
      return (
        <InviteNewAccount teamSlug={teamSlug} />
      );
    }

    const isMediaPage = /\/media\/[0-9]+/.test(window.location.pathname);
    const isTrendsPage = /\/trends\/cluster\/[0-9]+/.test(window.location.pathname);

    let userTiplines = '';
    if (user && user.current_team && user.current_team.team_bot_installation && user.current_team.team_bot_installation.smooch_enabled_integrations) {
      userTiplines = Object.keys(user.current_team.team_bot_installation.smooch_enabled_integrations).join(', ');
    }

    return (
      <React.Fragment>
        <MuiPickersUtilsProvider utils={MomentUtils}>
          {config.intercomAppId && user.dbid && window.parent === window ?
            <Intercom
              appID={config.intercomAppId}
              user_id={user.dbid}
              email={user.email}
              name={user.name}
              check_workspace={teamSlug}
              tipline={userTiplines}
              data={(user && user.current_team) ? user.current_team.get_data_report_url : ''}
            /> : null
          }
          <Favicon url={`/images/logo/${config.appName}.ico`} animated={false} />
          <BrowserSupport />
          <UserTos user={user} />
          <Wrapper className={bemClass('home', routeSlug, `--${routeSlug}`)}>
            {!isMediaPage && !isTrendsPage && loggedIn ? (
              <DrawerNavigation
                loggedIn={loggedIn}
                teamSlug={teamSlug}
                inTeamContext={inTeamContext}
                currentUserIsMember={currentUserIsMember}
                {...this.props}
              />
            ) : null}
            <Main>
              <Header
                loggedIn={loggedIn}
                pageType={routeSlug}
                inTeamContext={inTeamContext}
                currentUserIsMember={currentUserIsMember}
                {...this.props}
              />
              <FlashMessage />
              <StyledContent className="content-wrapper">
                {children}
              </StyledContent>
            </Main>
          </Wrapper>
        </MuiPickersUtilsProvider>
      </React.Fragment>
    );
  }
}

HomeComponent.propTypes = {
  // https://github.com/yannickcr/eslint-plugin-react/issues/1389
  // eslint-disable-next-line react/no-typos
  clientSessionId: PropTypes.string.isRequired,
  setFlashMessage: PropTypes.func.isRequired,
};

HomeComponent.contextTypes = {
  store: PropTypes.object,
};

const ConnectedHomeComponent = withSetFlashMessage(withClientSessionId(HomeComponent));

const HomeContainer = Relay.createContainer(ConnectedHomeComponent, {
  fragments: {
    user: () => Relay.QL`
      fragment on User {
        id
        dbid
        name
        token
        email
        is_admin
        accepted_terms
        last_accepted_terms_at
        completed_signup
        name
        login
        permissions
        profile_image
        settings
        source_id
        team_ids
        user_teams
        current_project {
          dbid
          id
          title
          team {
            id
            dbid
            avatar
            name
            slug
          }
        }
        current_team {
          id
          dbid
          avatar
          name
          slug
          get_data_report_url
          team_bot_installation(bot_identifier: "smooch") {
            smooch_enabled_integrations
          }
          projects(first: 1) {
            edges {
              node {
                id
                dbid
                title
                team {
                  id
                  dbid
                  avatar
                  name
                  slug
                }
              }
            }
          }
        }
      }
    `,
  },
});

// eslint-disable-next-line react/no-multi-comp
class Home extends Component {
  componentWillMount() {
    const context = new CheckContext(this);
    const { clientSessionId, setFlashMessage } = this.props;
    context.startNetwork(null, clientSessionId, setFlashMessage);
  }

  // eslint-disable-next-line class-methods-use-this
  render() {
    const route = new MeRoute();

    return (
      <Relay.RootContainer
        Component={HomeContainer}
        route={route}
        renderFetched={data => (
          <HomeContainer
            location={this.props.location}
            params={this.props.params}
            {...data}
          >
            {this.props.children}
          </HomeContainer>
        )}
      />
    );
  }
}

Home.propTypes = {
  clientSessionId: PropTypes.string.isRequired,
  setFlashMessage: PropTypes.func.isRequired,
};

Home.contextTypes = {
  store: PropTypes.object,
};

export default withSetFlashMessage(withClientSessionId(Home));
<|endoftext|>


<commit_before>const findBaseLength = () => {
  for (let n = 2; ; n += 2) {
    const b = Math.pow(10, n);
    if (2 * b - 2 == 2 * b - 1 || -2 * b + 2 == -2 * b + 1)
      return n - 2;
  }
};

export function methods(baseLength = 0) {

  const BASE_LENGTH = (baseLength & ~1) || findBaseLength();

  const BASE = Math.pow(10, BASE_LENGTH);
  const HALFBASE = Math.sqrt(BASE);

  const ZEROES = ('' + BASE).slice(1);

  const zeroFill = s => ZEROES.slice(s.length) + s;

  const _toString = function _toString(r) {
    return r
      .map(d => zeroFill(''+d))
      .reverse()
      .join('')
      .replace(/^0+/, '');
  };

  class LongInt {
    constructor(sign, digits) {
      if (digits.some(d => d < 0 || d >= BASE))
        throw new Error(`illegal long integer digits ${digits}`);
      this.sign = sign;
      this.digits = digits;
    }

    toString() {
      if (_isZero(this))
        return '0';
      else
        return (this.sign < 0 ? '-' : '') + _toString(this.digits);
    }
  };

  const make = (s, d) => {
    if (d.length > 1)
      return new LongInt(s, d);
    else if (d.length == 1)
      return s * d[0];
    else
      return 0;
  }

  const shouldPromote = n => Math.abs(n) >= BASE;

  const promote = function promote(n) {
    const sign = (n > 0) - (n < 0);
    n = Math.abs(n);

    const digits = [];
    while (n > 0) {
      digits.push(n % BASE);
      n = Math.floor(n / BASE);
    }

    return new LongInt(sign, digits);
  };

  const toJS = n => {
    if (n.sign == 0)
      return 0;
    else
      return (n.sign * n.digits[0] * Math.pow(BASE, n.digits.length - 1));
  };

  const _last = a => a[a.length - 1];

  const parse = function parse(literal) {
    if (!literal.match(/^[+-]?\d{1,3}(_?\d{3})*$/))
      throw new Error("expected an integer literal, got "+literal);

    const s = literal.replace(/^[+-]/, '').replace(/_/g, '');

    const digits = [];
    let n = s.length;
    while (n > 0) {
      const m = Math.max(n - BASE_LENGTH, 0);
      digits.push(parseInt(s.slice(m, n)));
      n = m;
    }

    while (digits.length > 0 && _last(digits) == 0)
      digits.pop();

    const sign = digits.length == 0 ? 0 : literal[0] == '-' ? -1 : 1;

    return make(sign, digits);
  };

  const negative = n => make(-n.sign, n.digits);
  const abs      = n => sgn(n) ? make(1, n.digits) : n;
  const sgn      = n => n.sign;
  const _isZero  = n => n.sign == 0;
  const isEven   = n => _isZero(n) || n.digits[0] % 2 == 0;

  const _cmp = function _cmp(r, s) {
    if (r.length != s.length)
      return r.length - s.length;

    for (let i = r.length - 1; i >= 0; --i)
      if (r[i] != s[i])
        return r[i] - s[i];

    return 0;
  };

  const _plus = function _plus(r, s) {
    const result = [];
    let carry = 0;
    let i = 0;
    while (i < r.length || i < s.length || carry) {
      const digit = (r[i] || 0) + (s[i] || 0) + carry;
      carry = digit >= BASE;
      result.push(digit % BASE);
      ++i;
    }
    return result;
  };

  const _minus = function _minus(r, s) {
    const result = [];
    let borrow = 0;
    let i = 0;
    while (i < r.length || i < s.length) {
      const digit = (r[i] || 0) - (s[i] || 0) - borrow;
      borrow = digit < 0;
      result.push((digit + BASE) % BASE);
      ++i;
    }
    if (borrow)
      throw new Error("panic: internal function called with bad arguments");

    while (_last(result) == 0)
      result.pop();
    return result;
  };

  const cmp = function cmp(a, b) {
    if (_isZero(a))
      return -b.sign;
    else if (_isZero(b))
      return a.sign;
    else if (a.sign != b.sign)
      return a.sign;
    else
      return a.sign * _cmp(a.digits, b.digits);
  };

  const _negative = n => new LongInt(-n.sign, n.digits);

  const plus = function plus(a, b) {
    if (_isZero(a))
      return b;
    else if (_isZero(b))
      return a;
    else if (a.sign != b.sign)
      return minus(a, _negative(b));
    else
      return make(a.sign, _plus(a.digits, b.digits));
  };

  const minus = function minus(a, b) {
    if (_isZero(a))
      return negative(b);
    else if (_isZero(b))
      return a;
    else if (a.sign != b.sign)
      return plus(a, _negative(b));
    else {
      const d = _cmp(a.digits, b.digits);
      if (d == 0)
        return 0;
      else if (d < 0)
        return make(-a.sign, _minus(b.digits, a.digits));
      else
        return make(a.sign, _minus(a.digits, b.digits));
    }
  }

  const _lo = d => d % HALFBASE;
  const _hi = d => Math.floor(d / HALFBASE);

  const _digitByDigit = function _digitByDigit(a, b) {
    if (b < BASE / a)
      return [a*b, 0];
    else {
      const alo = _lo(a);
      const ahi = _hi(a);
      const blo = _lo(b);
      const bhi = _hi(b);

      const m = alo * bhi + blo * ahi;
      const lo = alo * blo + _lo(m) * HALFBASE;

      return [lo % BASE, ahi * bhi + _hi(m) + (lo >= BASE)];
    }
  };

  const _seqByDigit = function _seqByDigit(s, d) {
    const result = [];
    let carry = 0;
    for (let i = 0; i < s.length; ++i) {
      const t = _digitByDigit(d, s[i]);
      result.push((t[0] + carry) % BASE);
      carry = t[1] + (t[0] + carry >= BASE);
    }
    if (carry)
      result.push(carry);
    return result;
  };

  const _times = function _times(r, s) {
    const result = [];
    let tmp = [];
    for (let i = 0; i < r.length; ++i) {
      tmp = _plus(tmp, _seqByDigit(s, r[i]));
      result.push(tmp.shift());
    }
    for (let i = 0; i < tmp.length; ++i)
      result.push(tmp[i]);
    return result;
  };

  const times = function times(a, b) {
    if (_isZero(a))
      return a;
    else if (_isZero(b))
      return b;
    else
      return make(a.sign * b.sign, _times(a.digits, b.digits));
  };

  const _idiv = function _idiv(r, s) {
    const scale = Math.floor((BASE - 1) / _last(s));
    const rs = _seqByDigit(r, scale);
    const ss = _seqByDigit(s, scale);
    const m = ss.length;
    const d = _last(ss) + 1;

    let q = [];
    let h = [];
    let t = rs;

    let f, n;

    while (true) {
      while (_last(q) == 0)
        q.pop();

      if (_cmp(h, ss) >= 0) {
        n = _last(h) * (h.length > m ? BASE : 1);
        f = Math.floor(n / d) || 1;
        q = _plus(q, [f]);
        h = _minus(h, _seqByDigit(ss, f));
      } else if (t.length) {
        q.unshift(0);
        h.unshift(_last(t))
        t.pop();
      } else
        return q;
    }
  };

  const idiv = function idiv(a, b) {
    if (b.sign == 0)
      throw new Error('division by zero');

    const d = _cmp(a.digits, b.digits);

    if (d < 0)
      return 0;
    else if (d == 0)
      return a.sign * b.sign;
    else {
      const s = a.sign * b.sign;
      if (s > 0)
        return make(s, _idiv(a.digits, b.digits));
      else
        return make(s, _idiv(_plus(a.digits, _minus(b.digits, [1])), b.digits));
    }
  };

  const gcd = (a, b) => {
    a = Math.abs(a);
    b = Math.abs(b);

    while (b > 0)
      [a, b] = [b, a % b];

    return a;
  };

  const checkedOperator = (longMethod, nativeMethod) => (x, y) => {
    const t = nativeMethod(x, y);
    if (shouldPromote(x) || shouldPromote(y) || shouldPromote(t))
      return longMethod(promote(x), promote(y));
    else
      return t;
  };

  return {
    isInteger: {
      LongInt: x => true,
      Integer: x => true
    },
    isRational: {
      LongInt: x => true,
      Integer: x => true
    },
    isReal: {
      LongInt: x => true,
      Integer: x => true
    },
    integer: {
      String : parse,
      LongInt: x => x,
      Integer: x => x
    },
    toJS: {
      LongInt: toJS,
      Integer: x => x
    },
    negative: {
      LongInt: negative,
      Integer: x => -x
    },
    abs: {
      LongInt: abs,
      Integer: x => Math.abs(x)
    },
    sgn: {
      LongInt: sgn,
      Integer: x => (x > 0) - (x < 0)
    },
    isEven: {
      LongInt: isEven,
      Integer: x => x % 2 == 0
    },
    floor: {
      LongInt: x => x,
      Integer: x => x
    },
    ceil: {
      LongInt: x => x,
      Integer: x => x
    },
    round: {
      LongInt: x => x,
      Integer: x => x
    },
    cmp: {
      LongInt: {
        LongInt: cmp,
        Integer: (x, y) => cmp(x, promote(y))
      },
      Integer: {
        LongInt: (x, y) => cmp(promote(x), y),
        Integer: (x, y) => (x > y) - (x < y)
      }
    },
    plus: {
      LongInt: {
        LongInt: plus,
        Integer: (x, y) => plus(x, promote(y))
      },
      Integer: {
        LongInt: (x, y) => plus(promote(x), y),
        Integer: checkedOperator(plus, (x, y) => x + y)
      }
    },
    minus: {
      LongInt: {
        LongInt: minus,
        Integer: (x, y) => minus(x, promote(y))
      },
      Integer: {
        LongInt: (x, y) => minus(promote(x), y),
        Integer: checkedOperator(minus, (x, y) => x - y)
      }
    },
    times: {
      LongInt: {
        LongInt: times,
        Integer: (x, y) => times(x, promote(y))
      },
      Integer: {
        LongInt: (x, y) => times(promote(x), y),
        Integer: checkedOperator(times, (x, y) => x * y)
      }
    },
    idiv: {
      LongInt: {
        LongInt: idiv,
        Integer: (x, y) => idiv(x, promote(y))
      },
      Integer: {
        LongInt: (x, y) => idiv(promote(x), y),
        Integer: (x, y) => Math.floor(x / y)
      }
    },
    mod: {
      Integer: {
        Integer: (x, y) => x % y + (x < 0 ? y : 0)
      }
    },
    gcd: {
      Integer: {
        Integer: gcd
      }
    },
    __repr__: { LongInt: x => ({ sign: x.sign, digits: x.digits }) },
    __Integer__: { Object: ({ Integer: n }) => n },
    __LongInt__: { Object: ({ LongInt: obj }) => make(obj.sign, obj.digits) }
  }
};

if (require.main == module) {
  const ops = require('./base').arithmetic().register(methods()).ops();
  const timer = require('../common/util').timer();

  const N = 59;
  let t = 1;

  for (let i = 1; i < N; ++i) {
    t = ops.times(t, i);
    console.log(`${t}`);
  }

  console.log(`${ops.idiv(ops.plus(t, 1), t)}`);
  console.log(`${ops.idiv(ops.negative(ops.plus(t, 1)), t)}`);
  console.log(`${ops.integer(t.toString())}`);
  console.log(`${ops.idiv(ops.plus(t, 1), ops.negative(t))}`);
  console.log(`${ops.idiv(ops.negative(ops.plus(t, 1)), ops.negative(t))}`);

  for (let i = 1; i < N; ++i) {
    t = ops.idiv(t, i);
    console.log(`${t}`);
  }

  console.log(`${ops.integer('-12_345_678_901_234_567_890')}`);

  console.log();
  console.log(`Computation time: ${timer()} msec`);
}
<commit_msg>fix another issue with the long integer _idiv() routine<commit_after>const findBaseLength = () => {
  for (let n = 2; ; n += 2) {
    const b = Math.pow(10, n);
    if (2 * b - 2 == 2 * b - 1 || -2 * b + 2 == -2 * b + 1)
      return n - 2;
  }
};

export function methods(baseLength = 0) {

  const BASE_LENGTH = (baseLength & ~1) || findBaseLength();

  const BASE = Math.pow(10, BASE_LENGTH);
  const HALFBASE = Math.sqrt(BASE);

  const ZEROES = ('' + BASE).slice(1);

  const zeroFill = s => ZEROES.slice(s.length) + s;

  const _toString = function _toString(r) {
    return r
      .map(d => zeroFill(''+d))
      .reverse()
      .join('')
      .replace(/^0+/, '');
  };

  class LongInt {
    constructor(sign, digits) {
      if (digits.some(d => d < 0 || d >= BASE))
        throw new Error(`illegal long integer digits ${digits}`);
      this.sign = sign;
      this.digits = digits;
    }

    toString() {
      if (_isZero(this))
        return '0';
      else
        return (this.sign < 0 ? '-' : '') + _toString(this.digits);
    }
  };

  const make = (s, d) => {
    if (d.length > 1)
      return new LongInt(s, d);
    else if (d.length == 1)
      return s * d[0];
    else
      return 0;
  }

  const shouldPromote = n => Math.abs(n) >= BASE;

  const promote = function promote(n) {
    const sign = (n > 0) - (n < 0);
    n = Math.abs(n);

    const digits = [];
    while (n > 0) {
      digits.push(n % BASE);
      n = Math.floor(n / BASE);
    }

    return new LongInt(sign, digits);
  };

  const toJS = n => {
    if (n.sign == 0)
      return 0;
    else
      return (n.sign * n.digits[0] * Math.pow(BASE, n.digits.length - 1));
  };

  const _last = a => a[a.length - 1];

  const parse = function parse(literal) {
    if (!literal.match(/^[+-]?\d{1,3}(_?\d{3})*$/))
      throw new Error("expected an integer literal, got "+literal);

    const s = literal.replace(/^[+-]/, '').replace(/_/g, '');

    const digits = [];
    let n = s.length;
    while (n > 0) {
      const m = Math.max(n - BASE_LENGTH, 0);
      digits.push(parseInt(s.slice(m, n)));
      n = m;
    }

    while (digits.length > 0 && _last(digits) == 0)
      digits.pop();

    const sign = digits.length == 0 ? 0 : literal[0] == '-' ? -1 : 1;

    return make(sign, digits);
  };

  const negative = n => make(-n.sign, n.digits);
  const abs      = n => sgn(n) ? make(1, n.digits) : n;
  const sgn      = n => n.sign;
  const _isZero  = n => n.sign == 0;
  const isEven   = n => _isZero(n) || n.digits[0] % 2 == 0;

  const _cmp = function _cmp(r, s) {
    if (r.length != s.length)
      return r.length - s.length;

    for (let i = r.length - 1; i >= 0; --i)
      if (r[i] != s[i])
        return r[i] - s[i];

    return 0;
  };

  const _plus = function _plus(r, s) {
    const result = [];
    let carry = 0;
    let i = 0;
    while (i < r.length || i < s.length || carry) {
      const digit = (r[i] || 0) + (s[i] || 0) + carry;
      carry = digit >= BASE;
      result.push(digit % BASE);
      ++i;
    }
    return result;
  };

  const _minus = function _minus(r, s) {
    const result = [];
    let borrow = 0;
    let i = 0;
    while (i < r.length || i < s.length) {
      const digit = (r[i] || 0) - (s[i] || 0) - borrow;
      borrow = digit < 0;
      result.push((digit + BASE) % BASE);
      ++i;
    }
    if (borrow)
      throw new Error("panic: internal function called with bad arguments");

    while (_last(result) == 0)
      result.pop();
    return result;
  };

  const cmp = function cmp(a, b) {
    if (_isZero(a))
      return -b.sign;
    else if (_isZero(b))
      return a.sign;
    else if (a.sign != b.sign)
      return a.sign;
    else
      return a.sign * _cmp(a.digits, b.digits);
  };

  const _negative = n => new LongInt(-n.sign, n.digits);

  const plus = function plus(a, b) {
    if (_isZero(a))
      return b;
    else if (_isZero(b))
      return a;
    else if (a.sign != b.sign)
      return minus(a, _negative(b));
    else
      return make(a.sign, _plus(a.digits, b.digits));
  };

  const minus = function minus(a, b) {
    if (_isZero(a))
      return negative(b);
    else if (_isZero(b))
      return a;
    else if (a.sign != b.sign)
      return plus(a, _negative(b));
    else {
      const d = _cmp(a.digits, b.digits);
      if (d == 0)
        return 0;
      else if (d < 0)
        return make(-a.sign, _minus(b.digits, a.digits));
      else
        return make(a.sign, _minus(a.digits, b.digits));
    }
  }

  const _lo = d => d % HALFBASE;
  const _hi = d => Math.floor(d / HALFBASE);

  const _digitByDigit = function _digitByDigit(a, b) {
    if (b < BASE / a)
      return [a*b, 0];
    else {
      const alo = _lo(a);
      const ahi = _hi(a);
      const blo = _lo(b);
      const bhi = _hi(b);

      const m = alo * bhi + blo * ahi;
      const lo = alo * blo + _lo(m) * HALFBASE;

      return [lo % BASE, ahi * bhi + _hi(m) + (lo >= BASE)];
    }
  };

  const _seqByDigit = function _seqByDigit(s, d) {
    const result = [];
    let carry = 0;
    for (let i = 0; i < s.length; ++i) {
      const t = _digitByDigit(d, s[i]);
      result.push((t[0] + carry) % BASE);
      carry = t[1] + (t[0] + carry >= BASE);
    }
    if (carry)
      result.push(carry);
    return result;
  };

  const _times = function _times(r, s) {
    const result = [];
    let tmp = [];
    for (let i = 0; i < r.length; ++i) {
      tmp = _plus(tmp, _seqByDigit(s, r[i]));
      result.push(tmp.shift());
    }
    for (let i = 0; i < tmp.length; ++i)
      result.push(tmp[i]);
    return result;
  };

  const times = function times(a, b) {
    if (_isZero(a))
      return a;
    else if (_isZero(b))
      return b;
    else
      return make(a.sign * b.sign, _times(a.digits, b.digits));
  };

  const _idiv = function _idiv(r, s) {
    const scale = Math.floor((BASE - 1) / _last(s));
    const rs = _seqByDigit(r, scale);
    const ss = _seqByDigit(s, scale);
    const m = ss.length;
    const d = _last(ss) + 1;

    let q = [];
    let h = [];
    let t = rs;

    let f, n, ssf;

    while (true) {
      while (_last(q) == 0)
        q.pop();

      if (_cmp(h, ss) >= 0) {
        n = _last(h) * (h.length > m ? BASE : 1);
        f = Math.floor(n / d) || 1;
        ssf = _seqByDigit(ss, f);
        if (_cmp(h, ssf) < 0) {
          --f;
          ssf = _minus(ssf, ss);
        }
        q = _plus(q, [f]);
        h = _minus(h, ssf);
      } else if (t.length) {
        q.unshift(0);
        h.unshift(_last(t))
        t.pop();
      } else
        return q;
    }
  };

  const idiv = function idiv(a, b) {
    if (b.sign == 0)
      throw new Error('division by zero');

    const d = _cmp(a.digits, b.digits);

    if (d < 0)
      return 0;
    else if (d == 0)
      return a.sign * b.sign;
    else {
      const s = a.sign * b.sign;
      if (s > 0)
        return make(s, _idiv(a.digits, b.digits));
      else
        return make(s, _idiv(_plus(a.digits, _minus(b.digits, [1])), b.digits));
    }
  };

  const gcd = (a, b) => {
    a = Math.abs(a);
    b = Math.abs(b);

    while (b > 0)
      [a, b] = [b, a % b];

    return a;
  };

  const checkedOperator = (longMethod, nativeMethod) => (x, y) => {
    const t = nativeMethod(x, y);
    if (shouldPromote(x) || shouldPromote(y) || shouldPromote(t))
      return longMethod(promote(x), promote(y));
    else
      return t;
  };

  return {
    isInteger: {
      LongInt: x => true,
      Integer: x => true
    },
    isRational: {
      LongInt: x => true,
      Integer: x => true
    },
    isReal: {
      LongInt: x => true,
      Integer: x => true
    },
    integer: {
      String : parse,
      LongInt: x => x,
      Integer: x => x
    },
    toJS: {
      LongInt: toJS,
      Integer: x => x
    },
    negative: {
      LongInt: negative,
      Integer: x => -x
    },
    abs: {
      LongInt: abs,
      Integer: x => Math.abs(x)
    },
    sgn: {
      LongInt: sgn,
      Integer: x => (x > 0) - (x < 0)
    },
    isEven: {
      LongInt: isEven,
      Integer: x => x % 2 == 0
    },
    floor: {
      LongInt: x => x,
      Integer: x => x
    },
    ceil: {
      LongInt: x => x,
      Integer: x => x
    },
    round: {
      LongInt: x => x,
      Integer: x => x
    },
    cmp: {
      LongInt: {
        LongInt: cmp,
        Integer: (x, y) => cmp(x, promote(y))
      },
      Integer: {
        LongInt: (x, y) => cmp(promote(x), y),
        Integer: (x, y) => (x > y) - (x < y)
      }
    },
    plus: {
      LongInt: {
        LongInt: plus,
        Integer: (x, y) => plus(x, promote(y))
      },
      Integer: {
        LongInt: (x, y) => plus(promote(x), y),
        Integer: checkedOperator(plus, (x, y) => x + y)
      }
    },
    minus: {
      LongInt: {
        LongInt: minus,
        Integer: (x, y) => minus(x, promote(y))
      },
      Integer: {
        LongInt: (x, y) => minus(promote(x), y),
        Integer: checkedOperator(minus, (x, y) => x - y)
      }
    },
    times: {
      LongInt: {
        LongInt: times,
        Integer: (x, y) => times(x, promote(y))
      },
      Integer: {
        LongInt: (x, y) => times(promote(x), y),
        Integer: checkedOperator(times, (x, y) => x * y)
      }
    },
    idiv: {
      LongInt: {
        LongInt: idiv,
        Integer: (x, y) => idiv(x, promote(y))
      },
      Integer: {
        LongInt: (x, y) => idiv(promote(x), y),
        Integer: (x, y) => Math.floor(x / y)
      }
    },
    mod: {
      Integer: {
        Integer: (x, y) => x % y + (x < 0 ? y : 0)
      }
    },
    gcd: {
      Integer: {
        Integer: gcd
      }
    },
    __repr__: { LongInt: x => ({ sign: x.sign, digits: x.digits }) },
    __Integer__: { Object: ({ Integer: n }) => n },
    __LongInt__: { Object: ({ LongInt: obj }) => make(obj.sign, obj.digits) }
  }
};

if (require.main == module) {
  const ops = require('./base').arithmetic().register(methods()).ops();
  const timer = require('../common/util').timer();

  const N = 59;
  let t = 1;

  for (let i = 1; i < N; ++i) {
    t = ops.times(t, i);
    console.log(`${t}`);
  }

  console.log(`${ops.idiv(ops.plus(t, 1), t)}`);
  console.log(`${ops.idiv(ops.negative(ops.plus(t, 1)), t)}`);
  console.log(`${ops.integer(t.toString())}`);
  console.log(`${ops.idiv(ops.plus(t, 1), ops.negative(t))}`);
  console.log(`${ops.idiv(ops.negative(ops.plus(t, 1)), ops.negative(t))}`);

  for (let i = 1; i < N; ++i) {
    t = ops.idiv(t, i);
    console.log(`${t}`);
  }

  console.log(`${ops.integer('-12_345_678_901_234_567_890')}`);

  console.log();
  console.log(`Computation time: ${timer()} msec`);
}
<|endoftext|>


<commit_before>var footballApp = angular.module('footballApp', ['ngTable', 'ngRoute', 'ui.bootstrap']);
var users = 'api/users';
var identity = 'api/identity';
var match = 'api/matches';

footballApp.config(['$routeProvider',
  function ($routeProvider) {
      $routeProvider.
        when('/matches', {
            templateUrl: 'Angular/CreateMatch.html',
            controller: 'matchController'
        })
        .otherwise({
            templateUrl: 'Angular/UsersTable.html',
          controller: 'footballController'
        });
  }]);

footballApp.controller('matchController', function ($scope, matchesRepository, ngTableParams, $filter) {
    $scope.submit = function () {
        var date = $scope.date;
        var time = $scope.time;
        var dateTime = date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate() + 'T' + time.getHours() + ':' + time.getMinutes();
        matchesRepository.insertMatch(dateTime, function () { reloadMatches(); });
    };
    $scope.open = function($event) {
        $event.preventDefault();
        $event.stopPropagation();
        $scope.opened = true;
    };
    function getMatches() {
        return $scope.matches;
    };
    function transformMatches(match) {
        return {
            Id: match.Id,
            PlannedTime: match.PlannedTime,
            Creator: match.Creator.Name,
            Players: match.Players.map(function (user) { return user.Name; }).join(' ')
        };
    };
    reloadMatches();
    $scope.join = function (matchId) {
        alert('joining match ' + matchId + ' not yet implemented');
    };
    $scope.leave = function (matchId) {
        alert('leaving match ' + matchId + ' not yet implemented');
    };
    $scope.forJoin = function (matchId) {
        return true
    };
    $scope.forLeave = function (matchId) {
        return true;
    };
    function reloadMatches() {
        matchesRepository.getPlannedMatches(function (result) {
            $scope.matches = result.map(transformMatches);

            if (typeof ($scope.tableParams) == 'undefined') {
                $scope.tableParams = new ngTableParams({
                    page: 1,
                    count: 10,
                    sorting: { Name: 'asc' }
                }, {
                    total: getMatches().length,
                    getData: function ($defer, params) {
                        var orderedData = params.sorting()
                            ? $filter('orderBy')(getMatches(), params.orderBy())
                            : getMatches();
                        $defer.resolve(orderedData.slice((params.page() - 1) * params.count(), params.page() * params.count()));
                    }
                });
            }
            else
                $scope.tableParams.reload();
        });
    };
    $scope.minDate = new Date();
    $scope.date = new Date();
    $scope.time = new Date();
});

footballApp.controller('footballController', function ($scope, $filter, footballersRepository, identityRepository, ngTableParams) {
    $scope.registered = false;

    identityRepository.getIdentity(function (user) {
        $scope.identity = user;
        $scope.registered = true;
    }, function () {
        $scope.identity = null;
        $scope.registered = false;
    });

    $scope.register = function () {
        footballersRepository.insertFootballer(function (user) {
            $scope.registered = true;
            $scope.identity = user;
            reloadFootballers();
        });
    };

    $scope.unregister = function () {
        footballersRepository.deleteFootballer($scope.identity.Id, function () {
            $scope.registered = false;
            $scope.identity = null;
            reloadFootballers();
        });
    };

    function getFootballers() {
        return $scope.footballers;
    }

    reloadFootballers();
    function reloadFootballers() {
        footballersRepository.getFootballers(function (result) {
            $scope.footballers = result;

            if (typeof ($scope.tableParams) == 'undefined') {
                $scope.tableParams = new ngTableParams({
                    page: 1,
                    count: 10,
                    sorting: { Name: 'asc' }
                }, {
                    total: getFootballers().length,
                    getData: function ($defer, params) {
                        var orderedData = params.sorting()
                            ? $filter('orderBy')(getFootballers(), params.orderBy())
                            : getFootballers();
                        $defer.resolve(orderedData.slice((params.page() - 1) * params.count(), params.page() * params.count()));
                    }
                });
            }
            else
                $scope.tableParams.reload();
        });
    }
});

footballApp.factory('footballersRepository', function ($http) {
    return {
        getFootballers: function (callback) {
            $http.get(users).success(callback);
        },
        insertFootballer: function (callback, fallback) {
            $http.post(users).success(callback).error(fallback);
        },
        deleteFootballer: function (id, callback) {
            $http.delete(users + '/' + id).success(callback);
        }
    }
});

footballApp.factory('identityRepository', function ($http) {
    return {
        getIdentity: function (callback, fallback) {
            $http.get(identity).success(callback).error(fallback);
        }
    }
});

footballApp.factory('matchesRepository', function ($http) {
    return {
        insertMatch: function (date, callback) {
            $http.post(match, { PlannedTime: date }).success(callback);
        },
        getPlannedMatches: function (callback) {
            $http.get(match).success(callback);
        }
    }
});<commit_msg>#8 displaying join/leave button based on logged in user and users in match<commit_after>var footballApp = angular.module('footballApp', ['ngTable', 'ngRoute', 'ui.bootstrap']);
var users = 'api/users';
var identity = 'api/identity';
var match = 'api/matches';

footballApp.config(['$routeProvider',
  function ($routeProvider) {
      $routeProvider.
        when('/matches', {
            templateUrl: 'Angular/CreateMatch.html',
            controller: 'matchController'
        })
        .otherwise({
            templateUrl: 'Angular/UsersTable.html',
          controller: 'footballController'
        });
  }]);

footballApp.controller('matchController', function ($scope, $rootScope, matchesRepository, ngTableParams, $filter) {
    $scope.submit = function () {
        var date = $scope.date;
        var time = $scope.time;
        var dateTime = date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate() + 'T' + time.getHours() + ':' + time.getMinutes();
        matchesRepository.insertMatch(dateTime, function () { reloadMatches(); });
    };
    $scope.open = function($event) {
        $event.preventDefault();
        $event.stopPropagation();
        $scope.opened = true;
    };
    function getMatches() {
        return $scope.matches;
    };
    function transformMatches(match) {
        return {
            Id: match.Id,
            PlannedTime: match.PlannedTime,
            Creator: match.Creator.Name,
            Players: match.Players.map(function (user) { return user.Name; }).join(' ')
        };
    };
    reloadMatches();
    $scope.join = function (matchId) {
        alert('joining match ' + matchId + ' not yet implemented');
    };
    $scope.leave = function (matchId) {
        alert('leaving match ' + matchId + ' not yet implemented');
    };
    $scope.forJoin = function (matchId) {
        return $rootScope.registered && !isUserInMatch(matchId, $rootScope.identity);
    };
    $scope.forLeave = function (matchId) {
        return $rootScope.registered && isUserInMatch(matchId, $rootScope.identity);
    };
    function isUserInMatch(matchId, user) {
        var matches = $scope.serverMatches.filter(function (match) { return match.Id == matchId });
        if (matches.length == 0)
            return false;

        return matches[0].Players.filter(function (player) { return player.Id == user.Id }).length;
    };
    function reloadMatches() {
        matchesRepository.getPlannedMatches(function (result) {
            $scope.serverMatches = result;
            $scope.matches = result.map(transformMatches);

            if (typeof ($scope.tableParams) == 'undefined') {
                $scope.tableParams = new ngTableParams({
                    page: 1,
                    count: 10,
                    sorting: { Name: 'asc' }
                }, {
                    total: getMatches().length,
                    getData: function ($defer, params) {
                        var orderedData = params.sorting()
                            ? $filter('orderBy')(getMatches(), params.orderBy())
                            : getMatches();
                        $defer.resolve(orderedData.slice((params.page() - 1) * params.count(), params.page() * params.count()));
                    }
                });
            }
            else
                $scope.tableParams.reload();
        });
    };
    $scope.minDate = new Date();
    $scope.date = new Date();
    $scope.time = new Date();
});

footballApp.controller('footballController', function ($scope, $rootScope, $filter, footballersRepository, identityRepository, ngTableParams) {
    $rootScope.registered = false;

    identityRepository.getIdentity(function (user) {
        $rootScope.identity = user;
        $rootScope.registered = true;
    }, function () {
        $rootScope.identity = null;
        $rootScope.registered = false;
    });

    $scope.register = function () {
        footballersRepository.insertFootballer(function (user) {
            $rootScope.registered = true;
            $rootScope.identity = user;
            reloadFootballers();
        });
    };

    $scope.unregister = function () {
        footballersRepository.deleteFootballer($rootScope.identity.Id, function () {
            $rootScope.registered = false;
            $rootScope.identity = null;
            reloadFootballers();
        });
    };

    function getFootballers() {
        return $scope.footballers;
    }

    reloadFootballers();
    function reloadFootballers() {
        footballersRepository.getFootballers(function (result) {
            $scope.footballers = result;

            if (typeof ($scope.tableParams) == 'undefined') {
                $scope.tableParams = new ngTableParams({
                    page: 1,
                    count: 10,
                    sorting: { Name: 'asc' }
                }, {
                    total: getFootballers().length,
                    getData: function ($defer, params) {
                        var orderedData = params.sorting()
                            ? $filter('orderBy')(getFootballers(), params.orderBy())
                            : getFootballers();
                        $defer.resolve(orderedData.slice((params.page() - 1) * params.count(), params.page() * params.count()));
                    }
                });
            }
            else
                $scope.tableParams.reload();
        });
    }
});

footballApp.factory('footballersRepository', function ($http) {
    return {
        getFootballers: function (callback) {
            $http.get(users).success(callback);
        },
        insertFootballer: function (callback, fallback) {
            $http.post(users).success(callback).error(fallback);
        },
        deleteFootballer: function (id, callback) {
            $http.delete(users + '/' + id).success(callback);
        }
    }
});

footballApp.factory('identityRepository', function ($http) {
    return {
        getIdentity: function (callback, fallback) {
            $http.get(identity).success(callback).error(fallback);
        }
    }
});

footballApp.factory('matchesRepository', function ($http) {
    return {
        insertMatch: function (date, callback) {
            $http.post(match, { PlannedTime: date }).success(callback);
        },
        getPlannedMatches: function (callback) {
            $http.get(match).success(callback);
        }
    }
});<|endoftext|>


<commit_before>const {LayerController} = require("../components/layerController");

var globalMap = {};
global.L = {DomEvent: {disableClickPropagation: jest.fn(), disableScrollPropagation: jest.fn()},
            imageOverlay: (raster, coordinates, settings) => ({addTo: (map) => {globalMap[raster] = coordinates}, remove: (map) => {delete globalMap[raster]}, bringToFront: () => {}, bringToBack: () => {}})};

const controllers = require("../components/Controller.js");

var testDisplay = {};
jest.mock('../components/Controller.js', () => ({
    currentDomain: ({
        getValue: () => 1,
        subscribe: () => {}
    }),
    current_display: ({
        getValue: () => ({}),
        setValue: jest.fn()
    }),
    currentSimulation: ({
        getValue: () => "test",
    }),
    currentDomain: ({
        getValue: () => 1,
        subscribe: () => {}
    }),
    sorted_timestamps: ({
        getValue: () => ["2020"]
    }),
    raster_base: ({
        getValue: () => "test_base/"
    }),
    current_timestamp: ({
        getValue: () => ["2020"]
    }),
    rasters: ({
        getValue: () => ({
            1: {
                "2020": {"raster": {raster: "raster test", coords: {0: [0, 0], 1: [0, 1], 2: [1, 0], 3: [1, 1]}, "colorbar": "test colorbar raster"},
                       "overlay": {raster: "overlay test", coords: {0: [0, 0], 1: [0, 1], 2: [1, 0], 3: [1, 1]}, "colorbar": "test colorbar overlay" }},
                "2021": {"raster": {raster: "raster test current timestamp", coords: {0: [0, 0], 1: [0, 1], 2: [1, 0], 3: [1, 1]}},
                       "overlay": {raster: "overlay test", coords: {0: [0, 0], 1: [0, 1], 2: [1, 0], 3: [1, 1]} }}
            },
            2: {
                "2020": {"raster": {raster: "raster test 2", coords: {0: [0, 0], 1: [0, 1], 2: [1, 0], 3: [1, 1]}},
                       "overlay": {raster: "overlay test 2", coords: {0: [0, 0], 1: [0, 1], 2: [1, 0], 3: [1, 1]} }}
            }
        })
    }),
    organization: ({
        getValue: () => "SJSU"
    })
}));

const utils = require("../util.js");
jest.mock('../util.js', () => ({
    baseLayerDict: {},
    map: {
        addTo: jest.fn(),
        fitBounds: jest.fn()
    },
    dragElement: jest.fn(),
    overlay_list: ['overlay']
}));

describe('Tests for adding layers to menu and selecting layers', () => {
    var layerController;

    beforeEach(async () => {
        testDisplay = {};
        globalMap = {};
        controllers.currentDomain.getValue = () => 1;
        controllers.current_timestamp.getValue = () => "2020";
        controllers.current_display.getValue = () => testDisplay;
        controllers.current_display.setValue = (newDisplay) => {testDisplay = newDisplay};
        const div = document.createElement("div");
        div.id = "raster-colorbar";
        await document.body.appendChild(div);
        layerController = await document.body.appendChild(new LayerController());
        layerController.domainSwitch();
    });

    test('Layer Controller should populate its raster and overlay layers correctly', () => {
        const rasterDict = layerController.rasterDict;
        const overlayDict = layerController.overlayDict;
        expect(Object.entries(rasterDict).length).toEqual(1);
        expect("raster" in rasterDict).toEqual(true);
        expect(Object.entries(overlayDict).length).toEqual(1);
        expect("overlay" in overlayDict).toEqual(true);
    });

    test('Layers should be correctly added to the map when selected', () => {
        const rasterDict = layerController.rasterDict;
        layerController.handleOverlayadd("raster", rasterDict["raster"]);
        expect("test_base/raster test" in globalMap).toEqual(true);
        expect("raster" in controllers.current_display.getValue()).toEqual(true);
    });

    test('Layers should be correctly removed from the map when selected', () => {
        const rasterDict = layerController.rasterDict;
        layerController.handleOverlayadd("raster", rasterDict["raster"]);
        layerController.handleOverlayRemove("raster", rasterDict["raster"]);
        expect("test_base/raster test" in globalMap).toEqual(false);
    });

    test('Layer Controller should preserve previous selected layers when domain is switched on the same simulation', () => {
        const rasterDict = layerController.rasterDict;
        layerController.handleOverlayadd("raster", rasterDict["raster"]);
        controllers.currentDomain.getValue = () => 2;
        layerController.domainSwitch();
        expect("raster" in controllers.current_display.getValue()).toEqual(true);
        expect("test_base/raster test 2" in globalMap).toEqual(true);
        expect("test_base/raster test" in globalMap).toEqual(false);
    });

    test('Layer Controller should clear selected layers when domain is switched to new simulation', () => {
        const rasterDict = layerController.rasterDict;
        layerController.handleOverlayadd("raster", rasterDict["raster"]);
        controllers.currentSimulation.getValue = () => "new simulation";
        layerController.domainSwitch();
        expect(controllers.current_display.getValue()).toEqual({});
        expect(globalMap).toEqual({});
    });

    test('Layer Controller should show the current_timestamp', () => {
        controllers.current_timestamp.getValue = () => "2021";
        layerController.domainSwitch();
        const rasterDict = layerController.rasterDict;
        layerController.handleOverlayadd("raster", rasterDict["raster"]);
        expect("test_base/raster test current timestamp" in globalMap).toEqual(true);
    });

    test('Layer Controller should add any colorbars', () => {
        const rasterDict = layerController.rasterDict;
        layerController.handleOverlayadd("raster", rasterDict["raster"]);
        expect(layerController.displayedColorbar).toEqual("raster");
    });

    test('Layer Controller should remove colorbars when layer deselected', () => {
        const rasterDict = layerController.rasterDict;
        layerController.handleOverlayadd("raster", rasterDict["raster"]);
        layerController.handleOverlayRemove("raster", rasterDict["raster"]);
        expect(layerController.displayedColorbar).toEqual(null);
    });

    test('Layer Controller should put most recent selected colorbar on top', () => {
        const rasterDict = layerController.rasterDict;
        const overlayDict = layerController.overlayDict;
        layerController.handleOverlayadd("raster", rasterDict["raster"]);
        layerController.handleOverlayadd("overlay", overlayDict["overlay"]);
        expect(layerController.displayedColorbar).toEqual("overlay");
    });

    test('Layer Controller should put last selected colorbar on top when another is deselected', () => {
        const rasterDict = layerController.rasterDict;
        const overlayDict = layerController.overlayDict;
        layerController.handleOverlayadd("raster", rasterDict["raster"]);
        layerController.handleOverlayadd("overlay", overlayDict["overlay"]);
        layerController.handleOverlayRemove("overlay", overlayDict["overlay"]);
        expect(layerController.displayedColorbar).toEqual("raster");
    });

});<commit_msg>Added test for removing colorbar when switching to a domain without one<commit_after>const {LayerController} = require("../components/layerController");

var globalMap = {};
global.L = {DomEvent: {disableClickPropagation: jest.fn(), disableScrollPropagation: jest.fn()},
            imageOverlay: (raster, coordinates, settings) => ({addTo: (map) => {globalMap[raster] = coordinates}, remove: (map) => {delete globalMap[raster]}, bringToFront: () => {}, bringToBack: () => {}})};

const controllers = require("../components/Controller.js");

var testDisplay = {};
jest.mock('../components/Controller.js', () => ({
    currentDomain: ({
        getValue: () => 1,
        subscribe: () => {}
    }),
    current_display: ({
        getValue: () => ({}),
        setValue: jest.fn()
    }),
    currentSimulation: ({
        getValue: () => "test",
    }),
    currentDomain: ({
        getValue: () => 1,
        subscribe: () => {}
    }),
    sorted_timestamps: ({
        getValue: () => ["2020"]
    }),
    raster_base: ({
        getValue: () => "test_base/"
    }),
    current_timestamp: ({
        getValue: () => ["2020"]
    }),
    rasters: ({
        getValue: () => ({
            1: {
                "2020": {"raster": {raster: "raster test", coords: {0: [0, 0], 1: [0, 1], 2: [1, 0], 3: [1, 1]}, "colorbar": "test colorbar raster"},
                       "overlay": {raster: "overlay test", coords: {0: [0, 0], 1: [0, 1], 2: [1, 0], 3: [1, 1]}, "colorbar": "test colorbar overlay" }},
                "2021": {"raster": {raster: "raster test current timestamp", coords: {0: [0, 0], 1: [0, 1], 2: [1, 0], 3: [1, 1]}},
                       "overlay": {raster: "overlay test", coords: {0: [0, 0], 1: [0, 1], 2: [1, 0], 3: [1, 1]} }}
            },
            2: {
                "2020": {"raster": {raster: "raster test 2", coords: {0: [0, 0], 1: [0, 1], 2: [1, 0], 3: [1, 1]}},
                       "overlay": {raster: "overlay test 2", coords: {0: [0, 0], 1: [0, 1], 2: [1, 0], 3: [1, 1]} }}
            }
        })
    }),
    organization: ({
        getValue: () => "SJSU"
    })
}));

const utils = require("../util.js");
jest.mock('../util.js', () => ({
    baseLayerDict: {},
    map: {
        addTo: jest.fn(),
        fitBounds: jest.fn()
    },
    dragElement: jest.fn(),
    overlay_list: ['overlay']
}));

describe('Tests for adding layers to menu and selecting layers', () => {
    var layerController;

    beforeEach(async () => {
        testDisplay = {};
        globalMap = {};
        controllers.currentDomain.getValue = () => 1;
        controllers.current_timestamp.getValue = () => "2020";
        controllers.current_display.getValue = () => testDisplay;
        controllers.current_display.setValue = (newDisplay) => {testDisplay = newDisplay};
        const div = document.createElement("div");
        div.id = "raster-colorbar";
        await document.body.appendChild(div);
        layerController = await document.body.appendChild(new LayerController());
        layerController.domainSwitch();
    });

    test('Layer Controller should populate its raster and overlay layers correctly', () => {
        const rasterDict = layerController.rasterDict;
        const overlayDict = layerController.overlayDict;
        expect(Object.entries(rasterDict).length).toEqual(1);
        expect("raster" in rasterDict).toEqual(true);
        expect(Object.entries(overlayDict).length).toEqual(1);
        expect("overlay" in overlayDict).toEqual(true);
    });

    test('Layers should be correctly added to the map when selected', () => {
        const rasterDict = layerController.rasterDict;
        layerController.handleOverlayadd("raster", rasterDict["raster"]);
        expect("test_base/raster test" in globalMap).toEqual(true);
        expect("raster" in controllers.current_display.getValue()).toEqual(true);
    });

    test('Layers should be correctly removed from the map when selected', () => {
        const rasterDict = layerController.rasterDict;
        layerController.handleOverlayadd("raster", rasterDict["raster"]);
        layerController.handleOverlayRemove("raster", rasterDict["raster"]);
        expect("test_base/raster test" in globalMap).toEqual(false);
    });

    test('Layer Controller should preserve previous selected layers when domain is switched on the same simulation', () => {
        const rasterDict = layerController.rasterDict;
        layerController.handleOverlayadd("raster", rasterDict["raster"]);
        controllers.currentDomain.getValue = () => 2;
        layerController.domainSwitch();
        expect("raster" in controllers.current_display.getValue()).toEqual(true);
        expect("test_base/raster test 2" in globalMap).toEqual(true);
        expect("test_base/raster test" in globalMap).toEqual(false);
    });

    test('Layer Controller should clear selected layers when domain is switched to new simulation', () => {
        const rasterDict = layerController.rasterDict;
        layerController.handleOverlayadd("raster", rasterDict["raster"]);
        controllers.currentSimulation.getValue = () => "new simulation";
        layerController.domainSwitch();
        expect(controllers.current_display.getValue()).toEqual({});
        expect(globalMap).toEqual({});
    });

    test('Layer Controller should show the current_timestamp', () => {
        controllers.current_timestamp.getValue = () => "2021";
        layerController.domainSwitch();
        const rasterDict = layerController.rasterDict;
        layerController.handleOverlayadd("raster", rasterDict["raster"]);
        expect("test_base/raster test current timestamp" in globalMap).toEqual(true);
    });
});

describe('Tests for adding layers with colorbars', () => {
    var layerController;

    beforeEach(async () => {
        controllers.currentDomain.getValue = () => 1;
        controllers.current_timestamp.getValue = () => "2020";
        const div = document.createElement("div");
        div.id = "raster-colorbar";
        await document.body.appendChild(div);
        layerController = await document.body.appendChild(new LayerController());
        layerController.domainSwitch();
    });

    test('Layer Controller should add any colorbars', () => {
        const rasterDict = layerController.rasterDict;
        layerController.handleOverlayadd("raster", rasterDict["raster"]);
        expect(layerController.displayedColorbar).toEqual("raster");
    });

    test('Layer Controller should remove colorbars when layer deselected', () => {
        const rasterDict = layerController.rasterDict;
        layerController.handleOverlayadd("raster", rasterDict["raster"]);
        layerController.handleOverlayRemove("raster", rasterDict["raster"]);
        expect(layerController.displayedColorbar).toEqual(null);
    });

    test('Layer Controller should put most recent selected colorbar on top', () => {
        const rasterDict = layerController.rasterDict;
        const overlayDict = layerController.overlayDict;
        layerController.handleOverlayadd("raster", rasterDict["raster"]);
        layerController.handleOverlayadd("overlay", overlayDict["overlay"]);
        expect(layerController.displayedColorbar).toEqual("overlay");
    });

    test('Layer Controller should put last selected colorbar on top when another is deselected', () => {
        const rasterDict = layerController.rasterDict;
        const overlayDict = layerController.overlayDict;
        layerController.handleOverlayadd("raster", rasterDict["raster"]);
        layerController.handleOverlayadd("overlay", overlayDict["overlay"]);
        layerController.handleOverlayRemove("overlay", overlayDict["overlay"]);
        expect(layerController.displayedColorbar).toEqual("raster");
    });

    test('Layer Controller should remove colorbar when switching to a domain without one', () => {
        const rasterDict = layerController.rasterDict;
        layerController.handleOverlayadd("raster", rasterDict["raster"]);
        controllers.currentDomain.getValue = () => 2;
        layerController.domainSwitch();
        expect(layerController.displayedColorbar).toEqual(null);
    });
});<|endoftext|>


<commit_before>/**
 * React CSV Spreadsheet v1.1.0
 * David Timmons (github@timmons.io)
 * http://david.timmons.io
 * MIT License
 */

import React from 'react';
import Cell from './Cell.js';

// Spreadsheet header row intended for column titles.
var HeaderRow = React.createClass({
  propTypes: {
    saveChange: React.PropTypes.func.isRequired,
    csv: React.PropTypes.arrayOf(React.PropTypes.array.isRequired)
  },
  render: function () {
    var cells = Array(this.props.numCols).fill(0).map((val, i) =>

        <Cell csv={this.props.csv[0][i]} classHead="bg-lighten-1 bold" />

    );
    return (

          {cells}

    );
  }
});

export default HeaderRow;
<commit_msg>Cleaned up the HeaderRow class format.<commit_after>/**
 * React CSV Spreadsheet v1.1.0
 * David Timmons (github@timmons.io)
 * http://david.timmons.io
 * MIT License
 *
 * @summary A component controlling the table header.
 * @module ReactCsv/HeaderRow
 */

import React from 'react';
import Cell from './Cell.js';

/**
 * Spreadsheet header row intended for column titles.
 * @extends React.Component
 */
export default class HeaderRow extends React.Component {

  constructor(props) {
    super(props);
  }

  /**
   * Default React render function.
   * @return {object} A reference to the DOM component.
   */
  render() {
    var cells = Array(this.props.numCols).fill(0).map((val, i) =>

        <Cell csv={this.props.csv[0][i]} classHead="bg-lighten-1 bold" />

    );
    return (

          {cells}

    );
  }
}

/**
 * Restrict the property types.
 * @type {object}
 * @memberof HeaderRow
 */
HeaderRow.propTypes = {
  saveChange: React.PropTypes.func.isRequired,
  csv: React.PropTypes.arrayOf(React.PropTypes.array.isRequired)
};
<|endoftext|>