<commit_before>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Relay from 'react-relay/classic';
import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
import RaisedButton from 'material-ui/RaisedButton';
import Button from '@material-ui/core/Button';
import Dialog from '@material-ui/core/Dialog';
import DialogActions from '@material-ui/core/DialogActions';
import DialogContent from '@material-ui/core/DialogContent';
import DialogTitle from '@material-ui/core/DialogTitle';
import TextField from '@material-ui/core/TextField';
import ListItem from '@material-ui/core/ListItem';
import MdCancel from 'react-icons/lib/md/cancel';
import * as EmailValidator from 'email-validator';
import RoleSelect from './RoleSelect';
import UserUtil from '../user/UserUtil';
import CheckContext from '../../CheckContext';
import UserInvitationMutation from '../../relay/mutations/UserInvitationMutation';
import {
  units,
  StyledIconButton,
  Row,
  FlexRow,
} from '../../styles/js/shared';

const messages = defineMessages({
  inviteMembers: {
    id: 'TeamInviteMembers.newInvite',
    defaultMessage: 'Invite members',
  },
  inviteEmailInput: {
    id: 'TeamInviteMembers.emailInput',
    defaultMessage: 'Email address',
  },
  inviteEmailMultipleInput: {
    id: 'TeamInviteMembers.emailMultipleInput',
    defaultMessage: 'Separate emails by commas',
  },
  invalidEmail: {
    id: 'TeamInviteMembers.invalidEmail',
    defaultMessage: 'Not a valid email address.',
  },
  memberEmail: {
    id: 'TeamInviteMembers.existsEmail',
    defaultMessage: 'Already a team member.',
  },
  invitedEmail: {
    id: 'TeamInviteMembers.invitedEmail',
    defaultMessage: 'Already invited to this team.',
  },
  contributor: {
    id: 'TeamMembersListItem.contributor',
    defaultMessage: 'Contributor',
  },
  journalist: {
    id: 'TeamMembersListItem.journalist',
    defaultMessage: 'Journalist',
  },
  editor: {
    id: 'TeamMembersListItem.editor',
    defaultMessage: 'Editor',
  },
  annotator: {
    id: 'TeamMembersListItem.annotator',
    defaultMessage: 'Annotator',
  },
});

class TeamInviteMembers extends Component {
  static validateEmail(email, invitedEmails, membersEmails) {
    let error = null;
    const inputEmail = email.trim();
    if (EmailValidator.validate(inputEmail) === false) {
      error = 'invalid';
    } else if (invitedEmails.includes(inputEmail) === true) {
      error = 'invited';
    } else if (membersEmails.includes(inputEmail) === true) {
      error = 'member';
    }
    return error;
  }

  constructor(props) {
    super(props);
    this.state = {
      dialogOpen: false,
      membersToInvite: [{ email: '', role: 'contributor', error: [] }],
      addMany: false,
      errors: [],
    };
  }

  getCurrentUser() {
    return new CheckContext(this).getContextStore().currentUser;
  }

  handleOpenDialog() {
    this.setState({
      dialogOpen: true,
      sendDisabled: true,
      addMany: false,
      membersToInvite: [{ email: '', role: 'contributor', error: [] }],
      errors: [],
    });
  }

  handleCloseDialog() {
    this.setState({
      dialogOpen: false,
      sendDisabled: true,
      addMany: false,
      membersToInvite: [{ email: '', role: 'contributor', error: [] }],
      errors: [],
    });
  }

  handleAddAnother() {
    this.setState({
      membersToInvite: [...this.state.membersToInvite, { email: '', role: 'contributor', error: [] }],
    });
  }

  handleAddMany() {
    this.setState({
      addMany: true,
      sendDisabled: true,
      errors: [],
      membersToInvite: [{ email: '', role: 'contributor', error: [] }],
    });
  }

  handleAddSeparate() {
    this.setState({
      addMany: false,
      sendDisabled: true,
      errors: [],
      membersToInvite: [{ email: '', role: 'contributor', error: [] }],
    });
  }

  handleRemoveEmail(index) {
    this.state.membersToInvite.splice(index, 1);
    this.setState({ membersToInvite: this.state.membersToInvite });
    const emptyEmails = this.state.membersToInvite.every(obj => obj.email === '');
    this.setState({ sendDisabled: emptyEmails });
  }

  handleEmailChange(e, index) {
    this.state.membersToInvite[index].email = e.target.value;
    this.setState({ membersToInvite: this.state.membersToInvite });
    const emptyEmails = this.state.membersToInvite.every(obj => obj.email === '');
    this.setState({ sendDisabled: emptyEmails });
  }

  handleRoleChange(e, index) {
    this.state.membersToInvite[index].role = e.target.value;
    this.setState({ membersToInvite: this.state.membersToInvite });
  }

  validateMembers(members) {
    let validateMaxError = true;
    const { limits, members_count: membersCount } = this.props.team;
    const { invited_mails: invitedEmails, team_users: teamUsers } = this.props.team;
    let invitedCount = 0;
    const membersEmails = [];
    teamUsers.edges.map((teamUser) => {
      if (teamUser.node.user.email !== null) {
        return membersEmails.push(teamUser.node.user.email);
      }
      return null;
    });
    let emailError = null;
    members.forEach((item, index) => {
      const errors = [];
      if (item.email !== '') {
        if (this.state.addMany) {
          const emails = item.email.split(',');
          emails.forEach((x) => {
            invitedCount += 1;
            emailError = TeamInviteMembers.validateEmail(x, invitedEmails, membersEmails);
            if (emailError !== null) {
              errors.push({ key: emailError, email: x });
            }
          });
        } else {
          invitedCount += 1;
          emailError = TeamInviteMembers.validateEmail(item.email, invitedEmails, membersEmails);
          if (emailError !== null) {
            errors.push({ key: emailError, email: item.email });
          }
        }
        this.state.membersToInvite[index].error = errors;
      }
    });
    this.setState({ membersToInvite: this.state.membersToInvite });
    const allMembers = invitedEmails.length + membersCount + invitedCount;
    if (limits.max_number_of_members !== 0 && limits.max_number_of_members < allMembers) {
      const maxMembers = limits.max_number_of_members - (invitedEmails.length + membersCount);
      this.setState({ errors: [{ key: 'limits', maxMembers }] });
      validateMaxError = false;
    } else {
      this.setState({ errors: [] });
    }
    return validateMaxError;
  }

  handleSubmit() {
    const onFailure = () => {
    };

    const onSuccess = (response) => {
      const { userInvitation: { success } } = response;
      this.setState({
        dialogOpen: false,
      });
      if (success.length > 0) {
        const errorMessage = (
          success.map((message, index) => (
            <p key={`email-error-${index.toString()}`}>{message.email} : {message.error}

          ))
        );
        this.context.setMessage(errorMessage);
      } else {
        const message = (
          <FormattedMessage
            id="teamInviteMembers.resendEmailSuccess"
            defaultMessage="Invitation was sent successfully"
          />
        );
        this.context.setMessage(message);
      }
    };
    const validateMaxError = this.validateMembers(this.state.membersToInvite);
    const membersList = this.state.membersToInvite;
    const isValid = membersList.every(obj => obj.error.length === 0);
    if (isValid && validateMaxError === true && !this.state.sendDisabled) {
      const invitation = document.getElementById('invite-msg-input').value.trim();
      const members = JSON.stringify(membersList);
      Relay.Store.commitUpdate(
        new UserInvitationMutation({
          invitation,
          members,
        }),
        { onSuccess, onFailure },
      );
    }
  }

  renderError(item) {
    switch (item.key) {
    case 'invalid':
      return this.props.intl.formatMessage(messages.invalidEmail);
    case 'member':
      return this.props.intl.formatMessage(messages.memberEmail);
    case 'invited':
      return this.props.intl.formatMessage(messages.invitedEmail);
    case 'limits':
      return (
        <FormattedMessage
          id="teamInviteMembers.limits"
          defaultMessage="The maximum number of members for this team has been reached ({count, plural, =0 {0 members} one {1 member} other {# members}})."
          values={{ count: item.maxMembers }}
        />
      );
    default:
      return null;
    }
  }

  render() {
    const errosList = (
      this.state.errors.map((error, index) => (
        <ListItem key={`email-error-${index.toString()}`} style={{ color: 'red' }} className="team-invite-members__list-item-error">
          {this.renderError(error)}
        </ListItem>
      ))
    );
    const excludeRoles = UserUtil.myRole(this.getCurrentUser(), this.props.team.slug) === 'owner' ? [] : ['owner'];
    let inviteBody = null;
    if (this.state.addMany) {
      inviteBody = (
        this.state.membersToInvite.map((member, index) => (
          <div key={`invite-team-member-new-${index.toString()}`}>
            <FlexRow>
              {<FormattedMessage id="teamInviteMembers.inviteMembers" defaultMessage="Members will invited as" />}
              <RoleSelect
                className="invite-member-email-role"
                onChange={e => this.handleRoleChange(e, index)}
                value={member.role}
                excludeRoles={excludeRoles}
              />
            </FlexRow>
            <TextField
              key="invite-member-email-input"
              id={index.toString()}
              className="invite-member-email-input"
              label={this.props.intl.formatMessage(messages.inviteEmailMultipleInput)}
              multiline
              fullWidth
              margin="normal"
              variant="outlined"
              rows={4}
              onChange={e => this.handleEmailChange(e, index)}
              value={member.email}
              error={member.error.length > 0}
              helperText={
                member.error.map(errorItem => (
                  ` "${errorItem.email}": ${this.renderError(errorItem)}`
                ))
              }
            />

        ))
      );
    } else {
      inviteBody = (
        this.state.membersToInvite.map((member, index) => (
          <div key={`invite-team-member-new-${index.toString()}`}>
            <Row>
              <TextField
                key="invite-member-email-input"
                className="invite-member-email-input"
                id={index.toString()}
                placeholder={this.props.intl.formatMessage(messages.inviteEmailInput)}
                onChange={e => this.handleEmailChange(e, index)}
                value={member.email}
                error={member.error.length > 0}
                helperText={member.error.length === 0 ? null : this.renderError(member.error[0])}
                margin="normal"
                fullWidth
              />
              <Row
                style={{
                  marginLeft: units(2),
                  marginTop: units(1),
                  marginBottom: units(1),
                }}
              >
                <RoleSelect
                  className="invite-member-email-role"
                  onChange={e => this.handleRoleChange(e, index)}
                  value={member.role}
                  excludeRoles={excludeRoles}
                />

              </Row>
            </Row>

        ))
      );
    }

    return (
      <FlexRow>
        <RaisedButton
          style={{ marginLeft: 'auto', marginRight: units(2) }}
          onClick={this.handleOpenDialog.bind(this)}
          className="team-members__edit-button"
          label={
            <FormattedMessage id="teamInviteMembers.inviteMember" defaultMessage="Invite" />
          }
        />
        <Dialog
          className="team-invite-members__dialog"
          open={this.state.dialogOpen}
          onClose={this.handleCloseDialog.bind(this)}
          scroll="paper"
          fullWidth
        >
          <DialogTitle>{this.props.intl.formatMessage(messages.inviteMembers)}</DialogTitle>
          <DialogContent>
            { errosList }
            <TextField
              id="invite-msg-input"
              className="team-invite-members__input"
              label={
                <FormattedMessage id="teamInviteMembers.customInvitation" defaultMessage="Invitation note (optional)" />
              }
              fullWidth
              multiline
              rows={2}
              margin="normal"
              variant="outlined"
            />
            { inviteBody }
            { this.state.addMany ?

                <Row>
                  <Button
                    className="team-invite-members__dialog-add-separate-button"
                    onClick={this.handleAddSeparate.bind(this)}
                  >
                    <FormattedMessage id="teamInviteMembers.addSeparate" defaultMessage="Add separate" />
                  </Button>
                </Row>

 :

                <Row>
                  <Button
                    className="team-invite-members__dialog-add-another-button"
                    onClick={this.handleAddAnother.bind(this)}
                  >
                    <FormattedMessage id="teamInviteMembers.addAnother" defaultMessage="Add another" />
                  </Button>
                  <Button
                    className="team-invite-members__dialog-add-many-button"
                    onClick={this.handleAddMany.bind(this)}
                  >
                    <FormattedMessage id="teamInviteMembers.addMany" defaultMessage="Add many" />
                  </Button>
                </Row>

            }
          </DialogContent>
          <DialogActions>
            <Button onClick={this.handleCloseDialog.bind(this)}>
              <FormattedMessage id="teamInviteMembers.cancelAdd" defaultMessage="Cancel" />
            </Button>
            <Button
              className="team-invite-members__dialog-submit-button"
              onClick={this.handleSubmit.bind(this)}
              color="primary"
              disabled={this.state.sendDisabled}
            >
              <FormattedMessage id="teamInviteMembers.send" defaultMessage="Send" />
            </Button>
          </DialogActions>
        </Dialog>
      </FlexRow>
    );
  }
}

