<commit_before>/**
 *
 * SignUpPage
 *
 */

import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import { FormattedMessage } from 'react-intl';
import { createStructuredSelector } from 'reselect';
import { compose } from 'redux';
import { CardActions, CardTitle, CardText } from 'material-ui/Card';
import RaisedButton from 'material-ui/RaisedButton';
import FlatButton from 'material-ui/FlatButton';
import TextField from 'material-ui/TextField';
import Divider from 'material-ui/Divider';

import Head from 'components/Head';
import UserFlowCard from 'components/UserFlowCard';
import UserFlowNavigation from 'components/UserFlowNavigation';
import Notification from 'components/Notification';
import injectSaga from 'utils/injectSaga';
import injectReducer from 'utils/injectReducer';

import { makeSelectLoading, makeSelectError } from './selectors';
import reducer from './reducer';
import saga from './saga';
import messages from './messages';
import { signUpRequest } from './actions';
import LegalNotice from './components/LegalNotice';

export class SignUpPage extends React.Component { // eslint-disable-line react/prefer-stateless-function
  constructor() {
    super();
    this.state = {
      email: '',
      displayName: '',
      password: '',
    };
  }

  onFieldChanged = (event) => {
    this.setState({ [event.target.name]: event.target.value });
  }

  onSubmit = (event) => {
    event.preventDefault();

    if (!this.props.loading) {
      this.props.signUp(this.state);
    }
  }

  errorMessage = () => {
    if (!messages[this.props.error]) {
      return this.props.error;
    }

    return <FormattedMessage {...messages[this.props.error]} />;
  }

  render() {
    return (

        <UserFlowCard>
          <CardTitle
            title={<FormattedMessage {...messages.title} />}
            subtitle={<FormattedMessage {...messages.subtitle} />}
          />

          <Divider />

          <CardText>
            <TextField
              fullWidth
              name="email"
              type="email"
              floatingLabelText={<FormattedMessage {...messages.email} />}
              onChange={this.onFieldChanged}
            />
            <TextField
              fullWidth
              name="displayName"
              type="text"
              floatingLabelText={<FormattedMessage {...messages.displayName} />}
              onChange={this.onFieldChanged}
            />
            <TextField
              fullWidth
              name="password"
              type="password"
              floatingLabelText={<FormattedMessage {...messages.password} />}
              onChange={this.onFieldChanged}
            />
          </CardText>

          <LegalNotice>
            <FormattedMessage {...messages.legal} />
          </LegalNotice>

          <CardActions>
            <RaisedButton
              primary
              fullWidth
              label={<FormattedMessage {...messages.title} />}
              onClick={this.onSubmit}
            />
          </CardActions>

          <Divider />

          <UserFlowNavigation>
            <FlatButton
              disabled
              label={<FormattedMessage {...messages.alreadySignedUp} />}
              containerElement={<Link to="/login" />}
            />
            <FlatButton
              primary
              label={<FormattedMessage {...messages.login} />}
              containerElement={<Link to="/login" />}
            />
          </UserFlowNavigation>
        </UserFlowCard>

        <Notification
          watcher={this.props.error}
          message={this.errorMessage()}
        />

    );
  }
}

SignUpPage.propTypes = {
  signUp: PropTypes.func.isRequired,
  loading: PropTypes.bool.isRequired,
  error: PropTypes.string.isRequired,
};

const mapStateToProps = createStructuredSelector({
  loading: makeSelectLoading(),
  error: makeSelectError(),
});

function mapDispatchToProps(dispatch) {
  return {
    signUp: (props) => dispatch(signUpRequest(props)),
  };
}

const withConnect = connect(mapStateToProps, mapDispatchToProps);

const withReducer = injectReducer({ key: 'signUpPage', reducer });
const withSaga = injectSaga({ key: 'signUpPage', saga });

export default compose(
  withReducer,
  withSaga,
  withConnect,
)(SignUpPage);
<commit_msg>Sign Up: remove link from text<commit_after>/**
 *
 * SignUpPage
 *
 */

import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import { FormattedMessage } from 'react-intl';
import { createStructuredSelector } from 'reselect';
import { compose } from 'redux';
import { CardActions, CardTitle, CardText } from 'material-ui/Card';
import RaisedButton from 'material-ui/RaisedButton';
import FlatButton from 'material-ui/FlatButton';
import TextField from 'material-ui/TextField';
import Divider from 'material-ui/Divider';

import Head from 'components/Head';
import UserFlowCard from 'components/UserFlowCard';
import UserFlowNavigation from 'components/UserFlowNavigation';
import Notification from 'components/Notification';
import injectSaga from 'utils/injectSaga';
import injectReducer from 'utils/injectReducer';

import { makeSelectLoading, makeSelectError } from './selectors';
import reducer from './reducer';
import saga from './saga';
import messages from './messages';
import { signUpRequest } from './actions';
import LegalNotice from './components/LegalNotice';

export class SignUpPage extends React.Component { // eslint-disable-line react/prefer-stateless-function
  constructor() {
    super();
    this.state = {
      email: '',
      displayName: '',
      password: '',
    };
  }

  onFieldChanged = (event) => {
    this.setState({ [event.target.name]: event.target.value });
  }

  onSubmit = (event) => {
    event.preventDefault();

    if (!this.props.loading) {
      this.props.signUp(this.state);
    }
  }

  errorMessage = () => {
    if (!messages[this.props.error]) {
      return this.props.error;
    }

    return <FormattedMessage {...messages[this.props.error]} />;
  }

  render() {
    return (

        <UserFlowCard>
          <CardTitle
            title={<FormattedMessage {...messages.title} />}
            subtitle={<FormattedMessage {...messages.subtitle} />}
          />

          <Divider />

          <CardText>
            <TextField
              fullWidth
              name="email"
              type="email"
              floatingLabelText={<FormattedMessage {...messages.email} />}
              onChange={this.onFieldChanged}
            />
            <TextField
              fullWidth
              name="displayName"
              type="text"
              floatingLabelText={<FormattedMessage {...messages.displayName} />}
              onChange={this.onFieldChanged}
            />
            <TextField
              fullWidth
              name="password"
              type="password"
              floatingLabelText={<FormattedMessage {...messages.password} />}
              onChange={this.onFieldChanged}
            />
          </CardText>

          <LegalNotice>
            <FormattedMessage {...messages.legal} />
          </LegalNotice>

          <CardActions>
            <RaisedButton
              primary
              fullWidth
              label={<FormattedMessage {...messages.title} />}
              onClick={this.onSubmit}
            />
          </CardActions>

          <Divider />

          <UserFlowNavigation>
            <FlatButton
              disabled
              label={<FormattedMessage {...messages.alreadySignedUp} />}
            />
            <FlatButton
              primary
              label={<FormattedMessage {...messages.login} />}
              containerElement={<Link to="/login" />}
            />
          </UserFlowNavigation>
        </UserFlowCard>

        <Notification
          watcher={this.props.error}
          message={this.errorMessage()}
        />

    );
  }
}

SignUpPage.propTypes = {
  signUp: PropTypes.func.isRequired,
  loading: PropTypes.bool.isRequired,
  error: PropTypes.string.isRequired,
};

const mapStateToProps = createStructuredSelector({
  loading: makeSelectLoading(),
  error: makeSelectError(),
});

function mapDispatchToProps(dispatch) {
  return {
    signUp: (props) => dispatch(signUpRequest(props)),
  };
}

const withConnect = connect(mapStateToProps, mapDispatchToProps);

const withReducer = injectReducer({ key: 'signUpPage', reducer });
const withSaga = injectSaga({ key: 'signUpPage', saga });

export default compose(
  withReducer,
  withSaga,
  withConnect,
)(SignUpPage);
<|endoftext|>


<commit_before>'use strict';

const userMiddleware = require('../middlewares/user');
const errorMiddleware = require('../middlewares/error');
const authorizationMiddleware = require('../middlewares/authorization');
const connectionParamsMiddleware = require('../middlewares/connection-params');
const timeoutLimitsMiddleware = require('../middlewares/timeout-limits');
const { initializeProfilerMiddleware } = require('../middlewares/profiler');
const rateLimitsMiddleware = require('../middlewares/rate-limit');
const { RATE_LIMIT_ENDPOINTS_GROUPS } = rateLimitsMiddleware;
const errorHandlerFactory = require('../services/error_handler_factory');
const streamCopy = require('../services/stream_copy');

function CopyController(metadataBackend, userDatabaseService, userLimitsService, logger) {
    this.metadataBackend = metadataBackend;
    this.userDatabaseService = userDatabaseService;
    this.userLimitsService = userLimitsService;
    this.logger = logger;
}

CopyController.prototype.route = function (app) {
    const { base_url } = global.settings;

    const copyFromMiddlewares = endpointGroup => {
        return [
            initializeProfilerMiddleware('copyfrom'),
            userMiddleware(this.metadataBackend),
            rateLimitsMiddleware(this.userLimitsService, endpointGroup),
            authorizationMiddleware(this.metadataBackend),
            connectionParamsMiddleware(this.userDatabaseService),
            timeoutLimitsMiddleware(this.metadataBackend),
            validateCopyQuery(),
            handleCopyFrom(this.logger),
            errorHandler(),
            errorMiddleware()
        ];
    };

    const copyToMiddlewares = endpointGroup => {
        return [
            initializeProfilerMiddleware('copyto'),
            userMiddleware(this.metadataBackend),
            rateLimitsMiddleware(this.userLimitsService, endpointGroup),
            authorizationMiddleware(this.metadataBackend),
            connectionParamsMiddleware(this.userDatabaseService),
            timeoutLimitsMiddleware(this.metadataBackend),
            validateCopyQuery(),
            handleCopyTo(this.logger),
            errorHandler(),
            errorMiddleware()
        ];
    };

    app.post(`${base_url}/sql/copyfrom`, copyFromMiddlewares(RATE_LIMIT_ENDPOINTS_GROUPS.COPY_FROM));
    app.get(`${base_url}/sql/copyto`, copyToMiddlewares(RATE_LIMIT_ENDPOINTS_GROUPS.COPY_TO));
};

function handleCopyTo (logger) {
    return function handleCopyToMiddleware (req, res, next) {
        const filename = req.query.filename || 'carto-sql-copyto.dmp';

        res.header("Content-Disposition", `attachment; filename=${encodeURIComponent(filename)}`);
        res.header("Content-Type", "application/octet-stream");

        streamCopy.to(
            res,
            req.query.q,
            res.locals.userDbParams,
            res.locals.user,
            logger,
            function(err) {
                if (err) {
                    return next(err);
                }

                // this is a especial endpoint
                // the data from postgres is streamed to response directly
            }
        );
    };
}

function handleCopyFrom (logger) {
    return function handleCopyFromMiddleware (req, res, next) {
        streamCopy.from(
            req,
            req.query.q,
            res.locals.userDbParams,
            res.locals.user,
            req.get('content-encoding') === 'gzip',
            logger,
            function(err, metrics) {
                if (err) {
                    return next(err);
                }

                // TODO: change when data-ingestion log works: const { time, rows } = metrics;
                const { time, rows, type, format, isGzip, size } = metrics;
                if (!rows) {
                    return next(new Error("No rows copied"));
                }

                // TODO: remove when data-ingestion log works
                if (req.profiler) {
                    req.profiler.add({copyFrom: { type, format, gzip: isGzip, size, rows, time }});
                    res.header('X-SQLAPI-Profiler', req.profiler.toJSONString());
                }

                res.send({
                    time,
                    total_rows: rows
                });
            }
        );
    };
}

function validateCopyQuery () {
    return function validateCopyQueryMiddleware (req, res, next) {
        const sql = req.query.q;

        if (!sql) {
            return next(new Error("SQL is missing"));
        }

        // Only accept SQL that starts with 'COPY'
        if (!sql.toUpperCase().startsWith("COPY ")) {
            return next(new Error("SQL must start with COPY"));
        }

        next();
    };
}