TeamInviteMembers.contextTypes = {
  store: PropTypes.object,
  setMessage: PropTypes.func,
};
export default injectIntl(TeamInviteMembers);
<commit_msg>Ticket #7201 - re-invite deleted team member<commit_after>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Relay from 'react-relay/classic';
import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
import RaisedButton from 'material-ui/RaisedButton';
import Button from '@material-ui/core/Button';
import Dialog from '@material-ui/core/Dialog';
import DialogActions from '@material-ui/core/DialogActions';
import DialogContent from '@material-ui/core/DialogContent';
import DialogTitle from '@material-ui/core/DialogTitle';
import TextField from '@material-ui/core/TextField';
import ListItem from '@material-ui/core/ListItem';
import MdCancel from 'react-icons/lib/md/cancel';
import * as EmailValidator from 'email-validator';
import RoleSelect from './RoleSelect';
import UserUtil from '../user/UserUtil';
import CheckContext from '../../CheckContext';
import UserInvitationMutation from '../../relay/mutations/UserInvitationMutation';
import {
  units,
  StyledIconButton,
  Row,
  FlexRow,
} from '../../styles/js/shared';

const messages = defineMessages({
  inviteMembers: {
    id: 'TeamInviteMembers.newInvite',
    defaultMessage: 'Invite members',
  },
  inviteEmailInput: {
    id: 'TeamInviteMembers.emailInput',
    defaultMessage: 'Email address',
  },
  inviteEmailMultipleInput: {
    id: 'TeamInviteMembers.emailMultipleInput',
    defaultMessage: 'Separate emails by commas',
  },
  invalidEmail: {
    id: 'TeamInviteMembers.invalidEmail',
    defaultMessage: 'Not a valid email address.',
  },
  memberEmail: {
    id: 'TeamInviteMembers.existsEmail',
    defaultMessage: 'Already a team member.',
  },
  invitedEmail: {
    id: 'TeamInviteMembers.invitedEmail',
    defaultMessage: 'Already invited to this team.',
  },
  contributor: {
    id: 'TeamMembersListItem.contributor',
    defaultMessage: 'Contributor',
  },
  journalist: {
    id: 'TeamMembersListItem.journalist',
    defaultMessage: 'Journalist',
  },
  editor: {
    id: 'TeamMembersListItem.editor',
    defaultMessage: 'Editor',
  },
  annotator: {
    id: 'TeamMembersListItem.annotator',
    defaultMessage: 'Annotator',
  },
});

class TeamInviteMembers extends Component {
  static validateEmail(email, invitedEmails, membersEmails) {
    let error = null;
    const inputEmail = email.trim();
    if (EmailValidator.validate(inputEmail) === false) {
      error = 'invalid';
    } else if (invitedEmails.includes(inputEmail) === true) {
      error = 'invited';
    } else if (membersEmails.includes(inputEmail) === true) {
      error = 'member';
    }
    return error;
  }

  constructor(props) {
    super(props);
    this.state = {
      dialogOpen: false,
      membersToInvite: [{ email: '', role: 'contributor', error: [] }],
      addMany: false,
      errors: [],
    };
  }

  getCurrentUser() {
    return new CheckContext(this).getContextStore().currentUser;
  }

  handleOpenDialog() {
    this.setState({
      dialogOpen: true,
      sendDisabled: true,
      addMany: false,
      membersToInvite: [{ email: '', role: 'contributor', error: [] }],
      errors: [],
    });
  }

  handleCloseDialog() {
    this.setState({
      dialogOpen: false,
      sendDisabled: true,
      addMany: false,
      membersToInvite: [{ email: '', role: 'contributor', error: [] }],
      errors: [],
    });
  }

  handleAddAnother() {
    this.setState({
      membersToInvite: [...this.state.membersToInvite, { email: '', role: 'contributor', error: [] }],
    });
  }

  handleAddMany() {
    this.setState({
      addMany: true,
      sendDisabled: true,
      errors: [],
      membersToInvite: [{ email: '', role: 'contributor', error: [] }],
    });
  }

  handleAddSeparate() {
    this.setState({
      addMany: false,
      sendDisabled: true,
      errors: [],
      membersToInvite: [{ email: '', role: 'contributor', error: [] }],
    });
  }

  handleRemoveEmail(index) {
    this.state.membersToInvite.splice(index, 1);
    this.setState({ membersToInvite: this.state.membersToInvite });
    const emptyEmails = this.state.membersToInvite.every(obj => obj.email === '');
    this.setState({ sendDisabled: emptyEmails });
  }

  handleEmailChange(e, index) {
    this.state.membersToInvite[index].email = e.target.value;
    this.setState({ membersToInvite: this.state.membersToInvite });
    const emptyEmails = this.state.membersToInvite.every(obj => obj.email === '');
    this.setState({ sendDisabled: emptyEmails });
  }

  handleRoleChange(e, index) {
    this.state.membersToInvite[index].role = e.target.value;
    this.setState({ membersToInvite: this.state.membersToInvite });
  }

  validateMembers(members) {
    let validateMaxError = true;
    const { limits, members_count: membersCount } = this.props.team;
    const { invited_mails: invitedEmails, team_users: teamUsers } = this.props.team;
    let invitedCount = 0;
    const membersEmails = [];
    teamUsers.edges.map((teamUser) => {
      if (teamUser.node.status === 'member' && teamUser.node.user.email !== null) {
        return membersEmails.push(teamUser.node.user.email);
      }
      return null;
    });
    let emailError = null;
    members.forEach((item, index) => {
      const errors = [];
      if (item.email !== '') {
        if (this.state.addMany) {
          const emails = item.email.split(',');
          emails.forEach((x) => {
            invitedCount += 1;
            emailError = TeamInviteMembers.validateEmail(x, invitedEmails, membersEmails);
            if (emailError !== null) {
              errors.push({ key: emailError, email: x });
            }
          });
        } else {
          invitedCount += 1;
          emailError = TeamInviteMembers.validateEmail(item.email, invitedEmails, membersEmails);
          if (emailError !== null) {
            errors.push({ key: emailError, email: item.email });
          }
        }
        this.state.membersToInvite[index].error = errors;
      }
    });
    this.setState({ membersToInvite: this.state.membersToInvite });
    const allMembers = invitedEmails.length + membersCount + invitedCount;
    if (limits.max_number_of_members !== 0 && limits.max_number_of_members < allMembers) {
      const maxMembers = limits.max_number_of_members - (invitedEmails.length + membersCount);
      this.setState({ errors: [{ key: 'limits', maxMembers }] });
      validateMaxError = false;
    } else {
      this.setState({ errors: [] });
    }
    return validateMaxError;
  }

  handleSubmit() {
    const onFailure = () => {
    };

    const onSuccess = (response) => {
      const { userInvitation: { success } } = response;
      this.setState({
        dialogOpen: false,
      });
      if (success.length > 0) {
        const errorMessage = (
          success.map((message, index) => (
            <p key={`email-error-${index.toString()}`}>{message.email} : {message.error}

          ))
        );
        this.context.setMessage(errorMessage);
      } else {
        const message = (
          <FormattedMessage
            id="teamInviteMembers.resendEmailSuccess"
            defaultMessage="Invitation was sent successfully"
          />
        );
        this.context.setMessage(message);
      }
    };
    const validateMaxError = this.validateMembers(this.state.membersToInvite);
    const membersList = this.state.membersToInvite;
    const isValid = membersList.every(obj => obj.error.length === 0);
    if (isValid && validateMaxError === true && !this.state.sendDisabled) {
      const invitation = document.getElementById('invite-msg-input').value.trim();
      const members = JSON.stringify(membersList);
      Relay.Store.commitUpdate(
        new UserInvitationMutation({
          invitation,
          members,
        }),
        { onSuccess, onFailure },
      );
    }
  }

  renderError(item) {
    switch (item.key) {
    case 'invalid':
      return this.props.intl.formatMessage(messages.invalidEmail);
    case 'member':
      return this.props.intl.formatMessage(messages.memberEmail);
    case 'invited':
      return this.props.intl.formatMessage(messages.invitedEmail);
    case 'limits':
      return (
        <FormattedMessage
          id="teamInviteMembers.limits"
          defaultMessage="The maximum number of members for this team has been reached ({count, plural, =0 {0 members} one {1 member} other {# members}})."
          values={{ count: item.maxMembers }}
        />
      );
    default:
      return null;
    }
  }

  render() {
    const errosList = (
      this.state.errors.map((error, index) => (
        <ListItem key={`email-error-${index.toString()}`} style={{ color: 'red' }} className="team-invite-members__list-item-error">
          {this.renderError(error)}
        </ListItem>
      ))
    );
    const excludeRoles = UserUtil.myRole(this.getCurrentUser(), this.props.team.slug) === 'owner' ? [] : ['owner'];
    let inviteBody = null;
    if (this.state.addMany) {
      inviteBody = (
        this.state.membersToInvite.map((member, index) => (
          <div key={`invite-team-member-new-${index.toString()}`}>
            <FlexRow>
              {<FormattedMessage id="teamInviteMembers.inviteMembers" defaultMessage="Members will invited as" />}
              <RoleSelect
                className="invite-member-email-role"
                onChange={e => this.handleRoleChange(e, index)}
                value={member.role}
                excludeRoles={excludeRoles}
              />
            </FlexRow>
            <TextField
              key="invite-member-email-input"
              id={index.toString()}
              className="invite-member-email-input"
              label={this.props.intl.formatMessage(messages.inviteEmailMultipleInput)}
              multiline
              fullWidth
              margin="normal"
              variant="outlined"
              rows={4}
              onChange={e => this.handleEmailChange(e, index)}
              value={member.email}
              error={member.error.length > 0}
              helperText={
                member.error.map(errorItem => (
                  ` "${errorItem.email}": ${this.renderError(errorItem)}`
                ))
              }
            />

        ))
      );
    } else {
      inviteBody = (
        this.state.membersToInvite.map((member, index) => (
          <div key={`invite-team-member-new-${index.toString()}`}>
            <Row>
              <TextField
                key="invite-member-email-input"
                className="invite-member-email-input"
                id={index.toString()}
                placeholder={this.props.intl.formatMessage(messages.inviteEmailInput)}
                onChange={e => this.handleEmailChange(e, index)}
                value={member.email}
                error={member.error.length > 0}
                helperText={member.error.length === 0 ? null : this.renderError(member.error[0])}
                margin="normal"
                fullWidth
              />
              <Row
                style={{
                  marginLeft: units(2),
                  marginTop: units(1),
                  marginBottom: units(1),
                }}
              >
                <RoleSelect
                  className="invite-member-email-role"
                  onChange={e => this.handleRoleChange(e, index)}
                  value={member.role}
                  excludeRoles={excludeRoles}
                />

              </Row>
            </Row>

        ))
      );
    }

    return (
      <FlexRow>
        <RaisedButton
          style={{ marginLeft: 'auto', marginRight: units(2) }}
          onClick={this.handleOpenDialog.bind(this)}
          className="team-members__edit-button"
          label={
            <FormattedMessage id="teamInviteMembers.inviteMember" defaultMessage="Invite" />
          }
        />
        <Dialog
          className="team-invite-members__dialog"
          open={this.state.dialogOpen}
          onClose={this.handleCloseDialog.bind(this)}
          scroll="paper"
          fullWidth
        >
          <DialogTitle>{this.props.intl.formatMessage(messages.inviteMembers)}</DialogTitle>
          <DialogContent>
            { errosList }
            <TextField
              id="invite-msg-input"
              className="team-invite-members__input"
              label={
                <FormattedMessage id="teamInviteMembers.customInvitation" defaultMessage="Invitation note (optional)" />
              }
              fullWidth
              multiline
              rows={2}
              margin="normal"
              variant="outlined"
            />
            { inviteBody }
            { this.state.addMany ?

                <Row>
                  <Button
                    className="team-invite-members__dialog-add-separate-button"
                    onClick={this.handleAddSeparate.bind(this)}
                  >
                    <FormattedMessage id="teamInviteMembers.addSeparate" defaultMessage="Add separate" />
                  </Button>
                </Row>

 :

                <Row>
                  <Button
                    className="team-invite-members__dialog-add-another-button"
                    onClick={this.handleAddAnother.bind(this)}
                  >
                    <FormattedMessage id="teamInviteMembers.addAnother" defaultMessage="Add another" />
                  </Button>
                  <Button
                    className="team-invite-members__dialog-add-many-button"
                    onClick={this.handleAddMany.bind(this)}
                  >
                    <FormattedMessage id="teamInviteMembers.addMany" defaultMessage="Add many" />
                  </Button>
                </Row>

            }
          </DialogContent>
          <DialogActions>
            <Button onClick={this.handleCloseDialog.bind(this)}>
              <FormattedMessage id="teamInviteMembers.cancelAdd" defaultMessage="Cancel" />
            </Button>
            <Button
              className="team-invite-members__dialog-submit-button"
              onClick={this.handleSubmit.bind(this)}
              color="primary"
              disabled={this.state.sendDisabled}
            >
              <FormattedMessage id="teamInviteMembers.send" defaultMessage="Send" />
            </Button>
          </DialogActions>
        </Dialog>
      </FlexRow>
    );
  }
}

TeamInviteMembers.contextTypes = {
  store: PropTypes.object,
  setMessage: PropTypes.func,
};
export default injectIntl(TeamInviteMembers);
<|endoftext|>


<commit_before>/*jslint white: true, browser: true, devel: true,  nomen: true, todo: true */
/**
 *  jquery-biblio
 *
 * Copyright (c) 2015 Owen Beresford, All rights reserved.
 * I have not signed a total rights contract, my employer isn't relevant.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * jquery-readingDuration ~ a simple plugin to display average reading time of articles
 *
 * @author: Owen beresford owencanprogram@fastmail.fm
 * @version: 0.1.1
 * @date: 20/11/2015
 * @licence: AGPL <http://www.gnu.org/licenses/agpl-3.0.html>
 *
 * deps:
 *  jQuery must already be loaded
 *
These are the options that are currently supported:
 ** debug ~ default: 0 ~ how much information to dump to the console
 ** linkTo ~ default: null ~ if you want a full page rather than popup, set this to your page,
 ** dataLocation ~ default: '.blocker' (to suit me) ~ what element to take as the source of the raw data
 ** target ~ default: '#shareGroup' (to suit me) ~ where to put the generated text
 ** timeFormat ~ default 'm' ~ US people like putting things in a weird order, so that may be set by the dev
 ** wordPerMin ~ default: 275 ~ the number of words that it is expected that a person can read per minute.  Change if you know the audience is abnormal (i.e. expected age is 5 or all PHDs)
 ** refresh ~ default: 0 ~ whether to delete the contents of the target first? Useful if this is a repeat call, as you changed the page content
 ** callbacks ~ defaults :
   ~ shortDisplay
   ~ longDisplay

*/

(function($){
	"use strict";

	/**
	 * readingDuration ~ jQuery style constructor
	 *
	 * @param DOMElement el
	 * @param array options ~ see doc header
	 * @access public
	 * @return <object>
	 */
	$.readingImpl = function(options) {
// msie 8
		if (window.attachEvent && !window.addEventListener) {
			if(options.debug) {
				alert("readingTime() WARNING: Ancient browser, hopefully everything still computes.\n(Dev import es5-shim to avoid this message.)");
			}
		}

		/**
		 * BibliographyExtractor
		 *
		 * @param DOMElement el
		 * @param array options
		 * @return <self>
		 */
		function ReadingDuration( options) {
// http://stackoverflow.com/questions/171251/how-can-i-merge-properties-of-two-javascript-objects-dynamically
	        this.options = $.extend({}, $.readingImpl.defaultOptions, options);

			var duration= ($(this.options.dataLocation)
							.text()
							.match(/\b[^ (),;.\t\n]{3,}\b/g)
							.length)/this.options.wordPerMin * 60;
			duration += $(this.options.dataLocation).find('img').length * 12;
			// extend to pull CSS background-image 's, but that is quite slow with current jQuery

			this.options.injectedID="id"+new Date().getTime()/1000;
			var text=this.options.callbacks.displayShort.apply(this, [duration, this.options]);
			if(this.options.refresh ) {
				var tt=$(this.options.target+" a.reading").remove();
			}
			$(this.options.target).append(text);
			text=this.options.callbacks.displayLong.apply(this, [duration, this.options]);
			if(this.options.refresh ) {
				$(this.options.injectedID).remove();
			}
			$('body').append(text);

			return this;
		}

	    return new ReadingDuration( options);
	};

// pls see doc header
	$.readingImpl.defaultOptions = {
		debug:0,
		linkTo:null,
		timeFormat:'m', // US people like putting things in a weird order, so that may be set by the dev
		dataLocation:'.blocker',
		target:'#shareGroup',
		injectedID:'',
		wordPerMin:275,
		refresh:0,
		callbacks:{
			'displayShort':f1,
			'displayLong':f2,
				},
	};

	/**
	 * readingDuration ~ a simple plugin to display average reading time of articles

	 *
	 * @param array options
	 * @access public
	 * @return void;
	 */
	$.fn.readingDuration= function(options) {
		try {
			$.readingImpl(options);
		} catch( $e) { // this should only happen in dev..
			console.log($e);
		}
	};

// if you replace these, please keep the
	function f1(duration, param) {
		duration=Math.round(duration/60);
		if(param.linkTo) {
			return "Reading time: "+duration+"m";
		} else {
			// ugly...
			return "Reading time: "+duration+"m";
		}

	}

	function f2(duration, param) {
		duration=Math.round(duration/60);

		return "
 Author needs to add more text here.
";
	}

}(jQuery));

<commit_msg>the long block is valueless for linkTo<commit_after>/*jslint white: true, browser: true, devel: true,  nomen: true, todo: true */
/**
 *  jquery-biblio
 *
 * Copyright (c) 2015 Owen Beresford, All rights reserved.
 * I have not signed a total rights contract, my employer isn't relevant.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * jquery-readingDuration ~ a simple plugin to display average reading time of articles
 *
 * @author: Owen beresford owencanprogram@fastmail.fm
 * @version: 0.1.1
 * @date: 20/11/2015
 * @licence: AGPL <http://www.gnu.org/licenses/agpl-3.0.html>
 *
 * deps:
 *  jQuery must already be loaded
 *
These are the options that are currently supported:
 ** debug ~ default: 0 ~ how much information to dump to the console
 ** linkTo ~ default: null ~ if you want a full page rather than popup, set this to your page,
 ** dataLocation ~ default: '.blocker' (to suit me) ~ what element to take as the source of the raw data
 ** target ~ default: '#shareGroup' (to suit me) ~ where to put the generated text
 ** timeFormat ~ default 'm' ~ US people like putting things in a weird order, so that may be set by the dev
 ** wordPerMin ~ default: 275 ~ the number of words that it is expected that a person can read per minute.  Change if you know the audience is abnormal (i.e. expected age is 5 or all PHDs)
 ** refresh ~ default: 0 ~ whether to delete the contents of the target first? Useful if this is a repeat call, as you changed the page content
 ** callbacks ~ defaults :
   ~ shortDisplay
   ~ longDisplay

*/

(function($){
	"use strict";

	/**
	 * readingDuration ~ jQuery style constructor
	 *
	 * @param DOMElement el
	 * @param array options ~ see doc header
	 * @access public
	 * @return <object>
	 */
	$.readingImpl = function(options) {
// msie 8
		if (window.attachEvent && !window.addEventListener) {
			if(options.debug) {
				alert("readingTime() WARNING: Ancient browser, hopefully everything still computes.\n(Dev import es5-shim to avoid this message.)");
			}
		}

		/**
		 * BibliographyExtractor
		 *
		 * @param DOMElement el
		 * @param array options
		 * @return <self>
		 */
		function ReadingDuration( options) {
// http://stackoverflow.com/questions/171251/how-can-i-merge-properties-of-two-javascript-objects-dynamically
	        this.options = $.extend({}, $.readingImpl.defaultOptions, options);

			var duration= ($(this.options.dataLocation)
							.text()
							.match(/\b[^ (),;.\t\n]{3,}\b/g)
							.length)/this.options.wordPerMin * 60;
			duration += $(this.options.dataLocation).find('img').length * 12;
			// extend to pull CSS background-image 's, but that is quite slow with current jQuery

			this.options.injectedID="id"+new Date().getTime()/1000;
			var text=this.options.callbacks.displayShort.apply(this, [duration, this.options]);
			if(this.options.refresh ) {
				var tt=$(this.options.target+" a.reading").remove();
			}
			$(this.options.target).append(text);
			if(! this.options.linkTo) {
				text=this.options.callbacks.displayLong.apply(this, [duration, this.options]);
				if(this.options.refresh ) {
					$(this.options.injectedID).remove();
				}
				$('body').append(text);
			}
			return this;
		}

	    return new ReadingDuration( options);
	};

// pls see doc header
	$.readingImpl.defaultOptions = {
		debug:0,
		linkTo:null,
		timeFormat:'m', // US people like putting things in a weird order, so that may be set by the dev
		dataLocation:'.blocker',
		target:'#shareGroup',
		injectedID:'',
		wordPerMin:275,
		refresh:0,
		callbacks:{
			'displayShort':f1,
			'displayLong':f2,
				},
	};

	/**
	 * readingDuration ~ a simple plugin to display average reading time of articles

	 *
	 * @param array options
	 * @access public
	 * @return void;
	 */
	$.fn.readingDuration= function(options) {
		try {
			$.readingImpl(options);
		} catch( $e) { // this should only happen in dev..
			console.log($e);
		}
	};

// if you replace these, please keep the
	function f1(duration, param) {
		duration=Math.round(duration/60);
		if(param.linkTo) {
			return "Reading time: "+duration+"m";
		} else {
			// ugly...
			return "Reading time: "+duration+"m";
		}

	}

	function f2(duration, param) {
		duration=Math.round(duration/60);

		return "
 Author needs to add more text here.
";
	}

}(jQuery));