function errorHandler () {
    return function errorHandlerMiddleware (err, req, res, next) {
        if (res.headersSent) {
            console.error("EXCEPTION REPORT: " + err.stack);
            const errorHandler = errorHandlerFactory(err);
            res.write(JSON.stringify(errorHandler.getResponse()));
            res.end();
        } else {
            return next(err);
        }
    };
}

module.exports = CopyController;
<commit_msg> trailing white spaces<commit_after>'use strict';

const userMiddleware = require('../middlewares/user');
const errorMiddleware = require('../middlewares/error');
const authorizationMiddleware = require('../middlewares/authorization');
const connectionParamsMiddleware = require('../middlewares/connection-params');
const timeoutLimitsMiddleware = require('../middlewares/timeout-limits');
const { initializeProfilerMiddleware } = require('../middlewares/profiler');
const rateLimitsMiddleware = require('../middlewares/rate-limit');
const { RATE_LIMIT_ENDPOINTS_GROUPS } = rateLimitsMiddleware;
const errorHandlerFactory = require('../services/error_handler_factory');
const streamCopy = require('../services/stream_copy');

function CopyController(metadataBackend, userDatabaseService, userLimitsService, logger) {
    this.metadataBackend = metadataBackend;
    this.userDatabaseService = userDatabaseService;
    this.userLimitsService = userLimitsService;
    this.logger = logger;
}

CopyController.prototype.route = function (app) {
    const { base_url } = global.settings;

    const copyFromMiddlewares = endpointGroup => {
        return [
            initializeProfilerMiddleware('copyfrom'),
            userMiddleware(this.metadataBackend),
            rateLimitsMiddleware(this.userLimitsService, endpointGroup),
            authorizationMiddleware(this.metadataBackend),
            connectionParamsMiddleware(this.userDatabaseService),
            timeoutLimitsMiddleware(this.metadataBackend),
            validateCopyQuery(),
            handleCopyFrom(this.logger),
            errorHandler(),
            errorMiddleware()
        ];
    };

    const copyToMiddlewares = endpointGroup => {
        return [
            initializeProfilerMiddleware('copyto'),
            userMiddleware(this.metadataBackend),
            rateLimitsMiddleware(this.userLimitsService, endpointGroup),
            authorizationMiddleware(this.metadataBackend),
            connectionParamsMiddleware(this.userDatabaseService),
            timeoutLimitsMiddleware(this.metadataBackend),
            validateCopyQuery(),
            handleCopyTo(this.logger),
            errorHandler(),
            errorMiddleware()
        ];
    };

    app.post(`${base_url}/sql/copyfrom`, copyFromMiddlewares(RATE_LIMIT_ENDPOINTS_GROUPS.COPY_FROM));
    app.get(`${base_url}/sql/copyto`, copyToMiddlewares(RATE_LIMIT_ENDPOINTS_GROUPS.COPY_TO));
};

function handleCopyTo (logger) {
    return function handleCopyToMiddleware (req, res, next) {
        const filename = req.query.filename || 'carto-sql-copyto.dmp';

        res.header("Content-Disposition", `attachment; filename=${encodeURIComponent(filename)}`);
        res.header("Content-Type", "application/octet-stream");

        streamCopy.to(
            res,
            req.query.q,
            res.locals.userDbParams,
            res.locals.user,
            logger,
            function(err) {
                if (err) {
                    return next(err);
                }

                // this is a especial endpoint
                // the data from postgres is streamed to response directly
            }
        );
    };
}

function handleCopyFrom (logger) {
    return function handleCopyFromMiddleware (req, res, next) {
        streamCopy.from(
            req,
            req.query.q,
            res.locals.userDbParams,
            res.locals.user,
            req.get('content-encoding') === 'gzip',
            logger,
            function(err, metrics) {
                if (err) {
                    return next(err);
                }

                // TODO: change when data-ingestion log works: const { time, rows } = metrics;
                const { time, rows, type, format, isGzip, size } = metrics;
                if (!rows) {
                    return next(new Error("No rows copied"));
                }

                // TODO: remove when data-ingestion log works
                if (req.profiler) {
                    req.profiler.add({copyFrom: { type, format, gzip: isGzip, size, rows, time }});
                    res.header('X-SQLAPI-Profiler', req.profiler.toJSONString());
                }

                res.send({
                    time,
                    total_rows: rows
                });
            }
        );
    };
}

function validateCopyQuery () {
    return function validateCopyQueryMiddleware (req, res, next) {
        const sql = req.query.q;

        if (!sql) {
            return next(new Error("SQL is missing"));
        }

        // Only accept SQL that starts with 'COPY'
        if (!sql.toUpperCase().startsWith("COPY ")) {
            return next(new Error("SQL must start with COPY"));
        }

        next();
    };
}

function errorHandler () {
    return function errorHandlerMiddleware (err, req, res, next) {
        if (res.headersSent) {
            console.error("EXCEPTION REPORT: " + err.stack);
            const errorHandler = errorHandlerFactory(err);
            res.write(JSON.stringify(errorHandler.getResponse()));
            res.end();
        } else {
            return next(err);
        }
    };
}

module.exports = CopyController;
<|endoftext|>


<commit_before>/**
 * Created by ksinger on 19.02.2016.
 */

import won from "../won-es6.js";
import Immutable from "immutable";
import { actionTypes, actionCreators } from "./actions.js";
import { fetchOwnedData } from "../won-message-utils.js";
import {
  registerAccount,
  transferPrivateAccount,
  acceptTermsOfService,
  confirmRegistration,
  resendEmailVerification,
  login,
  logout,
  parseCredentials,
  generatePrivateId,
  checkLoginStatus,
} from "../won-utils.js";
import {
  clearPrivateId,
  savePrivateId,
  setDisclaimerAccepted,
} from "../won-localstorage.js";
import { stateGoCurrent } from "./cstm-router-actions.js";
import { checkAccessToCurrentRoute } from "../configRouting.js";

import { getIn } from "../utils.js";
import { getOwnedConnectionUris } from "../selectors/connection-selectors.js";
import { loadLatestMessagesOfConnection } from "./connections-actions.js";

/**
 * Makes sure user is either logged in
 * or creates a private-ID account as fallback.
 */
export async function ensureLoggedIn(dispatch, getState) {
  const state = getState();
  if (state.getIn(["account", "loggedIn"])) {
    return;
  }

  const privateId = generatePrivateId();
  try {
    await accountRegister({ privateId })(dispatch, getState);
  } catch (err) {
    console.error(
      `Creating temporary account (${privateId}) has failed due to `,
      err
    );
    dispatch(actionCreators.account__registerFailed({ privateId }));
  }
}

let _loginInProcessFor;
/**
 *
 * @param username
 * @param password
 * @param options
 *    * fetchData(true): whether or not to fetch a users owned needs. If the account
 *    signing in is new, there's no need to fetch this and `false` can be passed here
 *    * doRedirects(true): whether or not to do any redirects at all (e.g. if an invalid route was accessed)
 *    * redirectToFeed(false): whether or not to redirect to the feed after signing in (needs `redirects` to be true)
 *    * relogIfNecessary(true):  if there's a valid session or privateId, log out from that first.
 *
 * @param credentials either {email, password} or {privateId}
 * @returns {Function}
 */
export function accountLogin(credentials, options) {
  const options_ = Object.assign(
    {
      // defaults
      fetchData: true,
      doRedirects: true,
      redirectToFeed: false,
      relogIfNecessary: true, // if there's a valid session or privateId, log out from that first.
    },
    options
  );
  return (dispatch, getState) => {
    const state = getState();

    const { email } = parseCredentials(credentials);

    const prevPrivateId = getIn(state, [
      "router",
      "currentParams",
      "privateId",
    ]);
    const prevEmail = state.getIn(["account", "email"]);

    const wasLoggedIn =
      !state.getIn(["process", "processingInitialLoad"]) &&
      (prevPrivateId || prevEmail);

    if (
      state.getIn(["process", "processingLoginForEmail"]) === email ||
      _loginInProcessFor === email
    ) {
      console.debug(
        "Already logging in as ",
        email,
        ". Canceling redundant attempt."
      );
      return;
    }

    if (
      !state.getIn(["process", "processingInitialLoad"]) &&
      ((credentials.privateId && credentials.privateId === prevPrivateId) ||
        (credentials.email && credentials.email === prevEmail))
    ) {
      console.debug(
        "Already logged into this account (" +
          (credentials.privateId || credentials.email) +
          "). Aborting second login attempt."
      );
      return;
    }

    return Promise.resolve()
      .then(() => {
        _loginInProcessFor = email;
        return dispatch({
          type: actionTypes.account.loginStarted,
          payload: { email },
        });
      })
      .then(() => {
        if (wasLoggedIn) {
          return logout().then(() => {
            if (
              options_.doRedirects &&
              getIn(state, ["router", "currentParams", "privateId"])
            ) {
              return stateGoCurrent({ privateId: "" })(dispatch, getState);
            }
          });
        }
      })
      .then(() => {
        if (options_.doRedirects && credentials.privateId) {
          return stateGoCurrent({ privateId: credentials.privateId })(
            dispatch,
            getState
          );
        }
      })
      .then(() => login(credentials))
      .then(data =>
        dispatch({
          type: actionTypes.account.login,
          payload: Immutable.fromJS(data).merge({
            email: email,
            loggedIn: true,
          }),
        })
      )
      .then(() => dispatch({ type: actionTypes.upgradeHttpSession }))
      .then(() => {
        if (!options_.doRedirects) {
          return;
        } else if (options_.redirectToFeed) {
          return dispatch(
            actionCreators.router__stateGoResetParams("connections")
          );
        } else {
          return checkAccessToCurrentRoute(dispatch, getState);
        }
      })
      .then((/*response*/) => {
        if (options_.fetchData) {
          return fetchOwnedData(email, dispatch);
        } else {
          return Immutable.Map(); // only need to fetch data for non-new accounts
        }
      })
      .then(() => dispatch({ type: actionTypes.loginFinished }))
      .catch(error =>
        error.response.json().then(loginError => {
          return Promise.resolve()
            .then(() => {
              if (wasLoggedIn) {
                return dispatch({
                  type: actionTypes.account.logout,
                  payload: Immutable.fromJS({ loggedIn: false }),
                });
              }
            })
            .then(() => {
              if (credentials.privateId) {
                loginError = won.PRIVATEID_NOT_FOUND_ERROR;
              }

              dispatch(
                actionCreators.account__loginFailed({
                  loginError: Immutable.fromJS(loginError),
                  error,
                  credentials,
                })
              );
            })
            .then(
              () =>
                options_.doRedirects &&
                checkAccessToCurrentRoute(dispatch, getState)
            );
        })
      )
      .then(() => {
        _loginInProcessFor = undefined;
      })
      .then(() => {
        if (credentials.privateId) {
          savePrivateId(credentials.privateId);
        }
      });
  };
}

let _logoutInProcess;
/**
 *
 * @param options
 *    * doRedirects(true): whether or not to do any redirects at all (e.g. if an invalid route was accessed)
 *
 * @returns {Function}
 */
export function accountLogout(options) {
  const options_ = {
    doRedirects: true,
    ...options,
  };

  clearPrivateId();

  return (dispatch, getState) => {
    const state = getState();

    if (state.getIn(["process", "processingLogout"]) || _logoutInProcess) {
      console.debug(
        "There's already a logout in process. Aborting redundant attempt."
      );
      return;
    }
    _logoutInProcess = true;

    return Promise.resolve()
      .then(() =>
        dispatch({
          type: actionTypes.account.logoutStarted,
          payload: {},
        })
      )
      .then(() => logout())
      .catch(error => {
        //TODO: PRINT ERROR MESSAGE AND CHANGE STATE ACCORDINGLY
        console.error("Error while trying to log out: ", error);
      })
      .then(() => {
        // for the case that we've been logged in to an anonymous account, we need to remove the privateId here.
        if (
          options_.doRedirects &&
          getIn(state, ["router", "currentParams", "privateId"])
        ) {
          return stateGoCurrent({ privateId: null })(dispatch, getState);
        }
      })
      .then(() =>
        dispatch({
          type: actionTypes.account.logout,
          payload: Immutable.fromJS({
            loggedIn: false,
            httpSessionDowngraded: true,
          }),
        })
      )
      .then(() => {
        won.clearStore();
      })
      .then(
        () =>
          options_.doRedirects && checkAccessToCurrentRoute(dispatch, getState)
      )
      .then(() => {
        _logoutInProcess = false;
      })
      .then(() =>
        dispatch({
          type: actionTypes.account.logout,
          payload: Immutable.fromJS({ loggedIn: false, logoutFinished: true }),
        })
      );
  };
}