<|endoftext|>


<commit_before>//
// A minimal Maybe monad.
//
// Maybe's are a way of making compact sequences of transformations
// while avoiding runaway error handling. This is especially useful for
// adding guards to existing-code without needing to "break it up" into
// imperative steps.
//
// Think of them like Promise then/catch, but for synchronous code.
//

export const Identity = x => x

/**
 * Nothing monad. One half of {@link Maybe}.
 * @returns {Nothing} A Nothing.
 * @example
 * const nothing = Nothing('lost forever')
 * nothing.valueOf()
 * // undefined
 *
 * nothing
 *   .map(x => `Contents: ${x}`)
 *   .valueOf()
 * // undefined
 *
 * nothing
 *   .orElse(() => Just('fallback'))
 *   .map(x => `Contents: ${x}`)
 *   .valueOf()
 * // "Contents: fallback"
 */
export const Nothing = () => ({
  valueOf: () => undefined,
  toString: () => 'Nothing',
  map: () => Nothing(),
  chain: () => Nothing(),
  fold: (f /*, _*/) => f(),
  orElse: f => f(),
  ap: () => Nothing(),
  isNothing: true,
  isJust: false
})

Nothing.of = () => Nothing()

/**
 * Just monad. One half of {@link Maybe}.
 * @returns {Just} A Just.
 * @example
 * const just = Just(42)
 * just.valueOf()
 * // 42
 *
 * just
 *   .map(x => `Contents: ${x}`)
 *   .map(console.log)
 * // "Contents: 42"
 *
 * just
 *   .orElse(() => Just('fallback'))
 *   .map(x => `Contents: ${x}`)
 *   .valueOf()
 * // "Contents: 42"
 */
export const Just = x => ({
  valueOf: () => x,
  toString: () => `Just(${x})`,
  map: f => Just(f(x)),
  chain: f => f(x),
  fold: (_, g) => g(x),
  orElse: () => Just(x),
  ap: m => m.map(x),
  isNothing: false,
  isJust: true
})

Just.of = x => Just(x)

/**
 * Maybe factory. Make Maybe's that infer {@link Just}/{@link Nothing}
 * from a predicate.
 *
 * @param {function} predicate
 * @returns {Maybe}  A Maybe that uses the given predicate
 * to determine if construction will produce a Just or a Nothing.
 *
 * @example
 * const MaybeNumber = safe(x => !Number.isNaN(x))
 *
 * const result = MaybeNumber('not a number')
 *   .fold(
 *     () => 0,
 *     (x) => x + 1
 *   )
 *
 * result === 0 // true
 *
 */
export const safe = (predicate = x => x != null) => {
  const Maybe = x => {
    return predicate(x) ? Just(x) : Nothing()
  }
  Maybe.of = x => Maybe(x)
  return Maybe
}

/**
 * Return a {@link Maybe} that will be {@link Nothing} if the provided
 * function throws. If it doesn't throw, a {@link Just} of the result
 * will be returned.
 *
 * @example
 * maybeTry(msg => { throw msg; })('fit')
 *   .map(() => 'that did not throw')
 *   .orElse(() => Just('that threw'))
 *   .valueOf()
 * // "that threw"
 */
export const maybeTry = f => x => {
  try {
    return Just(f(x))
  } catch (e) {
    return Nothing()
  }
}

/**
 * Standard Maybe monad.
 *
 * @function
 * @returns {Maybe} Returns {@link Just} if the passed-in (lifted)
 * value is not nullish, otherwise will return {@link Nothing}
 * @example
 * const nothing = Maybe(undefined)
 * const just = Maybe(42)
 *
 * nothing.valueOf()
 * // undefined
 *
 * nothing
 *   .orElse(() => Just('fallback'))
 *   .map(x => `Contents: ${x}`)
 *   .valueOf()
 * // "Contents: fallback"
 *
 * just.valueOf()
 * // 42
 *
 * just
 *   .orElse(() => Just('fallback'))
 *   .map(x => `Contents: ${x}`)
 *   .valueOf()
 * // "Contents: 42"
 *
 */
export const Maybe = safe()
<commit_msg>fork != fold<commit_after>//
// A minimal Maybe monad.
//
// Maybe's are a way of making compact sequences of transformations
// while avoiding runaway error handling. This is especially useful for
// adding guards to existing-code without needing to "break it up" into
// imperative steps.
//
// Think of them like Promise then/catch, but for synchronous code.
//

export const Identity = x => x

/**
 * Nothing monad. One half of {@link Maybe}.
 * @returns {Nothing} A Nothing.
 * @example
 * const nothing = Nothing('lost forever')
 * nothing.valueOf()
 * // undefined
 *
 * nothing
 *   .map(x => `Contents: ${x}`)
 *   .valueOf()
 * // undefined
 *
 * nothing
 *   .orElse(() => Just('fallback'))
 *   .map(x => `Contents: ${x}`)
 *   .valueOf()
 * // "Contents: fallback"
 */
export const Nothing = () => ({
  valueOf: () => undefined,
  toString: () => 'Nothing',
  map: () => Nothing(),
  chain: () => Nothing(),
  fork: (f /*, _*/) => f(),
  orElse: f => f(),
  ap: () => Nothing(),
  isNothing: true,
  isJust: false
})

Nothing.of = () => Nothing()

/**
 * Just monad. One half of {@link Maybe}.
 * @returns {Just} A Just.
 * @example
 * const just = Just(42)
 * just.valueOf()
 * // 42
 *
 * just
 *   .map(x => `Contents: ${x}`)
 *   .map(console.log)
 * // "Contents: 42"
 *
 * just
 *   .orElse(() => Just('fallback'))
 *   .map(x => `Contents: ${x}`)
 *   .valueOf()
 * // "Contents: 42"
 */
export const Just = x => ({
  valueOf: () => x,
  toString: () => `Just(${x})`,
  map: f => Just(f(x)),
  chain: f => f(x),
  fork: (_, g) => g(x),
  orElse: () => Just(x),
  ap: m => m.map(x),
  isNothing: false,
  isJust: true
})

Just.of = x => Just(x)

/**
 * Maybe factory. Make Maybe's that infer {@link Just}/{@link Nothing}
 * from a predicate.
 *
 * @param {function} predicate
 * @returns {Maybe}  A Maybe that uses the given predicate
 * to determine if construction will produce a Just or a Nothing.
 *
 * @example
 * const MaybeNumber = safe(x => !Number.isNaN(x))
 *
 * const result = MaybeNumber('not a number')
 *   .fork(
 *     () => 0,
 *     (x) => x + 1
 *   )
 *
 * result === 0 // true
 *
 */
export const safe = (predicate = x => x != null) => {
  const Maybe = x => {
    return predicate(x) ? Just(x) : Nothing()
  }
  Maybe.of = x => Maybe(x)
  return Maybe
}

/**
 * Return a {@link Maybe} that will be {@link Nothing} if the provided
 * function throws. If it doesn't throw, a {@link Just} of the result
 * will be returned.
 *
 * @example
 * maybeTry(msg => { throw msg; })('fit')
 *   .map(() => 'that did not throw')
 *   .orElse(() => Just('that threw'))
 *   .valueOf()
 * // "that threw"
 */
export const maybeTry = f => x => {
  try {
    return Just(f(x))
  } catch (e) {
    return Nothing()
  }
}

/**
 * Standard Maybe monad.
 *
 * @function
 * @returns {Maybe} Returns {@link Just} if the passed-in (lifted)
 * value is not nullish, otherwise will return {@link Nothing}
 * @example
 * const nothing = Maybe(undefined)
 * const just = Maybe(42)
 *
 * nothing.valueOf()
 * // undefined
 *
 * nothing
 *   .orElse(() => Just('fallback'))
 *   .map(x => `Contents: ${x}`)
 *   .valueOf()
 * // "Contents: fallback"
 *
 * just.valueOf()
 * // 42
 *
 * just
 *   .orElse(() => Just('fallback'))
 *   .map(x => `Contents: ${x}`)
 *   .valueOf()
 * // "Contents: 42"
 *
 */
export const Maybe = safe()
<|endoftext|>


<commit_before>"use strict";
const moment = require('moment');
const fs = require('fs');
const EventEmitter = require('./EventEmitter');

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

var output = {
    file: false,
    console: true
};

var config = {
    file: null,
    stackTraceEnabled: true,
    level: Level.INFO
};

var write = function (string) {
    if (output.console) {
        console.log(string);
    }
    if (output.file && config.file) {
        fs.appendFile(config.file, string + "\n");
    }
};

var realLog = function (level, message, source) {
    var payload = {
        type: level,
        message: message,
        source: source
    };
    var levelString;
    if(level.colorModifier) {
        levelString = level.colorModifier(level.str);
    } else {
        levelString = level.str;
    }
    var logString = moment().format("YYYY-MM-DD HH:mm:ss,SSS") + " " + levelString + " [" + source + "] " + message;
    write(logString);

    if (config.stackTraceEnabled && level.printStackTraces) {
        var stack = new Error().stack;
        //remove Error + 1st and 2nd line
        stack = stack.replace(new RegExp("Error\n[^\n]+\n[^\n]+\n"), "");
        payload.stackTrace = stack;
        write(stack);
    }
    EventEmitter.emit(level.level, payload);
};

var isConfigLevelHigher = function(level) {
    return config.level.int > level.int;
};

function Logger(source) {
    if (!(this instanceof Logger)) {
        return new Logger(source);
    }
    if (source === undefined) {
        this.source = "main";
    } else {
        this.source = source;
    }
}

Logger.prototype.debug = function (message) {
    if(isConfigLevelHigher(Level.DEBUG)) return;
    realLog(Level.DEBUG, message, this.source);
};

Logger.prototype.info = function (message) {
    if(isConfigLevelHigher(Level.INFO)) return;
    realLog(Level.INFO, message, this.source);
};

Logger.prototype.warn = function (message) {
    if(isConfigLevelHigher(Level.WARN)) return;
    realLog(Level.WARN, message, this.source);
};

Logger.prototype.error = function (message) {
    if(isConfigLevelHigher(Level.ERROR)) return;
    realLog(Level.ERROR, message, this.source);
};

Logger.prototype.fatal = function (message) {
    if(isConfigLevelHigher(Level.FATAL)) return;
    realLog(Level.FATAL, message, this.source);
};

Logger.getLogger = function (source) {
    return new Logger(source);
};

Logger.setConsoleOutput = function (bool) {
    output.console = bool;
};

Logger.setFileOutput = function (bool) {
    output.file = bool;
};

Logger.setFilename = function (file) {
    config.file = file;
};

Logger.setLevel = function(level) {
    config.level = level;
};

Logger.setStackTraceEnabled = function (bool) {
    config.stackTraceEnabled = bool;
};