/**
 * @param credentials either {email, password} or {privateId}
 * @returns {Function}
 */
export function accountRegister(credentials) {
  return (dispatch, getState) =>
    registerAccount(credentials)
      .then(() =>
        /*response*/ accountLogin(credentials, {
          fetchData: false,
          redirectToFeed: true,
        })(dispatch, getState)
      )
      .catch(error => {
        //TODO: PRINT MORE SPECIFIC ERROR MESSAGE, already registered/password to short etc.
        const registerError =
          "Registration failed (E-Mail might already be used)";
        console.error(registerError, error);
        dispatch(
          actionCreators.account__registerFailed({ registerError, error })
        );
      });
}

/**
 * @param credentials {email, password, privateId}
 * @returns {Function}
 */
export function accountTransfer(credentials) {
  return (dispatch, getState) =>
    transferPrivateAccount(credentials)
      .then(() => {
        credentials.privateId = undefined;
        /*response*/
        accountLogin(credentials, {
          fetchData: true,
          redirectToFeed: true,
        })(dispatch, getState);
      })
      .catch(error => {
        //TODO: PRINT MORE SPECIFIC ERROR MESSAGE, already registered/password to short etc.
        const registerError = "Account Transfer failed";
        console.error(registerError, error);
        dispatch(
          actionCreators.account__registerFailed({ registerError, error })
        );
      });
}

export function accountAcceptDisclaimer() {
  return dispatch => {
    setDisclaimerAccepted();
    dispatch({ type: actionTypes.account.acceptDisclaimerSuccess });
  };
}

export function accountAcceptTermsOfService() {
  return dispatch => {
    dispatch({ type: actionTypes.account.acceptTermsOfServiceStarted });
    acceptTermsOfService()
      .then(() => {
        dispatch({ type: actionTypes.account.acceptTermsOfServiceSuccess });
      })
      .catch(() => {
        dispatch({ type: actionTypes.account.acceptTermsOfServiceFailed });
      });
  };
}

export function accountVerifyEmailAddress(verificationToken) {
  return dispatch => {
    dispatch({ type: actionTypes.account.verifyEmailAddressStarted });
    confirmRegistration(verificationToken)
      .then(() => {
        dispatch({ type: actionTypes.account.verifyEmailAddressSuccess });
      })
      .catch(error =>
        dispatch({
          type: actionTypes.account.verifyEmailAddressFailed,
          payload: {
            emailVerificationError: Immutable.fromJS(error.jsonResponse),
          },
        })
      );
  };
}

export function accountResendVerificationEmail(email) {
  return dispatch => {
    dispatch({ type: actionTypes.account.resendVerificationEmailStarted });
    resendEmailVerification(email)
      .then(() => {
        dispatch({ type: actionTypes.account.resendVerificationEmailSuccess });
      })
      .catch(error =>
        dispatch({
          type: actionTypes.account.resendVerificationEmailFailed,
          payload: {
            emailVerificationError: Immutable.fromJS(error.jsonResponse),
          },
        })
      );
  };
}

export function reconnect() {
  return async (dispatch, getState) => {
    dispatch({ type: actionTypes.reconnect.start });
    try {
      await checkLoginStatus();
      dispatch({ type: actionTypes.reconnect.success });

      const state = getState();

      /*
      * -- check for new connections (i.e. matches and incoming requests) --
      */
      // await pageLoadAction()(dispatch, getState);
      // const email = getIn(state, ["account", "email"]);
      // await fetchOwnedData(email, payload => {
      //   dispatch({
      //     type: actionTypes.initialPageLoad, // TODO make this it's own type
      //     payload: wellFormedPayload(payload),
      //   });
      // });
      //TODO hard reload

      /*
       * -- loading latest messages for all connections (we might have missed some during the dc) --
       */
      const connectionUris = getOwnedConnectionUris(state);
      await Promise.all(
        connectionUris.map(async connectionUri => {
          await loadLatestMessagesOfConnection({
            connectionUri,
            numberOfEvents: 10, //TODO magic number :|
            state,
            dispatch,
            actionTypesToDispatch: {
              start: actionTypes.reconnect.startingToLoadConnectionData,
              success: actionTypes.reconnect.receivedConnectionData,
              failure: actionTypes.reconnect.connectionFailedToLoad,
            },
          });
        })
      );
    } catch (e) {
      if (e.message == "Unauthorized") {
        //FIXME: this seems weird and unintentional to me, the actionTypes.account.logout closes the main menu (see view-reducer.js) and the dispatch after opens it again, is this wanted that way?
        dispatch({ type: actionTypes.account.logout });
        dispatch({ type: actionTypes.view.showMainMenu });
      } else {
        dispatch(actionCreators.lostConnection());
      }
      console.warn(e);
    }
  };
}
<commit_msg>Fixes actionTypes mismatch -> forgot the account prefix<commit_after>/**
 * Created by ksinger on 19.02.2016.
 */

import won from "../won-es6.js";
import Immutable from "immutable";
import { actionTypes, actionCreators } from "./actions.js";
import { fetchOwnedData } from "../won-message-utils.js";
import {
  registerAccount,
  transferPrivateAccount,
  acceptTermsOfService,
  confirmRegistration,
  resendEmailVerification,
  login,
  logout,
  parseCredentials,
  generatePrivateId,
  checkLoginStatus,
} from "../won-utils.js";
import {
  clearPrivateId,
  savePrivateId,
  setDisclaimerAccepted,
} from "../won-localstorage.js";
import { stateGoCurrent } from "./cstm-router-actions.js";
import { checkAccessToCurrentRoute } from "../configRouting.js";

import { getIn } from "../utils.js";
import { getOwnedConnectionUris } from "../selectors/connection-selectors.js";
import { loadLatestMessagesOfConnection } from "./connections-actions.js";

/**
 * Makes sure user is either logged in
 * or creates a private-ID account as fallback.
 */
export async function ensureLoggedIn(dispatch, getState) {
  const state = getState();
  if (state.getIn(["account", "loggedIn"])) {
    return;
  }

  const privateId = generatePrivateId();
  try {
    await accountRegister({ privateId })(dispatch, getState);
  } catch (err) {
    console.error(
      `Creating temporary account (${privateId}) has failed due to `,
      err
    );
    dispatch(actionCreators.account__registerFailed({ privateId }));
  }
}

let _loginInProcessFor;
/**
 *
 * @param username
 * @param password
 * @param options
 *    * fetchData(true): whether or not to fetch a users owned needs. If the account
 *    signing in is new, there's no need to fetch this and `false` can be passed here
 *    * doRedirects(true): whether or not to do any redirects at all (e.g. if an invalid route was accessed)
 *    * redirectToFeed(false): whether or not to redirect to the feed after signing in (needs `redirects` to be true)
 *    * relogIfNecessary(true):  if there's a valid session or privateId, log out from that first.
 *
 * @param credentials either {email, password} or {privateId}
 * @returns {Function}
 */
export function accountLogin(credentials, options) {
  const options_ = Object.assign(
    {
      // defaults
      fetchData: true,
      doRedirects: true,
      redirectToFeed: false,
      relogIfNecessary: true, // if there's a valid session or privateId, log out from that first.
    },
    options
  );
  return (dispatch, getState) => {
    const state = getState();

    const { email } = parseCredentials(credentials);

    const prevPrivateId = getIn(state, [
      "router",
      "currentParams",
      "privateId",
    ]);
    const prevEmail = state.getIn(["account", "email"]);

    const wasLoggedIn =
      !state.getIn(["process", "processingInitialLoad"]) &&
      (prevPrivateId || prevEmail);

    if (
      state.getIn(["process", "processingLoginForEmail"]) === email ||
      _loginInProcessFor === email
    ) {
      console.debug(
        "Already logging in as ",
        email,
        ". Canceling redundant attempt."
      );
      return;
    }

    if (
      !state.getIn(["process", "processingInitialLoad"]) &&
      ((credentials.privateId && credentials.privateId === prevPrivateId) ||
        (credentials.email && credentials.email === prevEmail))
    ) {
      console.debug(
        "Already logged into this account (" +
          (credentials.privateId || credentials.email) +
          "). Aborting second login attempt."
      );
      return;
    }

    return Promise.resolve()
      .then(() => {
        _loginInProcessFor = email;
        return dispatch({
          type: actionTypes.account.loginStarted,
          payload: { email },
        });
      })
      .then(() => {
        if (wasLoggedIn) {
          return logout().then(() => {
            if (
              options_.doRedirects &&
              getIn(state, ["router", "currentParams", "privateId"])
            ) {
              return stateGoCurrent({ privateId: "" })(dispatch, getState);
            }
          });
        }
      })
      .then(() => {
        if (options_.doRedirects && credentials.privateId) {
          return stateGoCurrent({ privateId: credentials.privateId })(
            dispatch,
            getState
          );
        }
      })
      .then(() => login(credentials))
      .then(data =>
        dispatch({
          type: actionTypes.account.login,
          payload: Immutable.fromJS(data).merge({
            email: email,
            loggedIn: true,
          }),
        })
      )
      .then(() => dispatch({ type: actionTypes.upgradeHttpSession }))
      .then(() => {
        if (!options_.doRedirects) {
          return;
        } else if (options_.redirectToFeed) {
          return dispatch(
            actionCreators.router__stateGoResetParams("connections")
          );
        } else {
          return checkAccessToCurrentRoute(dispatch, getState);
        }
      })
      .then((/*response*/) => {
        if (options_.fetchData) {
          return fetchOwnedData(email, dispatch);
        } else {
          return Immutable.Map(); // only need to fetch data for non-new accounts
        }
      })
      .then(() => dispatch({ type: actionTypes.account.loginFinished }))
      .catch(error =>
        error.response.json().then(loginError => {
          return Promise.resolve()
            .then(() => {
              if (wasLoggedIn) {
                return dispatch({
                  type: actionTypes.account.logout,
                  payload: Immutable.fromJS({ loggedIn: false }),
                });
              }
            })
            .then(() => {
              if (credentials.privateId) {
                loginError = won.PRIVATEID_NOT_FOUND_ERROR;
              }

              dispatch(
                actionCreators.account__loginFailed({
                  loginError: Immutable.fromJS(loginError),
                  error,
                  credentials,
                })
              );
            })
            .then(
              () =>
                options_.doRedirects &&
                checkAccessToCurrentRoute(dispatch, getState)
            );
        })
      )
      .then(() => {
        _loginInProcessFor = undefined;
      })
      .then(() => {
        if (credentials.privateId) {
          savePrivateId(credentials.privateId);
        }
      });
  };
}

let _logoutInProcess;
/**
 *
 * @param options
 *    * doRedirects(true): whether or not to do any redirects at all (e.g. if an invalid route was accessed)
 *
 * @returns {Function}
 */