module.exports = Logger;<commit_msg>emit events with "$"<commit_after>"use strict";
const moment = require('moment');
const fs = require('fs');
const EventEmitter = require('./EventEmitter');

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

var output = {
    file: false,
    console: true
};

var config = {
    file: null,
    stackTraceEnabled: true,
    level: Level.INFO
};

var write = function (string) {
    if (output.console) {
        console.log(string);
    }
    if (output.file && config.file) {
        fs.appendFile(config.file, string + "\n");
    }
};

var realLog = function (level, message, source) {
    var payload = {
        type: level,
        message: message,
        source: source
    };
    var levelString;
    if(level.colorModifier) {
        levelString = level.colorModifier(level.str);
    } else {
        levelString = level.str;
    }
    var logString = moment().format("YYYY-MM-DD HH:mm:ss,SSS") + " " + levelString + " [" + source + "] " + message;
    write(logString);

    if (config.stackTraceEnabled && level.printStackTraces) {
        var stack = new Error().stack;
        //remove Error + 1st and 2nd line
        stack = stack.replace(new RegExp("Error\n[^\n]+\n[^\n]+\n"), "");
        payload.stackTrace = stack;
        write(stack);
    }
    EventEmitter.emit("$" + level.level, payload);
};

var isConfigLevelHigher = function(level) {
    return config.level.int > level.int;
};

function Logger(source) {
    if (!(this instanceof Logger)) {
        return new Logger(source);
    }
    if (source === undefined) {
        this.source = "main";
    } else {
        this.source = source;
    }
}

Logger.prototype.debug = function (message) {
    if(isConfigLevelHigher(Level.DEBUG)) return;
    realLog(Level.DEBUG, message, this.source);
};

Logger.prototype.info = function (message) {
    if(isConfigLevelHigher(Level.INFO)) return;
    realLog(Level.INFO, message, this.source);
};

Logger.prototype.warn = function (message) {
    if(isConfigLevelHigher(Level.WARN)) return;
    realLog(Level.WARN, message, this.source);
};

Logger.prototype.error = function (message) {
    if(isConfigLevelHigher(Level.ERROR)) return;
    realLog(Level.ERROR, message, this.source);
};

Logger.prototype.fatal = function (message) {
    if(isConfigLevelHigher(Level.FATAL)) return;
    realLog(Level.FATAL, message, this.source);
};

Logger.getLogger = function (source) {
    return new Logger(source);
};

Logger.setConsoleOutput = function (bool) {
    output.console = bool;
};

Logger.setFileOutput = function (bool) {
    output.file = bool;
};

Logger.setFilename = function (file) {
    config.file = file;
};

Logger.setLevel = function(level) {
    config.level = level;
};

Logger.setStackTraceEnabled = function (bool) {
    config.stackTraceEnabled = bool;
};

module.exports = Logger;<|endoftext|>


<commit_before>module.exports = function (isExpress) {
  return function (err, req, res, next) {
    res.setHeader('Content-Type', 'application/json')

    if (isExpress) {
      res.status(err.statusCode).json(err)
    } else {
      res.send(err.statusCode, JSON.parse(JSON.stringify(err)))
    }
  }
}
<commit_msg>fix(onError): set a default status code<commit_after>module.exports = function (isExpress) {
  return function (err, req, res, next) {
    res.setHeader('Content-Type', 'application/json')

    if (isExpress) {
      res.status(err.statusCode || 500).json(err)
    } else {
      res.send(err.statusCode || 500, JSON.parse(JSON.stringify(err)))
    }
  }
}
<|endoftext|>


<commit_before>'use strict';

var gulp = require('gulp');
var gutil = require('gulp-util');
var webpack = require('webpack');
var minifyHTML = require('gulp-minify-html');
var clean = require('gulp-clean');
var gulpSequence = require('gulp-sequence');
var WebpackDevServer = require("webpack-dev-server");

//begin development

gulp.task("webpack-dev", function(callback) {

  var compiler = webpack({
    context: __dirname + '/src',
    entry: [
      'webpack/hot/dev-server',
      'webpack-dev-server/client?http://localhost:9000',
      './app.js',
      './index.html'
    ],
    output: {
      path: __dirname + '/src',
      filename: 'bundle.js'
    },
    module: {
      loaders: [
        {
          test: /\.js$/,
          exclude: /node_modules/,
          loader: 'babel-loader',
          query: {
            presets: ['es2015']
          }
        }, {
          test: /\.(png|jpg|jpeg|gif|svg|woff|woff2|ttf|eot|ico)$/,
          loader: 'file'
        }, {
          test: /\.html$/,
          exclude: /node_modules/,
          loader: 'raw'
        },
        {
          test: /\.scss$/,
          loader: 'style!css!sass'
        }, {
          test: /\.css$/,
          loader: 'style-loader!css-loader'
        }
      ]
    },
    plugins: [
      new webpack.optimize.UglifyJsPlugin({
        include: /\.min\.js$/,
        minimize: true,
        compress: {
          warnings: false
        }
      }),
      new webpack.HotModuleReplacementPlugin()
    ]

  });

  new WebpackDevServer(compiler, {
    hot: true,
    contentBase: './src/',
    proxy: [
      {
        path: '/api/*',
        target: 'http://localhost:3000'
      }, {
        path: '/authenticate',
        target: 'http://localhost:3000'
      }
    ],
    stats: {
      colors: true
    }
  }).listen(9000, "localhost", function(err) {
    if (err)
      throw new gutil.PluginError("webpack-dev-server", err);
    gutil.log("[webpack-dev-server]", "http://localhost:9000");

  });
});

gulp.task('server', ['webpack-dev']);

//end development

//begin production

gulp.task('minify-html', function() {
  gulp.src('src/*.html')
    .pipe(minifyHTML({
      empty: true
    }))
    .pipe(gulp.dest('./dist'));
});

gulp.task('clean', function() {
  return gulp.src('dist', {
    read: false
  })
    .pipe(clean());
});

gulp.task('webpack-pro', function(callback) {
  webpack({
    context: __dirname + '/app',
    entry: './app.js',
    output: {
      path: __dirname + '/dist',
      filename: 'bundle.min.js',
      chunkFilename: '[id].bundle.js'
    },
    module: {
      loaders: [
        {
          test: /\.js$/,
          exclude: /node_modules/,
          loader: 'babel-loader',
          query: {
            presets: ['es2015']
          }
        }, {
          test: /\.(png|jpg|jpeg|gif|svg|woff|woff2|ttf|eot|ico)$/,
          loader: 'file'
        }, {
          test: /\.html$/,
          exclude: /node_modules/,
          loader: 'raw'
        }, {
          test: /\.scss$/,
          loader: 'style!css!sass'
        }, {
          test: /\.css$/,
          loader: 'style-loader!css-loader'
        }
      ]
    },
    plugins: [
      new webpack.optimize.UglifyJsPlugin({
        include: /\.min\.js$/,
        minimize: true,
        compress: {
          warnings: false
        }
      })
    ]
  }, function(err, stats) {
    if (err)
      throw new gutil.PluginError('webpack', err);
    gutil.log('[webpack]', stats.toString({

    }));
    callback();
  });
});

gulp.task('server-build', function() {
  connect.server({
    root: 'dist',
    livereload: true
  });
});

gulp.task('build', gulpSequence('clean', 'webpack', 'minify-html'));

//end production
<commit_msg>Tambah proxy register<commit_after>'use strict';

var gulp = require('gulp');
var gutil = require('gulp-util');
var webpack = require('webpack');
var minifyHTML = require('gulp-minify-html');
var clean = require('gulp-clean');
var gulpSequence = require('gulp-sequence');
var WebpackDevServer = require("webpack-dev-server");

//begin development

gulp.task("webpack-dev", function(callback) {

  var compiler = webpack({
    context: __dirname + '/src',
    entry: [
      'webpack/hot/dev-server',
      'webpack-dev-server/client?http://localhost:9000',
      './app.js',
      './index.html'
    ],
    output: {
      path: __dirname + '/src',
      filename: 'bundle.js'
    },
    module: {
      loaders: [
        {
          test: /\.js$/,
          exclude: /node_modules/,
          loader: 'babel-loader',
          query: {
            presets: ['es2015']
          }
        }, {
          test: /\.(png|jpg|jpeg|gif|svg|woff|woff2|ttf|eot|ico)$/,
          loader: 'file'
        }, {
          test: /\.html$/,
          exclude: /node_modules/,
          loader: 'raw'
        },
        {
          test: /\.scss$/,
          loader: 'style!css!sass'
        }, {
          test: /\.css$/,
          loader: 'style-loader!css-loader'
        }
      ]
    },
    plugins: [
      new webpack.optimize.UglifyJsPlugin({
        include: /\.min\.js$/,
        minimize: true,
        compress: {
          warnings: false
        }
      }),
      new webpack.HotModuleReplacementPlugin()
    ]

  });

  new WebpackDevServer(compiler, {
    hot: true,
    contentBase: './src/',
    proxy: [
      {
        path: '/api/*',
        target: 'http://localhost:3000'
      }, {
        path: '/authenticate',
        target: 'http://localhost:3000'
      }, {
        path: '/register',
        target: 'http://localhost:3000'
      }
    ],
    stats: {
      colors: true
    }
  }).listen(9000, "localhost", function(err) {
    if (err)
      throw new gutil.PluginError("webpack-dev-server", err);
    gutil.log("[webpack-dev-server]", "http://localhost:9000");

  });
});

gulp.task('server', ['webpack-dev']);

//end development

//begin production

gulp.task('minify-html', function() {
  gulp.src('src/*.html')
    .pipe(minifyHTML({
      empty: true
    }))
    .pipe(gulp.dest('./dist'));
});

gulp.task('clean', function() {
  return gulp.src('dist', {
    read: false
  })
    .pipe(clean());
});

gulp.task('webpack-pro', function(callback) {
  webpack({
    context: __dirname + '/app',
    entry: './app.js',
    output: {
      path: __dirname + '/dist',
      filename: 'bundle.min.js',
      chunkFilename: '[id].bundle.js'
    },
    module: {
      loaders: [
        {
          test: /\.js$/,
          exclude: /node_modules/,
          loader: 'babel-loader',
          query: {
            presets: ['es2015']
          }
        }, {
          test: /\.(png|jpg|jpeg|gif|svg|woff|woff2|ttf|eot|ico)$/,
          loader: 'file'
        }, {
          test: /\.html$/,
          exclude: /node_modules/,
          loader: 'raw'
        }, {
          test: /\.scss$/,
          loader: 'style!css!sass'
        }, {
          test: /\.css$/,
          loader: 'style-loader!css-loader'
        }
      ]
    },
    plugins: [
      new webpack.optimize.UglifyJsPlugin({
        include: /\.min\.js$/,
        minimize: true,
        compress: {
          warnings: false
        }
      })
    ]
  }, function(err, stats) {
    if (err)
      throw new gutil.PluginError('webpack', err);
    gutil.log('[webpack]', stats.toString({

    }));
    callback();
  });
});

gulp.task('server-build', function() {
  connect.server({
    root: 'dist',
    livereload: true
  });
});

gulp.task('build', gulpSequence('clean', 'webpack', 'minify-html'));

//end production
<|endoftext|>


<commit_before>import Polymer from '../../polymer';
import hass from '../../util/home-assistant-js-instance';
import domainIcon from '../../util/domain-icon';
import canToggle from '../../util/can-toggle';

require('../../components/ha-label-badge');

const {
  moreInfoActions,
  serviceActions,
} = hass;

export default new Polymer({
  is: 'ha-state-label-badge',

  properties: {
    state: {
      type: Object,
      observer: 'stateChanged',
    },
  },

  listeners: {
    'click': 'badgeTap',
  },

  badgeTap(ev) {
    ev.stopPropagation();
    if (!canToggle(this.state.entityId)) {
      this.async(() => moreInfoActions.selectEntity(this.state.entityId), 1);
      return;
    }
    if (this.state.domain === 'scene') {
      serviceActions.callTurnOn(this.state.entityId);
    } else if (this.state.state === 'off') {
      serviceActions.callTurnOn(this.state.entityId);
    } else {
      serviceActions.callTurnOff(this.state.entityId);
    }
  },

  computeClasses(state) {
    switch (state.domain) {
    case 'scene':
      return 'green';
    case 'binary_sensor':
    case 'script':
      return state.state === 'on' ? 'blue' : 'grey';
    case 'updater':
      return 'blue';
    default:
      return '';
    }
  },

  computeValue(state) {
    switch (state.domain) {
    case 'binary_sensor':
    case 'device_tracker':
    case 'updater':
    case 'sun':
    case 'scene':
    case 'script':
    case 'alarm_control_panel':
      return undefined;
    case 'sensor':
      return state.state;
    default:
      return state.state;
    }
  },

  computeIcon(state) {
    switch (state.domain) {
    case 'alarm_control_panel':
      if (state.state === 'pending') {
        return 'mdi:clock-fast';
      } else if (state.state === 'armed_away') {
        return 'mdi:nature';
      } else if (state.state === 'armed_home') {
        return 'mdi:home-variant';
      }
      // state == 'disarmed'
      return domainIcon(state.domain, state.state);
    case 'binary_sensor':
    case 'device_tracker':
    case 'scene':
    case 'updater':
    case 'script':
      return domainIcon(state.domain, state.state);
    case 'sun':
      return state.state === 'above_horizon' ?
        domainIcon(state.domain) : 'mdi:brightness-3';
    default:
      return undefined;
    }
  },

  computeImage(state) {
    return state.attributes.entity_picture;
  },

  computeLabel(state) {
    switch (state.domain) {
    case 'scene':
    case 'script':
      return state.domain;
    case 'device_tracker':
      return state.state === 'not_home' ? 'Away' : state.state;
    case 'alarm_control_panel':
      if (state.state === 'pending') {
        return 'pend';
      } else if (state.state === 'armed_away' || state.state === 'armed_home') {
        return 'armed';
      }
      // state == 'disarmed'
      return 'disarm';
    default:
      return state.attributes.unit_of_measurement;
    }
  },

  computeDescription(state) {
    return state.entityDisplay;
  },

  stateChanged() {
    this.updateStyles();
  },
});
<commit_msg>Prevent tapping to go through overlay<commit_after>import Polymer from '../../polymer';
import hass from '../../util/home-assistant-js-instance';
import domainIcon from '../../util/domain-icon';
import canToggle from '../../util/can-toggle';

require('../../components/ha-label-badge');

const {
  moreInfoActions,
  serviceActions,
} = hass;

export default new Polymer({
  is: 'ha-state-label-badge',

  properties: {
    state: {
      type: Object,
      observer: 'stateChanged',
    },
  },

  listeners: {
    'tap': 'badgeTap',
  },

  badgeTap(ev) {
    ev.stopPropagation();
    if (!canToggle(this.state.entityId)) {
      this.async(() => moreInfoActions.selectEntity(this.state.entityId), 1);
      return;
    }
    if (this.state.domain === 'scene') {
      serviceActions.callTurnOn(this.state.entityId);
    } else if (this.state.state === 'off') {
      serviceActions.callTurnOn(this.state.entityId);
    } else {
      serviceActions.callTurnOff(this.state.entityId);
    }
  },

  computeClasses(state) {
    switch (state.domain) {
    case 'scene':
      return 'green';
    case 'binary_sensor':
    case 'script':
      return state.state === 'on' ? 'blue' : 'grey';
    case 'updater':
      return 'blue';
    default:
      return '';
    }
  },

  computeValue(state) {
    switch (state.domain) {
    case 'binary_sensor':
    case 'device_tracker':
    case 'updater':
    case 'sun':
    case 'scene':
    case 'script':
    case 'alarm_control_panel':
      return undefined;
    case 'sensor':
      return state.state;
    default:
      return state.state;
    }
  },

  computeIcon(state) {
    switch (state.domain) {
    case 'alarm_control_panel':
      if (state.state === 'pending') {
        return 'mdi:clock-fast';
      } else if (state.state === 'armed_away') {
        return 'mdi:nature';
      } else if (state.state === 'armed_home') {
        return 'mdi:home-variant';
      }
      // state == 'disarmed'
      return domainIcon(state.domain, state.state);
    case 'binary_sensor':
    case 'device_tracker':
    case 'scene':
    case 'updater':
    case 'script':
      return domainIcon(state.domain, state.state);
    case 'sun':
      return state.state === 'above_horizon' ?
        domainIcon(state.domain) : 'mdi:brightness-3';
    default:
      return undefined;
    }
  },

  computeImage(state) {
    return state.attributes.entity_picture;
  },

  computeLabel(state) {
    switch (state.domain) {
    case 'scene':
    case 'script':
      return state.domain;
    case 'device_tracker':
      return state.state === 'not_home' ? 'Away' : state.state;
    case 'alarm_control_panel':
      if (state.state === 'pending') {
        return 'pend';
      } else if (state.state === 'armed_away' || state.state === 'armed_home') {
        return 'armed';
      }
      // state == 'disarmed'
      return 'disarm';
    default:
      return state.attributes.unit_of_measurement;
    }
  },

  computeDescription(state) {
    return state.entityDisplay;
  },

  stateChanged() {
    this.updateStyles();
  },
});
<|endoftext|>


<commit_before>
// Elements are shown in this order
var data = [
	{name: "earth",   desc: "Earth",   action:"switch_display", url:"youtube"},
  {name: "doodles", desc: "Doodles", action:"showDoodlesPage"}
];

// Sends ROS message to display to change the browser's URL
var sendSwitchROSMessage = function(url) {
	// TODO invent some protocol message with url inside
  // TODO send proper message
	// TODO add support at display's side
};

var showDoodlesPage = function() {
  // TODO: implement the doodles page
	// TODO: add support for sending the ROS message to switch URL on display
};

var createElementsList = function() {

	var ul = document.createElement('ul');
	for(var i = 0 ; i < data.length ; i++ ) {

		var name   = data[i].name;
		var desc   = data[i].desc;
		var action = data[i].action;

		var li = document.createElement('li');

		var img = document.createElement('img');
		img.setAttribute('src', chrome.extension.getURL("images/" + name + '.jpg'));

    var span = document.createElement('span');
    span.innerText = desc;

		li.appendChild(img);
		li.appendChild(span);

		if (action == "switch_display")	li.onclick = sendSwitchROSMessage(url);
		if (action == "showDoodlesPage") li.onclick = showDoodlesPage();

		ul.appendChild(li);
	}

	return ul;

};

var initializeMoreFun = function() {

  var onclick = function() {
		document.getElementById("morefun_items").style.visibility='visible';
	};

  var s = document.createElement('div');
	s.id = "morefun";
  s.innerText = "More fun --ICON HERE--";
	s.onclick = onclick;
  document.body.appendChild(s);

	var d = document.createElement('div');
	d.id = "morefun_items";

	d.appendChild(createElementsList());
	d.style.visibility='hidden';
	console.log(d);
	document.body.appendChild(d);
};

initializeMoreFun();
<commit_msg>More comments<commit_after>
// Elements are shown in this order
// name 	- used as a key, image name
// desc 	- shown below the image
// action - (switch_display|showDoodlesPage)
// url 		- url to switch by the action switch_display
var data = [
	{name: "earth",   desc: "Earth",   action:"switch_display", url:"youtube"},
  {name: "doodles", desc: "Doodles", action:"showDoodlesPage"}
];

// Sends ROS message to display to change the browser's URL
var sendSwitchROSMessage = function(url) {
	// TODO invent some protocol message with url inside
  // TODO send proper message
	// TODO add support at display's side
};

var showDoodlesPage = function() {
  // TODO: implement the doodles page
	// TODO: add support for sending the ROS message to switch URL on display
};

var createElementsList = function() {

	var ul = document.createElement('ul');
	for(var i = 0 ; i < data.length ; i++ ) {

		var name   = data[i].name;
		var desc   = data[i].desc;
		var action = data[i].action;

		var li = document.createElement('li');

		var img = document.createElement('img');
		img.setAttribute('src', chrome.extension.getURL("images/" + name + '.jpg'));

    var span = document.createElement('span');
    span.innerText = desc;

		li.appendChild(img);
		li.appendChild(span);

		if (action == "switch_display")	li.onclick = sendSwitchROSMessage(url);
		if (action == "showDoodlesPage") li.onclick = showDoodlesPage();

		ul.appendChild(li);
	}

	return ul;

};

var initializeMoreFun = function() {

  var onclick = function() {
		document.getElementById("morefun_items").style.visibility='visible';
	};

  var s = document.createElement('div');
	s.id = "morefun";
  s.innerText = "More fun --ICON HERE--";
	s.onclick = onclick;
  document.body.appendChild(s);

	var d = document.createElement('div');
	d.id = "morefun_items";

	d.appendChild(createElementsList());
	d.style.visibility='hidden';
	console.log(d);
	document.body.appendChild(d);
};

initializeMoreFun();
<|endoftext|>