export function accountLogout(options) {
  const options_ = {
    doRedirects: true,
    ...options,
  };

  clearPrivateId();

  return (dispatch, getState) => {
    const state = getState();

    if (state.getIn(["process", "processingLogout"]) || _logoutInProcess) {
      console.debug(
        "There's already a logout in process. Aborting redundant attempt."
      );
      return;
    }
    _logoutInProcess = true;

    return Promise.resolve()
      .then(() =>
        dispatch({
          type: actionTypes.account.logoutStarted,
          payload: {},
        })
      )
      .then(() => logout())
      .catch(error => {
        //TODO: PRINT ERROR MESSAGE AND CHANGE STATE ACCORDINGLY
        console.error("Error while trying to log out: ", error);
      })
      .then(() => {
        // for the case that we've been logged in to an anonymous account, we need to remove the privateId here.
        if (
          options_.doRedirects &&
          getIn(state, ["router", "currentParams", "privateId"])
        ) {
          return stateGoCurrent({ privateId: null })(dispatch, getState);
        }
      })
      .then(() =>
        dispatch({
          type: actionTypes.account.logout,
          payload: Immutable.fromJS({
            loggedIn: false,
            httpSessionDowngraded: true,
          }),
        })
      )
      .then(() => {
        won.clearStore();
      })
      .then(
        () =>
          options_.doRedirects && checkAccessToCurrentRoute(dispatch, getState)
      )
      .then(() => {
        _logoutInProcess = false;
      })
      .then(() =>
        dispatch({
          type: actionTypes.account.logout,
          payload: Immutable.fromJS({ loggedIn: false, logoutFinished: true }),
        })
      );
  };
}

/**
 * @param credentials either {email, password} or {privateId}
 * @returns {Function}
 */
export function accountRegister(credentials) {
  return (dispatch, getState) =>
    registerAccount(credentials)
      .then(() =>
        /*response*/ accountLogin(credentials, {
          fetchData: false,
          redirectToFeed: true,
        })(dispatch, getState)
      )
      .catch(error => {
        //TODO: PRINT MORE SPECIFIC ERROR MESSAGE, already registered/password to short etc.
        const registerError =
          "Registration failed (E-Mail might already be used)";
        console.error(registerError, error);
        dispatch(
          actionCreators.account__registerFailed({ registerError, error })
        );
      });
}

/**
 * @param credentials {email, password, privateId}
 * @returns {Function}
 */
export function accountTransfer(credentials) {
  return (dispatch, getState) =>
    transferPrivateAccount(credentials)
      .then(() => {
        credentials.privateId = undefined;
        /*response*/
        accountLogin(credentials, {
          fetchData: true,
          redirectToFeed: true,
        })(dispatch, getState);
      })
      .catch(error => {
        //TODO: PRINT MORE SPECIFIC ERROR MESSAGE, already registered/password to short etc.
        const registerError = "Account Transfer failed";
        console.error(registerError, error);
        dispatch(
          actionCreators.account__registerFailed({ registerError, error })
        );
      });
}

export function accountAcceptDisclaimer() {
  return dispatch => {
    setDisclaimerAccepted();
    dispatch({ type: actionTypes.account.acceptDisclaimerSuccess });
  };
}

export function accountAcceptTermsOfService() {
  return dispatch => {
    dispatch({ type: actionTypes.account.acceptTermsOfServiceStarted });
    acceptTermsOfService()
      .then(() => {
        dispatch({ type: actionTypes.account.acceptTermsOfServiceSuccess });
      })
      .catch(() => {
        dispatch({ type: actionTypes.account.acceptTermsOfServiceFailed });
      });
  };
}

export function accountVerifyEmailAddress(verificationToken) {
  return dispatch => {
    dispatch({ type: actionTypes.account.verifyEmailAddressStarted });
    confirmRegistration(verificationToken)
      .then(() => {
        dispatch({ type: actionTypes.account.verifyEmailAddressSuccess });
      })
      .catch(error =>
        dispatch({
          type: actionTypes.account.verifyEmailAddressFailed,
          payload: {
            emailVerificationError: Immutable.fromJS(error.jsonResponse),
          },
        })
      );
  };
}

export function accountResendVerificationEmail(email) {
  return dispatch => {
    dispatch({ type: actionTypes.account.resendVerificationEmailStarted });
    resendEmailVerification(email)
      .then(() => {
        dispatch({ type: actionTypes.account.resendVerificationEmailSuccess });
      })
      .catch(error =>
        dispatch({
          type: actionTypes.account.resendVerificationEmailFailed,
          payload: {
            emailVerificationError: Immutable.fromJS(error.jsonResponse),
          },
        })
      );
  };
}

export function reconnect() {
  return async (dispatch, getState) => {
    dispatch({ type: actionTypes.reconnect.start });
    try {
      await checkLoginStatus();
      dispatch({ type: actionTypes.reconnect.success });

      const state = getState();

      /*
      * -- check for new connections (i.e. matches and incoming requests) --
      */
      // await pageLoadAction()(dispatch, getState);
      // const email = getIn(state, ["account", "email"]);
      // await fetchOwnedData(email, payload => {
      //   dispatch({
      //     type: actionTypes.initialPageLoad, // TODO make this it's own type
      //     payload: wellFormedPayload(payload),
      //   });
      // });
      //TODO hard reload

      /*
       * -- loading latest messages for all connections (we might have missed some during the dc) --
       */
      const connectionUris = getOwnedConnectionUris(state);
      await Promise.all(
        connectionUris.map(async connectionUri => {
          await loadLatestMessagesOfConnection({
            connectionUri,
            numberOfEvents: 10, //TODO magic number :|
            state,
            dispatch,
            actionTypesToDispatch: {
              start: actionTypes.reconnect.startingToLoadConnectionData,
              success: actionTypes.reconnect.receivedConnectionData,
              failure: actionTypes.reconnect.connectionFailedToLoad,
            },
          });
        })
      );
    } catch (e) {
      if (e.message == "Unauthorized") {
        //FIXME: this seems weird and unintentional to me, the actionTypes.account.logout closes the main menu (see view-reducer.js) and the dispatch after opens it again, is this wanted that way?
        dispatch({ type: actionTypes.account.logout });
        dispatch({ type: actionTypes.view.showMainMenu });
      } else {
        dispatch(actionCreators.lostConnection());
      }
      console.warn(e);
    }
  };
}
<|endoftext|>


<commit_before>/*jslint unparam: true, browser: true, indent: 2 */