<commit_before>(function ($, lunr) {

    function initializeSearchField() {
        $('form[data-orchid-search]').submit(function (e) {
            event.preventDefault();

            var $queryEl = $(this).find("input[name=query]");
            var $query = $queryEl.val();
            var $orchidIndicesAllowed = $(this).attr('data-orchid-search');
            if($orchidIndicesAllowed && $orchidIndicesAllowed.length > 0) {
                $orchidIndicesAllowed = $orchidIndicesAllowed.split(",");
            }
            else {
                $orchidIndicesAllowed = null;
            }

            setSearchWorking(true);
            loadOrchidIndex($orchidIndicesAllowed, function () {
                searchOrchidIndex($query, function (results) {
                    displaySearchResults(results, function () {
                        setSearchWorking(false);
                    });
                });
            });
        });
    }

    function setSearchWorking(isWorking) {
        if (isWorking) {
            $('[data-orchid-search-progress]').show();
            $('[data-orchid-search-results]').hide();
        }
        else {
            $('[data-orchid-search-progress]').hide();
            $('[data-orchid-search-results]').show();
        }
        $(window).trigger('orchid.search.working');
    }

    function getOrchidDocuments($orchidIndicesAllowed, cb) {
        if (!window.orchidDocuments) {
            loadRootIndex($orchidIndicesAllowed, function (documents) {
                window.orchidDocuments = documents;
                window.orchidDocumentsMap = {};

                window.orchidDocuments.map(function (document) {
                    window.orchidDocumentsMap[document.link] = document;
                });

                cb(window.orchidDocuments);
            });
        }
        else {
            cb(window.orchidDocuments);
        }
    }

    function loadOrchidIndex($orchidIndicesAllowed, cb) {
        if (!window.orchidIdx) {
            getOrchidDocuments($orchidIndicesAllowed, function (docs) {
                window.orchidIdx = lunr(function () {
                    this.ref('link');
                    this.field('title');
                    this.field('description');
                    this.field('content');
                    this.metadataWhitelist = ['position'];

                    docs.forEach(function (doc) {
                        this.add(doc)
                    }, this)
                });

                cb(window.orchidIdx);
            });
        }
        else {
            cb(window.orchidIdx);
        }
    }

    function searchOrchidIndex(query, cb) {
        setTimeout(function () {
            var results = window.orchidIdx.search(query);
            cb(results);
        }, 1000)
    }

    function displaySearchResults(results, cb) {
        // console.log("displaying results");
        // console.log(results);

        var items = [];

        results.map(function (result) {
            var document = window.orchidDocumentsMap[result.ref];
            var summary = getSearchSummary(result, document);

            var $item = "
";
            $item += (items.length + 1) + ": " + document.title;
            $item += "
";
            if (summary.length > 0) {
                $item += "
" + summary + "
";
            }
            $item += "
";

            items.push($item);
        });

        var $searchResults = $('[data-orchid-search-results] ul');

        $searchResults.empty();
        $searchResults.html(items.join(''));

        cb();
    }

    function getSearchSummary(result, document) {
        var matches = 0;

        var startContextLength = 12;
        var endContextLength = 12;

        var snippets = [];

        // for every matching word
        for (var word in result.matchData.metadata) {

            // for every document field which has that word
            for (var field in result.matchData.metadata[word]) {
                if (field && result.matchData.metadata[word][field].position) {
                    matches += result.matchData.metadata[word][field].position.length;

                    for(var i_pos = 0; i_pos < result.matchData.metadata[word][field].position.length; i_pos++) {
                        if(i_pos === 3) break;

                        var snippetBounds = result.matchData.metadata[word][field].position[i_pos];
                        var pos_start = snippetBounds[0];
                        var pos_end = snippetBounds[0] + snippetBounds[1] + 1;

                        var match_start = snippetBounds[0];
                        var match_end = snippetBounds[0] + snippetBounds[1] + 1;

                        var leadingEllipses = false;
                        var trailingEllipses = false;

                        // add context to the beginning
                        if(pos_start > startContextLength) {
                            pos_start -= startContextLength;
                            leadingEllipses = true;
                        }
                        else {
                            pos_start = 0;
                        }

                        // add context to the end
                        if(pos_end < document[field].length - endContextLength) {
                            pos_end += endContextLength;
                            trailingEllipses = true;
                        }
                        else {
                            pos_end = document[field].length;
                        }

                        var snippet =
                            document[field].substring(pos_start, match_start) +
                            "" + document[field].substring(match_start, match_end) + "" +
                            document[field].substring(match_end, pos_end)
                        ;
                        snippet = snippet.trim();
                        if(leadingEllipses) {
                            snippet = "..." + snippet;
                        }
                        if(trailingEllipses) {
                            snippet = snippet + "...";
                        }
                        snippets.push(snippet);
                    }
                }
            }
        }

        return "" + matches + " matches:
" + snippets.join("
");
    }

// Load indices via AJAX and build a map of documents that can be passed to Lunr
//----------------------------------------------------------------------------------------------------------------------

    function loadRootIndex($orchidIndicesAllowed, done) {
        var allDocs = [];
        $.getJSON(window.site.baseUrl + "/meta/index.json", function (data) {
            var childIndices = data.childrenPages.meta.ownPages;
            var childIndicesFinishedCount = 0;

            childIndices.map(function (indexPage) {
                var shouldLoad = true;
                if($orchidIndicesAllowed) {
                    var isIndexAllowed = false;
                    for(var i = 0; i < $orchidIndicesAllowed.length; i++) {
                        if(indexPage.reference.fileName === $orchidIndicesAllowed[i].trim() + ".index") {
                            isIndexAllowed = true;
                            break;
                        }
                    }
                    shouldLoad = isIndexAllowed;
                }

                if (shouldLoad) {
                    loadIndexPage(indexPage.reference.link,
                        function (document) {
                            allDocs.push(document);
                        },
                        function () {
                            childIndicesFinishedCount++;

                            if (childIndicesFinishedCount === childIndices.length) {
                                done(allDocs);
                            }
                        });
                }
                else {
                    childIndicesFinishedCount++;

                    if (childIndicesFinishedCount === childIndices.length) {
                        done(allDocs);
                    }
                }
            });
        });
    }

    function loadIndexPage(url, cb, done) {
        $.getJSON(url, function (data) {
            getChildDocuments(data, cb);
            done();
        });
    }

    function getChildDocuments(data, cb) {
        for (var key in data.childrenPages) {
            if (data.childrenPages.hasOwnProperty(key)) {
                var doc = data.childrenPages[key];

                if (doc.childrenPages) {
                    getChildDocuments(doc, cb);
                }
                else if (doc.ownPages) {
                    doc.ownPages.map(function (ownPage) {
                        ownPage.link = ownPage.reference.link;
                        ownPage.content = stripTags(ownPage.content);
                        delete ownPage.reference;
                        cb(ownPage);
                    });
                }
            }
        }
    }

    function stripTags(html) {
        var doc = new DOMParser().parseFromString(html, 'text/html');
        return doc.body.textContent || "";
    }

// Initialize searching
//----------------------------------------------------------------------------------------------------------------------

    $(function () {
        initializeSearchField();
    });

})(jQuery, lunr);<commit_msg>Fixes issue in search widget in Firefox<commit_after>(function ($, lunr) {

    function initializeSearchField() {
        $('form[data-orchid-search]').submit(function (e) {
            e.preventDefault();

            var $queryEl = $(this).find("input[name=query]");
            var $query = $queryEl.val();
            var $orchidIndicesAllowed = $(this).attr('data-orchid-search');
            if($orchidIndicesAllowed && $orchidIndicesAllowed.length > 0) {
                $orchidIndicesAllowed = $orchidIndicesAllowed.split(",");
            }
            else {
                $orchidIndicesAllowed = null;
            }

            setSearchWorking(true);
            loadOrchidIndex($orchidIndicesAllowed, function () {
                searchOrchidIndex($query, function (results) {
                    displaySearchResults(results, function () {
                        setSearchWorking(false);
                    });
                });
            });
        });
    }

    function setSearchWorking(isWorking) {
        if (isWorking) {
            $('[data-orchid-search-progress]').show();
            $('[data-orchid-search-results]').hide();
        }
        else {
            $('[data-orchid-search-progress]').hide();
            $('[data-orchid-search-results]').show();
        }
        $(window).trigger('orchid.search.working');
    }

    function getOrchidDocuments($orchidIndicesAllowed, cb) {
        if (!window.orchidDocuments) {
            loadRootIndex($orchidIndicesAllowed, function (documents) {
                window.orchidDocuments = documents;
                window.orchidDocumentsMap = {};

                window.orchidDocuments.map(function (document) {
                    window.orchidDocumentsMap[document.link] = document;
                });

                cb(window.orchidDocuments);
            });
        }
        else {
            cb(window.orchidDocuments);
        }
    }

    function loadOrchidIndex($orchidIndicesAllowed, cb) {
        if (!window.orchidIdx) {
            getOrchidDocuments($orchidIndicesAllowed, function (docs) {
                window.orchidIdx = lunr(function () {
                    this.ref('link');
                    this.field('title');
                    this.field('description');
                    this.field('content');
                    this.metadataWhitelist = ['position'];

                    docs.forEach(function (doc) {
                        this.add(doc)
                    }, this)
                });

                cb(window.orchidIdx);
            });
        }
        else {
            cb(window.orchidIdx);
        }
    }

    function searchOrchidIndex(query, cb) {
        setTimeout(function () {
            var results = window.orchidIdx.search(query);
            cb(results);
        }, 1000)
    }

    function displaySearchResults(results, cb) {
        // console.log("displaying results");
        // console.log(results);

        var items = [];

        results.map(function (result) {
            var document = window.orchidDocumentsMap[result.ref];
            var summary = getSearchSummary(result, document);

            var $item = "
";
            $item += (items.length + 1) + ": " + document.title;
            $item += "
";
            if (summary.length > 0) {
                $item += "
" + summary + "
";
            }
            $item += "
";

            items.push($item);
        });

        var $searchResults = $('[data-orchid-search-results] ul');

        $searchResults.empty();
        $searchResults.html(items.join(''));

        cb();
    }

    function getSearchSummary(result, document) {
        var matches = 0;

        var startContextLength = 12;
        var endContextLength = 12;

        var snippets = [];

        // for every matching word
        for (var word in result.matchData.metadata) {

            // for every document field which has that word
            for (var field in result.matchData.metadata[word]) {
                if (field && result.matchData.metadata[word][field].position) {
                    matches += result.matchData.metadata[word][field].position.length;

                    for(var i_pos = 0; i_pos < result.matchData.metadata[word][field].position.length; i_pos++) {
                        if(i_pos === 3) break;

                        var snippetBounds = result.matchData.metadata[word][field].position[i_pos];
                        var pos_start = snippetBounds[0];
                        var pos_end = snippetBounds[0] + snippetBounds[1] + 1;

                        var match_start = snippetBounds[0];
                        var match_end = snippetBounds[0] + snippetBounds[1] + 1;

                        var leadingEllipses = false;
                        var trailingEllipses = false;

                        // add context to the beginning
                        if(pos_start > startContextLength) {
                            pos_start -= startContextLength;
                            leadingEllipses = true;
                        }
                        else {
                            pos_start = 0;
                        }

                        // add context to the end
                        if(pos_end < document[field].length - endContextLength) {
                            pos_end += endContextLength;
                            trailingEllipses = true;
                        }
                        else {
                            pos_end = document[field].length;
                        }

                        var snippet =
                            document[field].substring(pos_start, match_start) +
                            "" + document[field].substring(match_start, match_end) + "" +
                            document[field].substring(match_end, pos_end)
                        ;
                        snippet = snippet.trim();
                        if(leadingEllipses) {
                            snippet = "..." + snippet;
                        }
                        if(trailingEllipses) {
                            snippet = snippet + "...";
                        }
                        snippets.push(snippet);
                    }
                }
            }
        }

        return "" + matches + " matches:
" + snippets.join("
");
    }

// Load indices via AJAX and build a map of documents that can be passed to Lunr
//----------------------------------------------------------------------------------------------------------------------

    function loadRootIndex($orchidIndicesAllowed, done) {
        var allDocs = [];
        $.getJSON(window.site.baseUrl + "/meta/index.json", function (data) {
            var childIndices = data.childrenPages.meta.ownPages;
            var childIndicesFinishedCount = 0;

            childIndices.map(function (indexPage) {
                var shouldLoad = true;
                if($orchidIndicesAllowed) {
                    var isIndexAllowed = false;
                    for(var i = 0; i < $orchidIndicesAllowed.length; i++) {
                        if(indexPage.reference.fileName === $orchidIndicesAllowed[i].trim() + ".index") {
                            isIndexAllowed = true;
                            break;
                        }
                    }
                    shouldLoad = isIndexAllowed;
                }

                if (shouldLoad) {
                    loadIndexPage(indexPage.reference.link,
                        function (document) {
                            allDocs.push(document);
                        },
                        function () {
                            childIndicesFinishedCount++;

                            if (childIndicesFinishedCount === childIndices.length) {
                                done(allDocs);
                            }
                        });
                }
                else {
                    childIndicesFinishedCount++;

                    if (childIndicesFinishedCount === childIndices.length) {
                        done(allDocs);
                    }
                }
            });
        });
    }

    function loadIndexPage(url, cb, done) {
        $.getJSON(url, function (data) {
            getChildDocuments(data, cb);
            done();
        });
    }

    function getChildDocuments(data, cb) {
        for (var key in data.childrenPages) {
            if (data.childrenPages.hasOwnProperty(key)) {
                var doc = data.childrenPages[key];

                if (doc.childrenPages) {
                    getChildDocuments(doc, cb);
                }
                else if (doc.ownPages) {
                    doc.ownPages.map(function (ownPage) {
                        ownPage.link = ownPage.reference.link;
                        ownPage.content = stripTags(ownPage.content);
                        delete ownPage.reference;
                        cb(ownPage);
                    });
                }
            }
        }
    }

    function stripTags(html) {
        var doc = new DOMParser().parseFromString(html, 'text/html');
        return doc.body.textContent || "";
    }

// Initialize searching
//----------------------------------------------------------------------------------------------------------------------

    $(function () {
        initializeSearchField();
    });

})(jQuery, lunr);<|endoftext|>


<commit_before>import jsan from 'jsan';
import configureStore from '../../../app/store/configureStore';
import { isAllowed } from '../options/syncOptions';
import notifyErrors from '../utils/notifyErrors';

window.devToolsExtension = function(config = {}) {
  let store = {};
  if (!window.devToolsOptions) window.devToolsOptions = {};
  let shouldSerialize = false;
  let shouldInit = true;
  let lastAction;
  let errorOccurred = false;

  function stringify(obj) {
    return jsan.stringify(obj, function(key, value) {
      if (value && value.toJS) { return value.toJS(); }
      return value;
    }, null, true);
  }

  function relaySerialized(message) {
    message.payload = stringify(message.payload);
    if (message.action !== '') message.action = stringify(message.action);
    window.postMessage(message, '*');
  }

  function relay(type, state, action, nextActionId) {
    setTimeout(() => {
      if (type === 'STATE') addFilter(state);
      const message = {
        payload: state,
        action: action || '',
        nextActionId: nextActionId || '',
        type: type,
        source: 'redux-page',
        name: config.name || document.title,
        init: shouldInit
      };
      if (shouldInit) shouldInit = false;
      if (shouldSerialize || window.devToolsOptions.serialize) {
        relaySerialized(message);
      } else {
        try {
          window.postMessage(message, '*');
        } catch (err) {
          relaySerialized(message);
          shouldSerialize = true;
        }
      }
    }, 0);
  }

  function onMessage(event) {
    if (!event || event.source !== window) {
      return;
    }

    const message = event.data;

    if (!message || message.source !== 'redux-cs') {
      return;
    }

    if (message.type === 'DISPATCH') {
      store.liftedStore.dispatch(message.payload);
    } else if (message.type === 'UPDATE') {
      relay('STATE', store.liftedStore.getState());
    }
  }

  function isFiltered(action) {
    if (!window.devToolsOptions.filter) return false;
    const { whitelist, blacklist } = window.devToolsOptions;
    return (
      whitelist && !action.type.match(whitelist) ||
      blacklist && action.type.match(blacklist)
    );
  }

  function addFilter(state) {
    if (window.devToolsOptions.filter) {
      if (window.devToolsOptions.whitelist) state.whitelist = [window.devToolsOptions.whitelist];
      else if (window.devToolsOptions.blacklist) state.blacklist = [window.devToolsOptions.blacklist];
    }
  }

  function isLimit(nextActionId) {
    if (
      window.devToolsOptions.limit && window.devToolsOptions.limit !== '0'
      && nextActionId - 1 > window.devToolsOptions.limit
    ) {
      store.liftedStore.dispatch({type: 'COMMIT', timestamp: Date.now()});
      return true;
    }
    return false;
  }

  function init() {
    window.addEventListener('message', onMessage, false);
    relay('STATE', store.liftedStore.getState());
    notifyErrors(() => {
      errorOccurred = true;
      const state = store.liftedStore.getState();
      if (state.computedStates[state.currentStateIndex].error) {
        relay('STATE', state);
        return false;
      }
      return true;
    });

    // Detect when the tab is reactivated
    document.addEventListener('visibilitychange', function() {
      if (document.visibilityState === 'visible') {
        shouldInit = true;
        relay('STATE', store.liftedStore.getState());
      }
    }, false);
  }

  function monitorReducer(state = {}, action) {
    lastAction = action.type;
    if (lastAction === '@@redux/INIT' && store.liftedStore) {
      // Send new lifted state on hot-reloading
      setTimeout(() => {
        relay('STATE', store.liftedStore.getState());
      }, 0);
    }
    return state;
  }

  function handleChange(state, liftedState) {
    const nextActionId = liftedState.nextActionId;
    const liftedAction = liftedState.actionsById[nextActionId - 1];
    const action = liftedAction.action;
    if (action.type === '@@INIT') {
      relay('INIT', state, { timestamp: Date.now() });
    } else if (!errorOccurred && lastAction !== 'TOGGLE_ACTION' && lastAction !== 'SWEEP') {
      if (lastAction === 'JUMP_TO_STATE' || isLimit(nextActionId) || isFiltered(action)) return;
      relay('ACTION', state, liftedAction, nextActionId);
    } else {
      if (errorOccurred && !liftedState.computedStates[liftedState.currentStateIndex].error) errorOccurred = false;
      relay('STATE', liftedState);
    }
  }

  return (next) => {
    return (reducer, initialState, enhancer) => {
      if (!isAllowed(window.devToolsOptions)) return next(reducer, initialState, enhancer);

      const { deserializeState, deserializeAction } = config;
      store = configureStore(next, monitorReducer, {
        deserializeState,
        deserializeAction
      })(reducer, initialState, enhancer);
      init();
      store.subscribe(() => { handleChange(store.getState(), store.liftedStore.getState()); });
      return store;
    };
  };
};

window.devToolsExtension.open = function(position) {
  window.postMessage({
    source: 'redux-page',
    type: 'OPEN',
    position: position || ''
  }, '*');
};

window.devToolsExtension.notifyErrors = notifyErrors;
<commit_msg>Relay changes only when it is required by a monitor<commit_after>import jsan from 'jsan';
import configureStore from '../../../app/store/configureStore';
import { isAllowed } from '../options/syncOptions';
import notifyErrors from '../utils/notifyErrors';

window.devToolsExtension = function(config = {}) {
  let store = {};
  if (!window.devToolsOptions) window.devToolsOptions = {};
  let shouldSerialize = false;
  let shouldInit = true;
  let lastAction;
  let errorOccurred = false;
  let isMonitored = false;

  function stringify(obj) {
    return jsan.stringify(obj, function(key, value) {
      if (value && value.toJS) { return value.toJS(); }
      return value;
    }, null, true);
  }

  function relaySerialized(message) {
    message.payload = stringify(message.payload);
    if (message.action !== '') message.action = stringify(message.action);
    window.postMessage(message, '*');
  }

  function relay(type, state, action, nextActionId) {
    setTimeout(() => {
      if (type === 'STATE') addFilter(state);
      const message = {
        payload: state,
        action: action || '',
        nextActionId: nextActionId || '',
        type: type,
        source: 'redux-page',
        name: config.name || document.title,
        init: shouldInit
      };
      if (shouldInit) shouldInit = false;
      if (shouldSerialize || window.devToolsOptions.serialize) {
        relaySerialized(message);
      } else {
        try {
          window.postMessage(message, '*');
        } catch (err) {
          relaySerialized(message);
          shouldSerialize = true;
        }
      }
    }, 0);
  }

  function onMessage(event) {
    if (!event || event.source !== window) {
      return;
    }

    const message = event.data;

    if (!message || message.source !== 'redux-cs') {
      return;
    }

    if (message.type === 'DISPATCH') {
      store.liftedStore.dispatch(message.payload);
    } else if (message.type === 'UPDATE') {
      relay('STATE', store.liftedStore.getState());
    } else if (message.type === 'START') {
      isMonitored = true;
      relay('STATE', store.liftedStore.getState());
    } else if (message.type === 'STOP') {
      isMonitored = false;
    }
  }

  function isFiltered(action) {
    if (!window.devToolsOptions.filter) return false;
    const { whitelist, blacklist } = window.devToolsOptions;
    return (
      whitelist && !action.type.match(whitelist) ||
      blacklist && action.type.match(blacklist)
    );
  }

  function addFilter(state) {
    if (window.devToolsOptions.filter) {
      if (window.devToolsOptions.whitelist) state.whitelist = [window.devToolsOptions.whitelist];
      else if (window.devToolsOptions.blacklist) state.blacklist = [window.devToolsOptions.blacklist];
    }
  }

  function isLimit(nextActionId) {
    if (
      window.devToolsOptions.limit && window.devToolsOptions.limit !== '0'
      && nextActionId - 1 > window.devToolsOptions.limit
    ) {
      store.liftedStore.dispatch({type: 'COMMIT', timestamp: Date.now()});
      return true;
    }
    return false;
  }

  function init() {
    window.addEventListener('message', onMessage, false);
    relay('STATE', store.liftedStore.getState());
    notifyErrors(() => {
      errorOccurred = true;
      const state = store.liftedStore.getState();
      if (state.computedStates[state.currentStateIndex].error) {
        relay('STATE', state);
        return false;
      }
      return true;
    });

    // Detect when the tab is reactivated
    document.addEventListener('visibilitychange', function() {
      if (document.visibilityState === 'visible' && isMonitored) {
        shouldInit = true;
        relay('STATE', store.liftedStore.getState());
      }
    }, false);
  }

  function monitorReducer(state = {}, action) {
    if (!isMonitored) return state;
    lastAction = action.type;
    if (lastAction === '@@redux/INIT' && store.liftedStore) {
      // Send new lifted state on hot-reloading
      setTimeout(() => {
        relay('STATE', store.liftedStore.getState());
      }, 0);
    }
    return state;
  }

  function handleChange(state, liftedState) {
    if (!isMonitored) return;
    const nextActionId = liftedState.nextActionId;
    const liftedAction = liftedState.actionsById[nextActionId - 1];
    const action = liftedAction.action;
    if (action.type === '@@INIT') {
      relay('INIT', state, { timestamp: Date.now() });
    } else if (!errorOccurred && lastAction !== 'TOGGLE_ACTION' && lastAction !== 'SWEEP') {
      if (lastAction === 'JUMP_TO_STATE' || isLimit(nextActionId) || isFiltered(action)) return;
      relay('ACTION', state, liftedAction, nextActionId);
    } else {
      if (errorOccurred && !liftedState.computedStates[liftedState.currentStateIndex].error) errorOccurred = false;
      relay('STATE', liftedState);
    }
  }

  return (next) => {
    return (reducer, initialState, enhancer) => {
      if (!isAllowed(window.devToolsOptions)) return next(reducer, initialState, enhancer);

      const { deserializeState, deserializeAction } = config;
      store = configureStore(next, monitorReducer, {
        deserializeState,
        deserializeAction
      })(reducer, initialState, enhancer);
      init();
      store.subscribe(() => { handleChange(store.getState(), store.liftedStore.getState()); });
      return store;
    };
  };
};

window.devToolsExtension.open = function(position) {
  window.postMessage({
    source: 'redux-page',
    type: 'OPEN',
    position: position || ''
  }, '*');
};

window.devToolsExtension.notifyErrors = notifyErrors;
<|endoftext|>