;(function ($, window, document, undefined) {
  'use strict';

  Foundation.libs = Foundation.libs || {};

  Foundation.libs.topbar = {
    version : '4.0.0.alpha',

    settings : {
      index : 0,
      init : false
    },

    init : function (scope, method, options) {
      this.scope = scope;

      if (typeof method === 'object') {
        $.extend(true, this.settings, method);
      }

      if (typeof method != 'string') {
        this.settings.$w = $(window);
        this.settings.$topbar = $('nav.top-bar');
        this.settings.$section = this.settings.$topbar.find('section');
        this.settings.$titlebar = this.settings.$topbar.children('ul').first();

        var breakpoint = $("
").appendTo("body");
        this.settings.breakPoint = breakpoint.width();
        breakpoint.remove();

        if (!this.settings.init) {
          this.events();
          this.assemble();
        }

        if (!this.settings.height) this.largestUL();

        if (this.settings.$topbar.parent().hasClass('fixed')) {
          $('body').css('padding-top', this.settings.$topbar.outerHeight());
        }

        return this.settings.init;
      } else {
        // fire method
        return this[method].call(this, options);
      }
    },

    events : function () {
      $(this.scope)
        .on('click.fndtn.topbar', '.top-bar .toggle-topbar', function (e) {
          e.preventDefault();

          if (this.breakpoint()) {
            this.settings.$topbar.toggleClass('expanded');
            this.settings.$topbar.css('min-height', '');
          }

          if (!this.settings.$topbar.hasClass('expanded')) {
            this.settings.$section.css({left: '0%'});
            this.settings.$section.find('>.name').css({left: '100%'});
            this.settings.$section.find('li.moved').removeClass('moved');
            this.settings.index = 0;
          }
        }.bind(this))

        .on('click.fndtn.topbar', '.top-bar .has-dropdown>a', function (e) {
          var self = Foundation.libs.topbar;

          if (Modernizr.touch || self.breakpoint())
            e.preventDefault();

          if (self.breakpoint()) {
            var $this = $(this),
                $selectedLi = $this.closest('li');

            self.settings.index += 1;
            $selectedLi.addClass('moved');
            self.settings.$section.css({left: -(100 * self.settings.index) + '%'});
            self.settings.$section.find('>.name').css({left: 100 * self.settings.index + '%'});

            $this.siblings('ul')
              .height(self.settings.height + self.settings.$titlebar.outerHeight(true));
            self.settings.$topbar
              .css('min-height', self.settings.height + self.settings.$titlebar.outerHeight(true) * 2)
          }
      });

      $(window).on('resize.fndtn.topbar', function () {
        if (!this.breakpoint()) {
          this.settings.$topbar.css('min-height', '');
        }
      }.bind(this));

      // Go up a level on Click
      $(this.scope).on('click.fndtn', '.top-bar .has-dropdown .back', function (e) {
        e.preventDefault();

        var $this = $(this),
            self = Foundation.libs.topbar,
            $movedLi = $this.closest('li.moved'),
            $previousLevelUl = $movedLi.parent();

        self.settings.index -= 1;
        self.settings.$section.css({left: -(100 * self.settings.index) + '%'});
        self.settings.$section.find('>.name').css({'left': 100 * self.settings.index + '%'});

        if (self.settings.index === 0) {
          self.settings.$topbar.css('min-height', 0);
        }

        setTimeout(function () {
          $movedLi.removeClass('moved');
        }, 300);
      });
    },

    breakpoint : function () {
      return this.settings.$w.width() < this.settings.breakPoint;
    },

    assemble : function () {
      // Pull element out of the DOM for manipulation
      this.settings.$section.detach();

      this.settings.$section.find('.has-dropdown>a').each(function () {
        var $link = $(this),
            $dropdown = $link.siblings('.dropdown'),
            $titleLi = $('

');

        // Copy link to subnav
        $titleLi.find('h5>a').html($link.html());
        $dropdown.prepend($titleLi);
      });

      // Put element back in the DOM
      this.settings.$section.appendTo(this.settings.$topbar);
    },

    largestUL : function () {
      var uls = this.settings.$topbar.find('section ul ul'),
          largest = uls.first(),
          total = 0;

      uls.each(function () {
        if ($(this).children('li').length > largest.children('li').length) {
          largest = $(this);
        }
      });

      largest.children('li').each(function () { total += $(this).outerHeight(true); });

      this.settings.height = total;
    },

    unbind : function () {
      $(this.scope).off('.fndtn.topbar');
      $(window).off('.fndtn.topbar');
    }
  };
}(Foundation.zj, this, this.document));
<commit_msg>Update topbar.<commit_after>/*jslint unparam: true, browser: true, indent: 2 */

;(function ($, window, document, undefined) {
  'use strict';

  Foundation.libs.topbar = {
    name : 'topbar',

    version : '4.0.0.alpha',

    settings : {
      index : 0,
      init : false
    },

    init : function (scope, method, options) {
      this.scope = scope || this.scope;

      if (typeof method === 'object') {
        $.extend(true, this.settings, method);
      }

      if (typeof method != 'string') {
        this.settings.$w = $(window);
        this.settings.$topbar = $('nav.top-bar');
        this.settings.$section = this.settings.$topbar.find('section');
        this.settings.$titlebar = this.settings.$topbar.children('ul').first();

        var breakpoint = $("
").appendTo("body");
        this.settings.breakPoint = breakpoint.width();
        breakpoint.remove();

        if (!this.settings.init) {
          this.events();
          this.assemble();
        }

        if (!this.settings.height) this.largestUL();

        if (this.settings.$topbar.parent().hasClass('fixed')) {
          $('body').css('padding-top', this.settings.$topbar.outerHeight());
        }

        return this.settings.init;
      } else {
        // fire method
        return this[method].call(this, options);
      }
    },

    events : function () {
      $(this.scope)
        .on('click.fndtn.topbar', '.top-bar .toggle-topbar', function (e) {
          e.preventDefault();

          if (this.breakpoint()) {
            this.settings.$topbar.toggleClass('expanded');
            this.settings.$topbar.css('min-height', '');
          }

          if (!this.settings.$topbar.hasClass('expanded')) {
            this.settings.$section.css({left: '0%'});
            this.settings.$section.find('>.name').css({left: '100%'});
            this.settings.$section.find('li.moved').removeClass('moved');
            this.settings.index = 0;
          }
        }.bind(this))

        .on('click.fndtn.topbar', '.top-bar .has-dropdown>a', function (e) {
          var self = Foundation.libs.topbar;

          if (Modernizr.touch || self.breakpoint())
            e.preventDefault();

          if (self.breakpoint()) {
            var $this = $(this),
                $selectedLi = $this.closest('li');

            self.settings.index += 1;
            $selectedLi.addClass('moved');
            self.settings.$section.css({left: -(100 * self.settings.index) + '%'});
            self.settings.$section.find('>.name').css({left: 100 * self.settings.index + '%'});

            $this.siblings('ul')
              .height(self.settings.height + self.settings.$titlebar.outerHeight(true));
            self.settings.$topbar
              .css('min-height', self.settings.height + self.settings.$titlebar.outerHeight(true) * 2)
          }
      });

      $(window).on('resize.fndtn.topbar', function () {
        if (!this.breakpoint()) {
          this.settings.$topbar.css('min-height', '');
        }
      }.bind(this));

      // Go up a level on Click
      $(this.scope).on('click.fndtn', '.top-bar .has-dropdown .back', function (e) {
        e.preventDefault();

        var $this = $(this),
            self = Foundation.libs.topbar,
            $movedLi = $this.closest('li.moved'),
            $previousLevelUl = $movedLi.parent();

        self.settings.index -= 1;
        self.settings.$section.css({left: -(100 * self.settings.index) + '%'});
        self.settings.$section.find('>.name').css({'left': 100 * self.settings.index + '%'});

        if (self.settings.index === 0) {
          self.settings.$topbar.css('min-height', 0);
        }

        setTimeout(function () {
          $movedLi.removeClass('moved');
        }, 300);
      });
    },

    breakpoint : function () {
      return this.settings.$w.width() < this.settings.breakPoint;
    },

    assemble : function () {
      // Pull element out of the DOM for manipulation
      this.settings.$section.detach();

      this.settings.$section.find('.has-dropdown>a').each(function () {
        var $link = $(this),
            $dropdown = $link.siblings('.dropdown'),
            $titleLi = $('

');

        // Copy link to subnav
        $titleLi.find('h5>a').html($link.html());
        $dropdown.prepend($titleLi);
      });

      // Put element back in the DOM
      this.settings.$section.appendTo(this.settings.$topbar);
    },

    largestUL : function () {
      var uls = this.settings.$topbar.find('section ul ul'),
          largest = uls.first(),
          total = 0;

      uls.each(function () {
        if ($(this).children('li').length > largest.children('li').length) {
          largest = $(this);
        }
      });

      largest.children('li').each(function () { total += $(this).outerHeight(true); });

      this.settings.height = total;
    },

    off : function () {
      $(this.scope).off('.fndtn.topbar');
      $(window).off('.fndtn.topbar');
    }
  };
}(Foundation.zj, this, this.document));
<|endoftext|>


<commit_before>mini.Module(
	"behaviour/traits/showcase/jumpingcrate"
)
.requires(
	"behaviour/trait"
)
.defines(function (Trait) {
	Trait.Repository.add("showcase/jumpingcrate", function jump(entity) {
		// lazy initialization
		if(typeof this.jumpTimer === "undefined")
			// Jump all 5 seconds.
			this.jumpTimer =  new Bloob.Timer(5);

		if(this.jumpTimer.get() > 0) {
			this.jumpTimer.reset();
			var body = entity.getBody();
			body.addGlobalForce(body.getDerivedPosition().add(new Jello.Vector2(0.1, 0)), new Jello.Vector2(0, 1000));
		}

		// add additional force, if clicked on crate
		if(entity.isClicked()) {
			var body = entity.getBody();
			body.addGlobalForce(body.getDerivedPosition().add(new Jello.Vector2(0.1, 0)), new Jello.Vector2(1000, 0));
		} else if(entity.isHovered()) {
			// add additional force, if hovered over crate
			var body = entity.getBody();
			body.addGlobalForce(body.getDerivedPosition().add(new Jello.Vector2(0.1, 0)), new Jello.Vector2(0, 100));
		}
	});
});
<commit_msg>included Timer to jumpingcrate behaviour<commit_after>mini.Module(
	"behaviour/traits/showcase/jumpingcrate"
)
.requires(
	"behaviour/trait",
	"engine/time/timer"
)
.defines(function (Trait, Timer) {
	Trait.Repository.add("showcase/jumpingcrate", function jump(entity) {
		// lazy initialization
		if(typeof this.jumpTimer === "undefined")
			// Jump all 5 seconds.
			this.jumpTimer =  new Timer(5);

		if(this.jumpTimer.get() > 0) {
			this.jumpTimer.reset();
			var body = entity.getBody();
			body.addGlobalForce(body.getDerivedPosition().add(new Jello.Vector2(0.1, 0)), new Jello.Vector2(0, 1000));
		}

		// add additional force, if clicked on crate
		if(entity.isClicked()) {
			var body = entity.getBody();
			body.addGlobalForce(body.getDerivedPosition().add(new Jello.Vector2(0.1, 0)), new Jello.Vector2(1000, 0));
		} else if(entity.isHovered()) {
			// add additional force, if hovered over crate
			var body = entity.getBody();
			body.addGlobalForce(body.getDerivedPosition().add(new Jello.Vector2(0.1, 0)), new Jello.Vector2(0, 100));
		}
	});
});
<|endoftext|>


<commit_before>// External Dependencies
const React           = require('react');
const classNames      = require('classnames');

/**
 * A container component for configurable modals
 * @class ModalContainer
 *
 * @memberOf GlobalComponents
 * @see {@link GlobalComponents}
 *
 * @type {*|Function}
 */
const ModalContainer = React.createClass({
	propTypes: {
		openWhen         : React.PropTypes.bool.isRequired,
		width            : React.PropTypes.oneOfType([
			React.PropTypes.string,
			React.PropTypes.number
		]),
		title            : React.PropTypes.string.isRequired,
		closeText        : React.PropTypes.string,
		confirmText      : React.PropTypes.string,
		modalBodyClasses : React.PropTypes.string,
		beforeOpen       : React.PropTypes.func,
		afterOpen        : React.PropTypes.func,
		beforeClose      : React.PropTypes.func,
		afterClose       : React.PropTypes.func,
		beforeConfirm    : React.PropTypes.func,
		afterConfirm     : React.PropTypes.func,
		disableConfirm   : React.PropTypes.bool,
		children         : React.PropTypes.oneOfType([React.PropTypes.array, React.PropTypes.object]),
		options          : React.PropTypes.shape({
			keepOpen: React.PropTypes.bool // If this is true, clicking outside of a modal will not close the modal
		})
	},

	/**
	 * getInitialState
	 *
	 * @memberOf GlobalComponents.ModalContainer
	 *
	 * @return {object}
	 */
	getInitialState: function () {
		return {
			isOpen: false
		};
	},

	/**
	 * When the component mounts, an event listener is attached to the window
	 * to track keyup events, and filter them into this component's handleKeyUp function.
	 *
	 * @memberOf GlobalComponents.ModalContainer
	 *
	 * @return {null}
	 */
	componentDidMount: function() {
		window.addEventListener('keyup', this.handleKeyUp);
	},

	/**
	 * When the component is unmounted, the window's KeyUp listener is removed
	 * @memberOf GlobalComponents.ModalContainer
	 *
	 * @return {null}
	 */
	componentWillUnmount: function() {
		window.removeEventListener('keyup', this.handleKeyUp);
	},

	/**
	 * Invoked when a component is receiving new props. This method is not called for the initial render.
	 * @memberOf GlobalComponents.ModalContainer
	 *
	 * @param  {object} newProps Incoming props from parent component
	 *
	 * @return {null}
	 */
	componentWillReceiveProps: function(newProps) {
		newProps.openWhen && this.openModal();
	},

	/**
	 * Open the modal and fire appropriate user functions
	 * @memberOf GlobalComponents.ModalContainer
	 *
	 * @return {null}
	 */
	openModal: function () {
		this._modalLifeCycleEvent('open', true);
	},

	/**
	 * Close the modal and fire appropriate user functions
	 * @memberOf GlobalComponents.ModalContainer
	 *
	 * @return {null}
	 */
	closeModal: function () {
		this._modalLifeCycleEvent('close', false);
	},

	/**
	 * Save modal and fire the appropriate user functions
	 * @memberOf GlobalComponents.ModalContainer
	 *
	 * @return {null}
	 */
	confirmModal: function () {
		this._modalLifeCycleEvent('confirm', false);
	},

	/**
	 * Fire the before and after lifecycle hooks for the modal component.
	 *
	 * @private
	 * @memberOf GlobalComponents.ModalContainer
	 *
	 * @param  {string} action         Modal lifecycle action
	 * @param  {bool}   nextModalState The next value for the modal `isOpen` prop
	 *
	 * @return {null}
	 */
	_modalLifeCycleEvent: function(action, nextModalState) {
		action = action.charAt(0).toUpperCase() + action.slice(1);

		this._callUserFunc(`before${action}`);

		this.setState({
			isOpen: nextModalState
		});

		this._callUserFunc(`after${action}`);
	},

	/**
	 * Determine whether the modal is open and we should render the overlay
	 *
	 * @private
	 * @memberOf GlobalComponents.ModalContainer
	 *
	 * @returns {jsx|null}
	 */
	_shouldRenderOverlay: function () {
		return this.state.isOpen &&

;
	},

	/**
	 * Fire the selected user function, if it's provided
	 *
	 * @private
	 * @memberOf GlobalComponents.ModalContainer
	 *
	 * @param {string} propName Lifecycle event for modal
	 *
	 * @return {null}
	 */
	_callUserFunc: function (propName) {
		if (this.props[propName]) {
			this.props[propName]();
		}
	},

	/**
	 * Return save button jsx
	 * @memberOf GlobalComponents.ModalContainer
	 *
	 * @returns {jsx}
	 */
	confirmButton: function () {
		return <button type="button" className={"btn btn-primary"} disabled={this.props.disableConfirm} onClick={this.confirmModal}>{this.props.confirmText || 'Confirm'}</button>;
	},

	/**
	 * Return cancel button jsx
	 * @memberOf GlobalComponents.ModalContainer
	 *
	 * @returns {jsx}
	 */
	cancelButton: function () {
		return <button type="button" className="btn btn-default" onClick={this.closeModal}>{this.props.closeText || 'Cancel'}</button>;
	},

	/**
	 * A click listener attached to the modal.
	 * Every time a click happens, this function checks to see if the click was on the modal dialog,
	 * or outside of it.
	 *
	 * If the click is outside of the dialog, we close the modal without saving.
	 * @memberOf GlobalComponents.ModalContainer
	 *
	 * @param {Event} event The click event
	 *
	 * @return {null}
	 */
	handleClick: function(event) {
		let clickArea = this.refs.dialog,
			options   = this.props.options || {};
		if (! options.keepOpen && ! clickArea.contains(event.target)) {
			this.closeModal();
		}
	},

	/**
	 * A keyup listener attached to the modal.
	 * Every time a key is released, this function checks to see if it was the 'ESC' key.
	 * If the 'ESC' was released, we close the modal.
	 * @memberOf GlobalComponents.ModalContainer
	 *
	 * @param {Event} event The keyup event
	 *
	 * @return {null}
	 */
	handleKeyUp: function(event) {
		if (event.keyCode === 27) {
			this.closeModal();
		}
	},

	/**
	 * Determine which, if any, buttons to display
	 *
	 * @private
	 * @memberOf GlobalComponents.ModalContainer
	 *
	 * @returns {jsx}
	 */
	_buildButtons: function () {
		return (

				{this.cancelButton()}
				{this.confirmButton()}

		);
	},

	/**
	 * Set up a modal's class names
	 * @private
	 * @memberOf GlobalComponents.ModalContainer
	 *
	 * @return {object} CSS classes for the entire modal component
	 */
	_modalClassnames: function() {
		return classNames({
			'mortar-modal' : true,
			fade           : true,
			in             : this.state.isOpen
		});
	},

	/**
	 * Prepares the CSS classes for the modal body
	 *
	 * @private
	 * @memberOf GlobalComponents.ModalContainer
	 *
	 * @return {string} CSS classes for the modal body
	 */
	_modalBodyClasses: function() {
		return this.props.modalBodyClasses ? `mortar-modal-body ${this.props.modalBodyClasses}` : 'mortar-modal-body';
	},

	/**
	 * Sets the width of the modal component. The default modal size is 600px.
	 *
	 * @private
	 * @memberOf GlobalComponents.ModalContainer
	 *
	 * @return {object} Width of modal in pixels
	 */
	_modalWidth: function() {
		return {
			width: this.props.width || '600px'
		};
	},

	/**
	 * Render the active modal
	 *
	 * @private
	 * @memberOf GlobalComponents.ModalContainer
	 *
	 * @return {jsx}
	 */
	_renderModal: function() {
		return (

								<button type="button" className="close" onClick={this.closeModal}>×</button>

{this.props.title}

								{this.props.children}

							{this._buildButtons()}

				{this._shouldRenderOverlay()}

		);
	},

	/**
	 * Renders the complete Modal component.
	 * @memberOf GlobalComponents.ModalContainer
	 *
	 * @return {jsx}
	 */
	render: function () {
		return (

				{this.state.isOpen && this._renderModal()}

		);
	}
});

module.exports = ModalContainer;
<commit_msg>Chaning how Modal sets its widht to comply with lenghts needing a valid length unit<commit_after>// External Dependencies
const React           = require('react');
const classNames      = require('classnames');

/**
 * A container component for configurable modals
 * @class ModalContainer
 *
 * @memberOf GlobalComponents
 * @see {@link GlobalComponents}
 *
 * @type {*|Function}
 */
const ModalContainer = React.createClass({
	propTypes: {
		openWhen         : React.PropTypes.bool.isRequired,
		width            : React.PropTypes.string,
		title            : React.PropTypes.string.isRequired,
		closeText        : React.PropTypes.string,
		confirmText      : React.PropTypes.string,
		modalBodyClasses : React.PropTypes.string,
		beforeOpen       : React.PropTypes.func,
		afterOpen        : React.PropTypes.func,
		beforeClose      : React.PropTypes.func,
		afterClose       : React.PropTypes.func,
		beforeConfirm    : React.PropTypes.func,
		afterConfirm     : React.PropTypes.func,
		disableConfirm   : React.PropTypes.bool,
		children         : React.PropTypes.oneOfType([React.PropTypes.array, React.PropTypes.object]),
		options          : React.PropTypes.shape({
			keepOpen: React.PropTypes.bool // If this is true, clicking outside of a modal will not close the modal
		})
	},

	/**
	 * getInitialState
	 *
	 * @memberOf GlobalComponents.ModalContainer
	 *
	 * @return {object}
	 */
	getInitialState: function () {
		return {
			isOpen: false
		};
	},

	unitRegExp  : new RegExp(/^\d+(px|em|\%|cm|mm|in|pt|pc|ch|vh|vw|vmin|vmax)$/),
	digitRegExp : new RegExp(/\d+/),

	/**
	 * When the component mounts, an event listener is attached to the window
	 * to track keyup events, and filter them into this component's handleKeyUp function.
	 *
	 * @memberOf GlobalComponents.ModalContainer
	 *
	 * @return {null}
	 */
	componentDidMount: function() {
		window.addEventListener('keyup', this.handleKeyUp);
	},

	/**
	 * When the component is unmounted, the window's KeyUp listener is removed
	 * @memberOf GlobalComponents.ModalContainer
	 *
	 * @return {null}
	 */
	componentWillUnmount: function() {
		window.removeEventListener('keyup', this.handleKeyUp);
	},

	/**
	 * Invoked when a component is receiving new props. This method is not called for the initial render.
	 * @memberOf GlobalComponents.ModalContainer
	 *
	 * @param  {object} newProps Incoming props from parent component
	 *
	 * @return {null}
	 */
	componentWillReceiveProps: function(newProps) {
		newProps.openWhen && this.openModal();
	},

	/**
	 * Open the modal and fire appropriate user functions
	 * @memberOf GlobalComponents.ModalContainer
	 *
	 * @return {null}
	 */
	openModal: function () {
		this._modalLifeCycleEvent('open', true);
	},

	/**
	 * Close the modal and fire appropriate user functions
	 * @memberOf GlobalComponents.ModalContainer
	 *
	 * @return {null}
	 */
	closeModal: function () {
		this._modalLifeCycleEvent('close', false);
	},

	/**
	 * Save modal and fire the appropriate user functions
	 * @memberOf GlobalComponents.ModalContainer
	 *
	 * @return {null}
	 */
	confirmModal: function () {
		this._modalLifeCycleEvent('confirm', false);
	},

	/**
	 * Fire the before and after lifecycle hooks for the modal component.
	 *
	 * @private
	 * @memberOf GlobalComponents.ModalContainer
	 *
	 * @param  {string} action         Modal lifecycle action
	 * @param  {bool}   nextModalState The next value for the modal `isOpen` prop
	 *
	 * @return {null}
	 */
	_modalLifeCycleEvent: function(action, nextModalState) {
		action = action.charAt(0).toUpperCase() + action.slice(1);

		this._callUserFunc(`before${action}`);

		this.setState({
			isOpen: nextModalState
		});

		this._callUserFunc(`after${action}`);
	},

	/**
	 * Determine whether the modal is open and we should render the overlay
	 *
	 * @private
	 * @memberOf GlobalComponents.ModalContainer
	 *
	 * @returns {jsx|null}
	 */
	_shouldRenderOverlay: function () {
		return this.state.isOpen &&

;
	},

	/**
	 * Fire the selected user function, if it's provided
	 *
	 * @private
	 * @memberOf GlobalComponents.ModalContainer
	 *
	 * @param {string} propName Lifecycle event for modal
	 *
	 * @return {null}
	 */
	_callUserFunc: function (propName) {
		if (this.props[propName]) {
			this.props[propName]();
		}
	},

	/**
	 * Return save button jsx
	 * @memberOf GlobalComponents.ModalContainer
	 *
	 * @returns {jsx}
	 */
	confirmButton: function () {
		return <button type="button" className={"btn btn-primary"} disabled={this.props.disableConfirm} onClick={this.confirmModal}>{this.props.confirmText || 'Confirm'}</button>;
	},

	/**
	 * Return cancel button jsx
	 * @memberOf GlobalComponents.ModalContainer
	 *
	 * @returns {jsx}
	 */
	cancelButton: function () {
		return <button type="button" className="btn btn-default" onClick={this.closeModal}>{this.props.closeText || 'Cancel'}</button>;
	},

	/**
	 * A click listener attached to the modal.
	 * Every time a click happens, this function checks to see if the click was on the modal dialog,
	 * or outside of it.
	 *
	 * If the click is outside of the dialog, we close the modal without saving.
	 * @memberOf GlobalComponents.ModalContainer
	 *
	 * @param {Event} event The click event
	 *
	 * @return {null}
	 */
	handleClick: function(event) {
		let clickArea = this.refs.dialog,
			options   = this.props.options || {};
		if (! options.keepOpen && ! clickArea.contains(event.target)) {
			this.closeModal();
		}
	},

	/**
	 * A keyup listener attached to the modal.
	 * Every time a key is released, this function checks to see if it was the 'ESC' key.
	 * If the 'ESC' was released, we close the modal.
	 * @memberOf GlobalComponents.ModalContainer
	 *
	 * @param {Event} event The keyup event
	 *
	 * @return {null}
	 */
	handleKeyUp: function(event) {
		if (event.keyCode === 27) {
			this.closeModal();
		}
	},

	/**
	 * Determine which, if any, buttons to display
	 *
	 * @private
	 * @memberOf GlobalComponents.ModalContainer
	 *
	 * @returns {jsx}
	 */
	_buildButtons: function () {
		return (

				{this.cancelButton()}
				{this.confirmButton()}

		);
	},

	/**
	 * Set up a modal's class names
	 * @private
	 * @memberOf GlobalComponents.ModalContainer
	 *
	 * @return {object} CSS classes for the entire modal component
	 */
	_modalClassnames: function() {
		return classNames({
			'mortar-modal' : true,
			fade           : true,
			in             : this.state.isOpen
		});
	},

	/**
	 * Prepares the CSS classes for the modal body
	 *
	 * @private
	 * @memberOf GlobalComponents.ModalContainer
	 *
	 * @return {string} CSS classes for the modal body
	 */
	_modalBodyClasses: function() {
		return this.props.modalBodyClasses ? `mortar-modal-body ${this.props.modalBodyClasses}` : 'mortar-modal-body';
	},

	/**
	 * Sets the width of the modal component. The default modal size is 600px.
	 *
	 * @private
	 * @memberOf GlobalComponents.ModalContainer
	 *
	 * @return {object} Width of modal in pixels
	 */
	_modalWidth: function() {
		let width = this.props.width;
		try {
			if (! this.unitRegExp.test(width)) {
				let newWidth = width.match(this.digitRegExp)[0] + 'px';

				console.warn(`The value provided for width: "${width}" was not valid.  Adjusting to ${newWidth}. Please note that valid css units are required: https://developer.mozilla.org/en-US/docs/Web/CSS/length`);
				width = newWidth;
			}

		} catch (e) {
			console.warn(`A digit is required when specifying width.  Your input: "${width}".  Falling back to 600px.`);
			width = "600px";
		}

		return {
			width: width
		};
	},

	/**
	 * Render the active modal
	 *
	 * @private
	 * @memberOf GlobalComponents.ModalContainer
	 *
	 * @return {jsx}
	 */
	_renderModal: function() {
		return (

								<button type="button" className="close" onClick={this.closeModal}>×</button>

{this.props.title}

								{this.props.children}

							{this._buildButtons()}

				{this._shouldRenderOverlay()}

		);
	},

	/**
	 * Renders the complete Modal component.
	 * @memberOf GlobalComponents.ModalContainer
	 *
	 * @return {jsx}
	 */
	render: function () {
		return (

				{this.state.isOpen && this._renderModal()}

		);
	}
});

module.exports = ModalContainer;
<|endoftext|>


<commit_before>'use strict'

import { describe, it } from 'mocha'
import expect from 'expect'

import { List, fromJS, toJS } from 'immutable'

import flatten from 'lodash/flatten'

import reducer from '../../../../app/js/reducers/issues-by-opening-timestamp'

import { getMiniCSV } from './helpers'
import { transformCSVtoJSON } from '../../../../app/js/reducers/helpers'

describe('Issues by opening timestamp reducer', () => {

  // const state = List.of()

  // beforeEach()
  it('should have 10 items on initialization', () => {

    const csv = getMiniCSV()
    const json = transformCSVtoJSON(csv)

    const action = {
      type: 'INIT_LOAD',
    }
    const newState = reducer(undefined, action)

    const array = new Array(10).fill(List.of())
    const result = fromJS(array)
    // console.dir(JSON.parse(result.toJS()), {depth: null, colors: true})

    expect(newState.count()).toEqual(result.count())
  })

  it('should have 10 items on INIT_LOAD with CSV payload', () => {

    const csv = getMiniCSV()
    const json = transformCSVtoJSON(csv)

    const action = {
      type: 'INIT_LOAD',
      payload: {
        data: csv,
      },
    }
    const state = fromJS(new Array(10).fill(List.of()))
    const newState = reducer(state, action)

    const array = new Array(10).fill(List.of())
    const result = fromJS(array)
    // console.dir(JSON.parse(result.toJS()), {depth: null, colors: true})

    expect(newState.count()).toEqual(result.count())
  })

  it('should be array')

  it('should have items that are arrays of ids and timestamps')

})
<commit_msg>test(arrayByTimestamp): added tests to if it's array with correct fields<commit_after>'use strict'

import { describe, it } from 'mocha'
import expect from 'expect'

import { List, fromJS, toJS } from 'immutable'

import flatten from 'lodash/flatten'

import reducer from '../../../../app/js/reducers/issues-by-opening-timestamp'

import { getMiniCSV } from './helpers'
import { transformCSVtoJSON } from '../../../../app/js/reducers/helpers'

describe('Issues by opening timestamp reducer', () => {

  // const state = List.of()

  // beforeEach()
  it('should have 10 items on initialization', () => {

    const csv = getMiniCSV()
    const json = transformCSVtoJSON(csv)

    const action = {
      type: 'INIT_LOAD',
    }
    const newState = reducer(undefined, action)

    const array = new Array(10).fill(List.of())
    const expectedResult = fromJS(array)
    // console.dir(JSON.parse(result.toJS()), {depth: null, colors: true})

    expect(newState.count()).toEqual(expectedResult.count())
  })

  it('should have 10 items on INIT_LOAD with CSV payload', () => {

    const csv = getMiniCSV()
    const json = transformCSVtoJSON(csv)

    const action = {
      type: 'INIT_LOAD',
      payload: {
        data: csv,
      },
    }
    const state = fromJS(new Array(10).fill(List.of()))
    const newState = reducer(state, action)

    const array = new Array(10).fill(List.of())
    const expectedResult = fromJS(array)
    // console.dir(JSON.parse(result.toJS()), {depth: null, colors: true})

    expect(newState.count()).toEqual(expectedResult.count())
  })

  it('should be array', () => {

    const csv = getMiniCSV()
    const json = transformCSVtoJSON(csv)

    const action = {
      type: 'INIT_LOAD',
      payload: {
        data: csv,
      },
    }

    const state = fromJS(new Array(10).fill(List.of()))
    const newState = reducer(state, action)

    expect(List.isList(newState)).toBe(true)

  })

  it('should have items that are arrays of ids and timestamps', () =>{
    const csv = getMiniCSV()
    const json = transformCSVtoJSON(csv)

    const action = {
      type: 'INIT_LOAD',
      payload: {
        data: csv,
      },
    }

    const state = fromJS(new Array(10).fill(List.of()))
    const newState = reducer(state, action)

    expect(List.isList(newState.first())).toBe(true)
    expect(newState.first().first().includes('abcd')).toBe(true)
    expect(newState.first().includes('1454146495766')).toBe(true)

    expect(List.isList(newState.last())).toBe(true)
    expect(newState.last().isEmpty()).toBe(true)
  })

})
<|endoftext|>


<commit_before>import { transit_realtime as transitRealtime } from 'gtfs-realtime-bindings'
import L from 'leaflet'
import TransitVehicleOverlay from '@opentripplanner/transit-vehicle-overlay'
import PropTypes from 'prop-types'
import React from 'react'
import ReactDOMServer from 'react-dom/server'
import { FeatureGroup, MapLayer, Marker, Polyline } from 'react-leaflet'
import styled from 'styled-components'

export const VehicleShape = styled.span`
  background-color: #${props => props.route?.route_color || '000000'};
  border: 1px solid #000000;
  border-radius: 50%;
  color: #${props => props.route?.route_text_color || 'ffffff'};
  display: block;
  height: 24px;
  left: -6px;
  line-height: 24px;
  margin-left: auto;
  margin-right: auto;
  position: relative;
  text-align: center;
  top: -6px;
  width: 24px;
`

/**
 * Convert GTFS-rt entities to vehicle location from OTP-UI.
 * @param vehicle The GTFS-rt vehicle position entity to convert.
 * @param routes Optional GTFS-like routes list.
 */
function gtfsRtToTransitVehicle (gtfsRtVehicle, routes) {
  const { position, stopId, timestamp, trip, vehicle } = gtfsRtVehicle.vehicle
  let routeShortName
  if (routes.length !== 0) {
    routeShortName = routes.find(r => r.route_id === trip.routeId)?.route_short_name
  }
  return {
    heading: position.bearing,
    id: vehicle.id,
    lat: position.latitude,
    lon: position.longitude,
    reportDate: new Date(parseInt(timestamp, 10)).toLocaleString(),
    routeShortName,
    routeType: 'BUS',
    stopId,
    tripId: trip.tripId,
    vehicleId: vehicle.id
  }
}

/**
 * Generate a vehicle shape component (a colored dot) based on the provided routes list.
 */
function makeVehicleShape (routes) {
  return props => {
    const { lat, lon, routeShortName } = props.vehicle
    const route = routes.find(r => r.route_short_name === routeShortName) || {}
    const iconHtml = ReactDOMServer.renderToStaticMarkup(
      <VehicleShape route={route}>
        {routeShortName}
      </VehicleShape>
    )
    return (
      <Marker
        icon={L.divIcon({ html: iconHtml })}
        position={[lat, lon]}
      />
    )
  }
}

/**
 * Class that loads and renders a GTFS-rt vehicle positions feed
 * and associated route information.
 */
class GtfsRtVehicleOverlay extends MapLayer {
  static propTypes = {
    /** URL to GTFS-rt feed in protocol buffer format. */
    liveFeedUrl: PropTypes.string,
    /** URL to GTFS-like route list in JSON format. */
    routeDefinitionUrl: PropTypes.string
  }

  constructor (props) {
    super(props)
    this.state = {
      routes: [],
      vehicleLocations: [],
      vehicleSymbols: null,
      visible: props.visible
    }
  }

  componentDidMount () {
    const { liveFeedUrl, name, registerOverlay, routeDefinitionUrl } = this.props
    registerOverlay(this)
    if (!liveFeedUrl) {
      console.warn(`liveFeedUrl prop is missing for overlay '${name}'.`)
    }
    if (!routeDefinitionUrl) {
      console.warn(`routeDefinitionUrl prop is missing for overlay '${name}'.`)
    } else {
      this._fetchRoutes()
    }
  }

  componentWillUnmount () {
    this._stopRefreshing()
  }

  onOverlayAdded = () => {
    this.setState({ visible: true })
    this._startRefreshing()
  }

  onOverlayRemoved = () => {
    this.setState({ visible: false })
    this._stopRefreshing()
  }

  createLeafletElement () {}

  updateLeafletElement () {}

  /**
   * Fetches GTFS-rt vehicle positions (protocol buffer) and converts to
   * OTP-UI transitVehicleType before saving to component state.
   */
  _fetchVehiclePositions = async () => {
    if (this.props.liveFeedUrl) {
      try {
        const response = await fetch(this.props.liveFeedUrl)
        if (response.status >= 400) {
          const error = new Error('Received error from server')
          error.response = response
          throw error
        }
        const buffer = await response.arrayBuffer()
        const view = new Uint8Array(buffer)
        const feed = transitRealtime.FeedMessage.decode(view)

        const vehicleLocations = feed.entity.map(vehicle => gtfsRtToTransitVehicle(vehicle, this.state.routes))
        this.setState({ vehicleLocations })
      } catch (err) {
        console.log(err)
      }
    }
  }

  /**
   * Fetches GTFS-like route definitions (JSON).
   * (called once when component is mounted)
   */
  _fetchRoutes = async () => {
    if (this.props.routeDefinitionUrl) {
      try {
        const response = await fetch(this.props.routeDefinitionUrl)
        if (response.status >= 400) {
          const error = new Error('Received error from server')
          error.response = response
          throw error
        }
        const routes = await response.json()

        // Generate the symbols for the overlay at this time
        // so it renders the route colors for each vehicle.
        const vehicleSymbols = [
          {
            minZoom: 0,
            symbol: makeVehicleShape(routes)
          }
        ]

        this.setState({ routes, vehicleSymbols })
      } catch (err) {
        console.log(err)
      }
    }
  }

  _startRefreshing () {
    // ititial vehicle retrieval
    this._fetchVehiclePositions()

    // set up timer to refresh stations periodically
    // defaults to every 30 sec.
    this._refreshTimer = setInterval(this._fetchVehiclePositions, 30000)
  }

  _stopRefreshing () {
    if (this._refreshTimer) clearInterval(this._refreshTimer)
  }

  render () {
    const { routes, vehicleLocations, vehicleSymbols, visible } = this.state
    return (
      <FeatureGroup>
        {routes.map(r => (
          <Polyline
            color={`#${r.route_color}`}
            key={r.route_id}
            opacity={0.6}
            positions={r.shape}
          />
        ))}
        <TransitVehicleOverlay
          {...this.props}
          symbols={vehicleSymbols}
          vehicleList={visible ? vehicleLocations : null}
        />
      </FeatureGroup>
    )
  }
}

export default GtfsRtVehicleOverlay
<commit_msg>refactor(GtfsRtVehicleOverlay): Tweak state and remove prop spread.<commit_after>import { transit_realtime as transitRealtime } from 'gtfs-realtime-bindings'
import L from 'leaflet'
import TransitVehicleOverlay from '@opentripplanner/transit-vehicle-overlay'
import PropTypes from 'prop-types'
import React from 'react'
import ReactDOMServer from 'react-dom/server'
import { FeatureGroup, MapLayer, Marker, Polyline } from 'react-leaflet'
import styled from 'styled-components'

export const VehicleShape = styled.span`
  background-color: #${props => props.route?.route_color || '000000'};
  border: 1px solid #000000;
  border-radius: 50%;
  color: #${props => props.route?.route_text_color || 'ffffff'};
  display: block;
  height: 24px;
  left: -6px;
  line-height: 24px;
  margin-left: auto;
  margin-right: auto;
  position: relative;
  text-align: center;
  top: -6px;
  width: 24px;
`

/**
 * Convert GTFS-rt entities to vehicle location from OTP-UI.
 * @param vehicle The GTFS-rt vehicle position entity to convert.
 * @param routes Optional GTFS-like routes list.
 */
function gtfsRtToTransitVehicle (gtfsRtVehicle, routes) {
  const { position, stopId, timestamp, trip, vehicle } = gtfsRtVehicle.vehicle
  let routeShortName
  if (routes.length !== 0) {
    routeShortName = routes.find(r => r.route_id === trip.routeId)?.route_short_name
  }
  return {
    heading: position.bearing,
    id: vehicle.id,
    lat: position.latitude,
    lon: position.longitude,
    reportDate: new Date(parseInt(timestamp, 10)).toLocaleString(),
    routeShortName,
    routeType: 'BUS',
    stopId,
    tripId: trip.tripId,
    vehicleId: vehicle.id
  }
}

/**
 * Generate a vehicle shape component (a colored dot) based on the provided routes list.
 */
function makeVehicleShape (routes) {
  return props => {
    const { lat, lon, routeShortName } = props.vehicle
    const route = routes.find(r => r.route_short_name === routeShortName) || {}
    const iconHtml = ReactDOMServer.renderToStaticMarkup(
      <VehicleShape route={route}>
        {routeShortName}
      </VehicleShape>
    )
    return (
      <Marker
        icon={L.divIcon({ html: iconHtml })}
        position={[lat, lon]}
      />
    )
  }
}

/**
 * Class that loads and renders a GTFS-rt vehicle positions feed
 * and associated route information.
 */
class GtfsRtVehicleOverlay extends MapLayer {
  static propTypes = {
    /** URL to GTFS-rt feed in protocol buffer format. */
    liveFeedUrl: PropTypes.string,
    /** URL to GTFS-like route list in JSON format. */
    routeDefinitionUrl: PropTypes.string
  }

  constructor (props) {
    super(props)
    this.state = {
      routes: [],
      vehicleLocations: [],
      // Set to undefined to fall back on the default symbols,
      // unless a route definition is provided.
      vehicleSymbols: undefined,
      visible: props.visible
    }
  }

  componentDidMount () {
    const { liveFeedUrl, name, registerOverlay, routeDefinitionUrl } = this.props
    registerOverlay(this)
    if (!liveFeedUrl) {
      console.warn(`liveFeedUrl prop is missing for overlay '${name}'.`)
    }
    if (!routeDefinitionUrl) {
      console.warn(`routeDefinitionUrl prop is missing for overlay '${name}'.`)
    } else {
      this._fetchRoutes()
    }
  }

  componentWillUnmount () {
    this._stopRefreshing()
  }

  onOverlayAdded = () => {
    this.setState({ visible: true })
    this._startRefreshing()
  }

  onOverlayRemoved = () => {
    this.setState({ visible: false })
    this._stopRefreshing()
  }

  createLeafletElement () {}

  updateLeafletElement () {}

  /**
   * Fetches GTFS-rt vehicle positions (protocol buffer) and converts to
   * OTP-UI transitVehicleType before saving to component state.
   */
  _fetchVehiclePositions = async () => {
    if (this.props.liveFeedUrl) {
      try {
        const response = await fetch(this.props.liveFeedUrl)
        if (response.status >= 400) {
          const error = new Error('Received error from server')
          error.response = response
          throw error
        }
        const buffer = await response.arrayBuffer()
        const view = new Uint8Array(buffer)
        const feed = transitRealtime.FeedMessage.decode(view)

        const vehicleLocations = feed.entity.map(vehicle => gtfsRtToTransitVehicle(vehicle, this.state.routes))
        this.setState({ vehicleLocations })
      } catch (err) {
        console.log(err)
      }
    }
  }

  /**
   * Fetches GTFS-like route definitions (JSON).
   * (called once when component is mounted)
   */
  _fetchRoutes = async () => {
    if (this.props.routeDefinitionUrl) {
      try {
        const response = await fetch(this.props.routeDefinitionUrl)
        if (response.status >= 400) {
          const error = new Error('Received error from server')
          error.response = response
          throw error
        }
        const routes = await response.json()

        // Generate the symbols for the overlay at this time
        // so it renders the route colors for each vehicle.
        const vehicleSymbols = [
          {
            minZoom: 0,
            symbol: makeVehicleShape(routes)
          }
        ]

        this.setState({ routes, vehicleSymbols })
      } catch (err) {
        console.log(err)
      }
    }
  }

  _startRefreshing () {
    // ititial vehicle retrieval
    this._fetchVehiclePositions()

    // set up timer to refresh stations periodically
    // defaults to every 30 sec.
    this._refreshTimer = setInterval(this._fetchVehiclePositions, 30000)
  }

  _stopRefreshing () {
    if (this._refreshTimer) clearInterval(this._refreshTimer)
  }

  render () {
    const { routes, vehicleLocations, vehicleSymbols, visible } = this.state
    return (
      <FeatureGroup>
        {routes.map(r => (
          <Polyline
            color={`#${r.route_color}`}
            key={r.route_id}
            opacity={0.6}
            positions={r.shape}
          />
        ))}
        <TransitVehicleOverlay
          symbols={vehicleSymbols}
          vehicleList={visible ? vehicleLocations : null}
        />
      </FeatureGroup>
    )
  }
}

export default GtfsRtVehicleOverlay
<|endoftext|>


<commit_before>import path from 'path';
import fs from 'fs-extra';
import findUp from 'find-up';
import resolveFrom from 'resolve-from';
import fetch from 'node-fetch';
import deprecate from 'util-deprecate';
import dedent from 'ts-dedent';

import { logger } from '@storybook/node-logger';

import loadPresets from '../presets';
import loadCustomPresets from '../common/custom-presets';
import { typeScriptDefaults } from '../config/defaults';

const getAutoRefs = async (options) => {
  const location = await findUp('package.json', { cwd: options.configDir });
  const directory = path.dirname(location);

  const { dependencies, devDependencies } = await fs.readJSON(location);

  const list = await Promise.all(
    Object.keys({ ...dependencies, ...devDependencies }).map(async (d) => {
      try {
        const l = resolveFrom(directory, path.join(d, 'package.json'));

        const { storybook, name, version } = await fs.readJSON(l);

        if (storybook?.url) {
          return { id: name, ...storybook, version };
        }
      } catch {
        logger.warn(`unable to find package.json for ${d}`);
        return undefined;
      }
      return undefined;
    })
  );

  return list.filter(Boolean);
};

const checkRef = (url) =>
  fetch(`${url}/iframe.html`).then(
    ({ ok }) => ok,
    () => false
  );

const stripTrailingSlash = (url) => url.replace(/\/$/, '');

const toTitle = (input) => {
  const result = input
    .replace(/[A-Z]/g, (f) => ` ${f}`)
    .replace(/[-_][A-Z]/gi, (f) => ` ${f.toUpperCase()}`)
    .replace(/-/g, ' ')
    .replace(/_/g, ' ');

  return `${result.substring(0, 1).toUpperCase()}${result.substring(1)}`.trim();
};

const deprecatedDefinedRefDisabled = deprecate(
  () => {},
  dedent`
    Deprecated parameter: disabled => disable

    https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#deprecated-package-composition-disabled-parameter
  `
);

async function getManagerWebpackConfig(options, presets) {
  const typescriptOptions = await presets.apply('typescript', { ...typeScriptDefaults }, options);
  const babelOptions = await presets.apply('babel', {}, { ...options, typescriptOptions });

  const autoRefs = await getAutoRefs(options);
  const definedRefs = await presets.apply('refs', undefined, options);
  const entries = await presets.apply('managerEntries', [], options);

  const refs = {};

  if (autoRefs && autoRefs.length) {
    autoRefs.forEach(({ id, url, title, version }) => {
      refs[id.toLowerCase()] = {
        id: id.toLowerCase(),
        url: stripTrailingSlash(url),
        title,
        version,
      };
    });
  }

  if (definedRefs) {
    Object.entries(definedRefs).forEach(([key, value]) => {
      const { disable, disabled } = value;

      if (disable || disabled) {
        if (disabled) {
          deprecatedDefinedRefDisabled();
        }

        delete refs[key.toLowerCase()];
        return;
      }

      const url = typeof value === 'string' ? value : value.url;
      const rest =
        typeof value === 'string'
          ? { title: toTitle(key) }
          : { ...value, title: value.title || toTitle(value.key || key) };

      refs[key.toLowerCase()] = {
        id: key.toLowerCase(),
        ...rest,
        url: stripTrailingSlash(url),
      };
    });
  }

  if (autoRefs || definedRefs) {
    entries.push(path.resolve(path.join(options.configDir, `generated-refs.js`)));

    // verify the refs are publicly reachable, if they are not we'll require stories.json at runtime, otherwise the ref won't work
    await Promise.all(
      Object.entries(refs).map(async ([k, value]) => {
        const ok = checkRef(value.url);

        refs[k] = { ...value, type: ok ? 'server-checked' : 'unknown' };
      })
    );
  }

  return presets.apply('managerWebpack', {}, { ...options, babelOptions, entries, refs });
}

export default async (options) => {
  const { corePresets = [], frameworkPresets = [], overridePresets = [], ...restOptions } = options;

  const presetsConfig = [
    ...corePresets,
    require.resolve('../common/babel-cache-preset.js'),
    ...frameworkPresets,
    ...loadCustomPresets(options),
    ...overridePresets,
  ];

  const presets = loadPresets(presetsConfig, restOptions);

  return getManagerWebpackConfig({ ...restOptions, presets }, presets);
};
<commit_msg>Fixing incorrect ref type<commit_after>import path from 'path';
import fs from 'fs-extra';
import findUp from 'find-up';
import resolveFrom from 'resolve-from';
import fetch from 'node-fetch';
import deprecate from 'util-deprecate';
import dedent from 'ts-dedent';

import { logger } from '@storybook/node-logger';

import loadPresets from '../presets';
import loadCustomPresets from '../common/custom-presets';
import { typeScriptDefaults } from '../config/defaults';

const getAutoRefs = async (options) => {
  const location = await findUp('package.json', { cwd: options.configDir });
  const directory = path.dirname(location);

  const { dependencies, devDependencies } = await fs.readJSON(location);

  const list = await Promise.all(
    Object.keys({ ...dependencies, ...devDependencies }).map(async (d) => {
      try {
        const l = resolveFrom(directory, path.join(d, 'package.json'));

        const { storybook, name, version } = await fs.readJSON(l);

        if (storybook?.url) {
          return { id: name, ...storybook, version };
        }
      } catch {
        logger.warn(`unable to find package.json for ${d}`);
        return undefined;
      }
      return undefined;
    })
  );

  return list.filter(Boolean);
};

const checkRef = (url) =>
  fetch(`${url}/iframe.html`).then(
    ({ ok }) => ok,
    () => false
  );

const stripTrailingSlash = (url) => url.replace(/\/$/, '');

const toTitle = (input) => {
  const result = input
    .replace(/[A-Z]/g, (f) => ` ${f}`)
    .replace(/[-_][A-Z]/gi, (f) => ` ${f.toUpperCase()}`)
    .replace(/-/g, ' ')
    .replace(/_/g, ' ');

  return `${result.substring(0, 1).toUpperCase()}${result.substring(1)}`.trim();
};

const deprecatedDefinedRefDisabled = deprecate(
  () => {},
  dedent`
    Deprecated parameter: disabled => disable

    https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#deprecated-package-composition-disabled-parameter
  `
);

async function getManagerWebpackConfig(options, presets) {
  const typescriptOptions = await presets.apply('typescript', { ...typeScriptDefaults }, options);
  const babelOptions = await presets.apply('babel', {}, { ...options, typescriptOptions });

  const autoRefs = await getAutoRefs(options);
  const definedRefs = await presets.apply('refs', undefined, options);
  const entries = await presets.apply('managerEntries', [], options);

  const refs = {};

  if (autoRefs && autoRefs.length) {
    autoRefs.forEach(({ id, url, title, version }) => {
      refs[id.toLowerCase()] = {
        id: id.toLowerCase(),
        url: stripTrailingSlash(url),
        title,
        version,
      };
    });
  }

  if (definedRefs) {
    Object.entries(definedRefs).forEach(([key, value]) => {
      const { disable, disabled } = value;

      if (disable || disabled) {
        if (disabled) {
          deprecatedDefinedRefDisabled();
        }

        delete refs[key.toLowerCase()];
        return;
      }

      const url = typeof value === 'string' ? value : value.url;
      const rest =
        typeof value === 'string'
          ? { title: toTitle(key) }
          : { ...value, title: value.title || toTitle(value.key || key) };

      refs[key.toLowerCase()] = {
        id: key.toLowerCase(),
        ...rest,
        url: stripTrailingSlash(url),
      };
    });
  }

  if (autoRefs || definedRefs) {
    entries.push(path.resolve(path.join(options.configDir, `generated-refs.js`)));

    // verify the refs are publicly reachable, if they are not we'll require stories.json at runtime, otherwise the ref won't work
    await Promise.all(
      Object.entries(refs).map(async ([k, value]) => {
        const ok = await checkRef(value.url);

        refs[k] = { ...value, type: ok ? 'server-checked' : 'unknown' };
      })
    );
  }

  return presets.apply('managerWebpack', {}, { ...options, babelOptions, entries, refs });
}

export default async (options) => {
  const { corePresets = [], frameworkPresets = [], overridePresets = [], ...restOptions } = options;

  const presetsConfig = [
    ...corePresets,
    require.resolve('../common/babel-cache-preset.js'),
    ...frameworkPresets,
    ...loadCustomPresets(options),
    ...overridePresets,
  ];

  const presets = loadPresets(presetsConfig, restOptions);

  return getManagerWebpackConfig({ ...restOptions, presets }, presets);
};
<|endoftext|>