<commit_before>export default function ({ types: t }) {
  const nilIdentifier = t.unaryExpression("void", t.numericLiteral(0));

  function setOptionalTransformed(node) {
    t.assertMemberExpression(node); // Dev
    node._optionalTransformed = true;
  }

  function isOptionalTransformed(node) {
    t.assertMemberExpression(node); // Dev
    return node._optionalTransformed === true;
  }

  function createCondition(ref, access, nextProperty, bailout) {

    return t.conditionalExpression(
      createCheckAgainstNull(
        t.AssignmentExpression("=", ref, access)
      ),

      t.memberExpression(ref, nextProperty),
      bailout,
    );
  }

  function createCheckAgainstNull(left) {
    return t.BinaryExpression("!=", left, t.NullLiteral());
  }

  function isNodeOptional(node) {
    return node.optional === true;
  }

  return {
    visitor: {

      AssignmentExpression(path, state) {
        const { left } = path.node;

        if (!isNodeOptional(left) || isOptionalTransformed(left)) {
          return;
        }

        if (!state.optionalTemp) {
          const id = path.scope.generateUidIdentifier();

          state.optionalTemp = id;
          path.scope.push({ id });
        }

        const { object, property } = left;

        const isChainNil = t.BinaryExpression(
          "!=",
          createCondition(
            state.optionalTemp,
            object,
            property,
            nilIdentifier,
          ),
          nilIdentifier,
        );

        // FIXME(sven): if will be a ConditionalExpression for childs, only top level will be ifStatement
        const remplacement = t.ifStatement(isChainNil, t.blockStatement([t.expressionStatement(path.node)]));

        setOptionalTransformed(left);

        path.traverse({
          MemberExpression({ node }) {
            setOptionalTransformed(node);
          },
        });

        path.parentPath.replaceWith(remplacement);
      },

      UnaryExpression(path, state) {
        const { operator, argument } = path.node;

        if (operator !== "delete") {
          return;
        }

        if (!isNodeOptional(argument) || isOptionalTransformed(argument)) {
          return;
        }

        if (!state.optionalTemp) {
          const id = path.scope.generateUidIdentifier();

          state.optionalTemp = id;
          path.scope.push({ id });
        }

        const { object, property } = argument;

        const isChainNil = t.BinaryExpression(
          "!=",
          createCondition(
            state.optionalTemp,
            object,
            property,
            nilIdentifier,
          ),
          nilIdentifier,
        );

        const remplacement = t.ifStatement(isChainNil, t.blockStatement([t.expressionStatement(path.node)]));

        setOptionalTransformed(argument);

        path.parentPath.replaceWith(remplacement);
      },

      MemberExpression(path, state) {
        if (!isNodeOptional(path.node) || isOptionalTransformed(path.node)) {
          return;
        }

        const { object, property } = path.node;

        if (!state.optionalTemp) {
          const id = path.scope.generateUidIdentifier();

          state.optionalTemp = id;
          path.scope.push({ id });
        }

        if (t.isAssignmentExpression(path.parent)) {
          return;
        } else if (t.isUnaryExpression(path.parent)) {
          return;
        } else if (t.isCallExpression(path.parent)) {

          const remplacement = createCondition(
            state.optionalTemp,
            object,
            property,
            t.callExpression(t.identifier("Function"), []),
          );

          setOptionalTransformed(path.node);
          path.replaceWith(remplacement);
        } else {

          const remplacement = createCondition(
            state.optionalTemp,
            object,
            property,
            nilIdentifier,
          );

          setOptionalTransformed(path.node);
          path.replaceWith(remplacement);
        }
      },
    },
  };
}
<commit_msg>fix: typo<commit_after>export default function ({ types: t }) {
  const nilIdentifier = t.unaryExpression("void", t.numericLiteral(0));

  function setOptionalTransformed(node) {
    t.assertMemberExpression(node); // Dev
    node._optionalTransformed = true;
  }

  function isOptionalTransformed(node) {
    t.assertMemberExpression(node); // Dev
    return node._optionalTransformed === true;
  }

  function createCondition(ref, access, nextProperty, bailout) {

    return t.conditionalExpression(
      createCheckAgainstNull(
        t.AssignmentExpression("=", ref, access)
      ),

      t.memberExpression(ref, nextProperty),
      bailout,
    );
  }

  function createCheckAgainstNull(left) {
    return t.BinaryExpression("!=", left, t.NullLiteral());
  }

  function isNodeOptional(node) {
    return node.optional === true;
  }

  return {
    visitor: {

      AssignmentExpression(path, state) {
        const { left } = path.node;

        if (!isNodeOptional(left) || isOptionalTransformed(left)) {
          return;
        }

        if (!state.optionalTemp) {
          const id = path.scope.generateUidIdentifier();

          state.optionalTemp = id;
          path.scope.push({ id });
        }

        const { object, property } = left;

        const isChainNil = t.BinaryExpression(
          "!=",
          createCondition(
            state.optionalTemp,
            object,
            property,
            nilIdentifier,
          ),
          nilIdentifier,
        );

        // FIXME(sven): if will be a ConditionalExpression for childs, only top level will be ifStatement
        const replacement = t.ifStatement(isChainNil, t.blockStatement([t.expressionStatement(path.node)]));

        setOptionalTransformed(left);

        path.traverse({
          MemberExpression({ node }) {
            setOptionalTransformed(node);
          },
        });

        path.parentPath.replaceWith(replacement);
      },

      UnaryExpression(path, state) {
        const { operator, argument } = path.node;

        if (operator !== "delete") {
          return;
        }

        if (!isNodeOptional(argument) || isOptionalTransformed(argument)) {
          return;
        }

        if (!state.optionalTemp) {
          const id = path.scope.generateUidIdentifier();

          state.optionalTemp = id;
          path.scope.push({ id });
        }

        const { object, property } = argument;

        const isChainNil = t.BinaryExpression(
          "!=",
          createCondition(
            state.optionalTemp,
            object,
            property,
            nilIdentifier,
          ),
          nilIdentifier,
        );

        const replacement = t.ifStatement(isChainNil, t.blockStatement([t.expressionStatement(path.node)]));

        setOptionalTransformed(argument);

        path.parentPath.replaceWith(replacement);
      },

      MemberExpression(path, state) {
        if (!isNodeOptional(path.node) || isOptionalTransformed(path.node)) {
          return;
        }

        const { object, property } = path.node;

        if (!state.optionalTemp) {
          const id = path.scope.generateUidIdentifier();

          state.optionalTemp = id;
          path.scope.push({ id });
        }

        if (t.isAssignmentExpression(path.parent)) {
          return;
        } else if (t.isUnaryExpression(path.parent)) {
          return;
        } else if (t.isCallExpression(path.parent)) {

          const replacement = createCondition(
            state.optionalTemp,
            object,
            property,
            t.callExpression(t.identifier("Function"), []),
          );

          setOptionalTransformed(path.node);
          path.replaceWith(replacement);
        } else {

          const replacement = createCondition(
            state.optionalTemp,
            object,
            property,
            nilIdentifier,
          );

          setOptionalTransformed(path.node);
          path.replaceWith(replacement);
        }
      },
    },
  };
}
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import { Field } from 'redux-form';
import styled from 'styled-components';
import { Row, Col } from 'react-styled-flexboxgrid';
import Bundle from 'react-bundle';
import remcalc from 'remcalc';
import forceArray from 'force-array';
import is from 'styled-is';

import { Loader } from '@components/messaging';

import {
  FormGroup,
  FormMeta,
  Input,
  Button,
  Card,
  Progressbar,
  ProgressbarItem,
  ProgressbarButton,
  H3,
  P,
  typography,
  Divider,
  Chevron
} from 'joyent-ui-toolkit';

const EnvironmentChevron = Chevron.extend`
  float: right;
`;

const EnvironmentDivider = Divider.extend`
  margin-top: ${remcalc(34)};
`;

const ServiceDivider = Divider.extend`
  margin: ${remcalc(13)} ${remcalc(-20)} 0 ${remcalc(-20)};
`;

const Dl = styled.dl`
  margin: 0;
`;

const ServiceName = H3.extend`
  margin-top: 0;
  margin-bottom: ${remcalc(5)};
  line-height: 1.6;
  font-size: ${remcalc(18)};
`;

const ImageTitle = H3.extend`
  display: inline-block;
  margin: 0;
`;

const Image = styled.span`
  ${typography.fontFamily};
  font-size: ${remcalc(15)};
`;

const ServiceEnvironmentTitle = P.extend`
  margin: ${remcalc(13)} 0 0 0;

  ${is('expanded')`
    margin-bottom: ${remcalc(13)};
  `};
`;

const ButtonsRow = Row.extend`
  margin: ${remcalc(29)} 0 ${remcalc(60)} 0;
`;

const FilenameContainer = styled.span`
  display: flex;
  flex-direction: row;
  flex-wrap: nowrap;
  justify-content: space-between;
  align-content: stretch;
  align-items: stretch;
`;

const FilenameInput = styled(Input)`
  order: 0;
  flex: 1 1 auto;
  align-self: stretch;
  margin: 0 0 ${remcalc(13)} 0;
`;

const FilenameRemove = Button.extend`
  order: 0;
  flex: 0 1 auto;
  align-self: auto;
  margin: 0 0 0 ${remcalc(8)};
  height: ${remcalc(48)};
`;

const FileCard = Card.extend`
  padding: ${remcalc(24)} ${remcalc(19)};
`;

const ServiceCard = Card.extend`
  padding: ${remcalc(13)} ${remcalc(19)};
  min-height: initial;
`;

const Subtitle = H3.extend`
  margin-top: ${remcalc(34)};
  margin-bottom: ${remcalc(3)};
`;

const Description = P.extend`
  margin-top: ${remcalc(3)};
  margin-bottom: ${remcalc(20)};
`;

class ManifestEditorBundle extends Component {
  constructor() {
    super();

    this.state = {};

    this.handleRender = this.handleRender.bind(this);
  }
  handleRender(ManifestEditor) {
    if (ManifestEditor) {
      setTimeout(() => {
        this.setState({ ManifestEditor });
      }, 80);
    }

    return <Loader />;
  }
  render() {
    if (!this.state.ManifestEditor) {
      return (
        <Bundle load={() => import('joyent-manifest-editor')}>
          {this.handleRender}
        </Bundle>
      );
    }

    const { ManifestEditor } = this.state;
    const { children, ...rest } = this.props;

    return <ManifestEditor {...rest}>{children}</ManifestEditor>;
  }
}

const MEditor = ({ input, defaultValue, readOnly }) => (
  <ManifestEditorBundle
    mode="yaml"
    {...input}
    value={input.value || defaultValue}
    readOnly={readOnly}
  />
);

const EEditor = ({ input, defaultValue, readOnly }) => (
  <ManifestEditorBundle
    mode="ini"
    {...input}
    value={input.value || defaultValue}
    readOnly={readOnly}
  />
);

export const Name = ({ handleSubmit, onCancel, dirty }) => (
  <form onSubmit={handleSubmit}>
    <Row>
      <Col xs={12} md={3} lg={3}>
        <FormGroup name="name" reduxForm>
          <FormMeta left />
          <Input type="text" />
        </FormGroup>
      </Col>
    </Row>
    <ButtonsRow>
      <Button type="button" onClick={onCancel} secondary>
        Cancel
      </Button>
      <Button type="submit" disabled={!dirty}>
        Next
      </Button>
    </ButtonsRow>
  </form>
);

export const Manifest = ({
  handleSubmit,
  onCancel,
  dirty,
  defaultValue = '',
  loading
}) => (
  <form onSubmit={handleSubmit}>
    <Field name="manifest" defaultValue={defaultValue} component={MEditor} />
    <ButtonsRow>
      <Button type="button" onClick={onCancel} secondary>
        Cancel
      </Button>
      <Button
        disabled={!(dirty || !loading || defaultValue.length)}
        loading={loading}
        type="submit"
      >
        Environment
      </Button>
    </ButtonsRow>
  </form>
);

const File = ({ id, name, value, onRemoveFile, readOnly }) => {
  const removeButton = !readOnly ? (
    <FilenameRemove type="button" onClick={onRemoveFile} secondary>
      Remove
    </FilenameRemove>
  ) : null;

  const fileEditor = !readOnly ? (
    <Field name={`file-value-${id}`} defaultValue={value} component={EEditor} />
  ) : (
    <EEditor input={{ value }} readOnly />
  );

  const input = !readOnly ? (
    <FilenameInput type="text" placeholder="Filename including extension…" />
  ) : (
    <FilenameInput
      type="text"
      placeholder="Filename including extension…"
      value={name}
    />
  );

  return (
    <FileCard>
      <FormGroup name={`file-name-${id}`} reduxForm={!readOnly}>
        <FilenameContainer>
          {input}
          {removeButton}
        </FilenameContainer>
      </FormGroup>
      {fileEditor}
    </FileCard>
  );
};

const Files = ({ files, onAddFile, onRemoveFile, readOnly }) => {
  const footer = !readOnly ? (
    <Button type="button" onClick={onAddFile} secondary>
      Create new .env file
    </Button>
  ) : null;

  return (

      {files.map(({ id, ...rest }) => (
        <File
          key={id}
          id={id}
          onRemoveFile={() => onRemoveFile(id)}
          readOnly={readOnly}
          {...rest}
        />
      ))}
      {footer}

  );
};

export const Environment = ({
  handleSubmit,
  onCancel,
  onAddFile,
  onRemoveFile,
  dirty,
  defaultValue = '',
  files = [],
  readOnly = false,
  loading
}) => {
  const envEditor = !readOnly ? (
    <Field name="environment" defaultValue={defaultValue} component={EEditor} />
  ) : (
    <EEditor input={{ value: defaultValue }} readOnly />
  );

  const footerDivider = !readOnly ? <EnvironmentDivider /> : null;

  const footer = !readOnly ? (
    <ButtonsRow>
      <Button type="button" onClick={onCancel} secondary>
        Cancel
      </Button>
      <Button
        disabled={!(dirty || !loading || defaultValue.length)}
        loading={loading}
        type="submit"
      >
        Continue
      </Button>
    </ButtonsRow>
  ) : null;

  return (
    <form onSubmit={handleSubmit}>
      <Subtitle>Global variables</Subtitle>
      <Description>
        These variables are going to be availabe for interpolation in the
        manifest
      </Description>
      {envEditor}
      <EnvironmentDivider />
      <Subtitle>Enviroment files</Subtitle>
      <Description>
        The variables from this files will be applied to the services that
        require them
      </Description>
      <Files
        files={files}
        onAddFile={onAddFile}
        onRemoveFile={onRemoveFile}
        readOnly={readOnly}
      />
      {footerDivider}
      {footer}
    </form>
  );
};

const EnvironmentReview = ({ environment }) => {
  const value = environment
    .map(({ name, value }) => `${name}=${value}`)
    .join('\n');

  return <EEditor input={{ value }} />;
};

export const Review = ({
  handleSubmit,
  onEnvironmentToggle = () => null,
  onCancel,
  dirty,
  loading,
  environmentToggles,
  ...state
}) => {
  const serviceList = forceArray(state.services).map(({ name, config }) => (
    <ServiceCard key={name}>
      <ServiceName>{name}</ServiceName>

          <ImageTitle>Image:</ImageTitle> <Image>{config.image}</Image>

      {config.environment && config.environment.length ? (
        <ServiceDivider />
      ) : null}
      {config.environment && config.environment.length ? (
        <ServiceEnvironmentTitle
          expanded={environmentToggles[name]}
          onClick={() => onEnvironmentToggle(name)}
        >
          Environment variables{' '}
          <EnvironmentChevron
            down={!environmentToggles[name]}
            up={environmentToggles[name]}
          />
        </ServiceEnvironmentTitle>
      ) : null}
      {config.environment &&
      config.environment.length &&
      environmentToggles[name] ? (
        <EnvironmentReview environment={config.environment} />
      ) : null}
    </ServiceCard>
  ));

  return (
    <form onSubmit={handleSubmit}>
      {serviceList}
      <ButtonsRow>
        <Button type="button" onClick={onCancel} disabled={loading} secondary>
          Cancel
        </Button>
        <Button disabled={loading} loading={loading} type="submit">
          Confirm and Deploy
        </Button>
      </ButtonsRow>
    </form>
  );
};

export const Progress = ({ stage, create, edit }) => {
  const _nameCompleted = stage !== 'name';
  const _nameActive = stage === 'name';

  const _name = !create ? null : (
    <ProgressbarItem>
      <ProgressbarButton
        zIndex="10"
        completed={_nameCompleted}
        active={_nameActive}
        first
      >
        Name the group
      </ProgressbarButton>
    </ProgressbarItem>
  );

  const _manifestCompleted = ['environment', 'review'].indexOf(stage) >= 0;
  const _manifestActive = create ? stage === 'manifest' : stage === 'edit';

  const _manifest = (
    <ProgressbarItem>
      <ProgressbarButton
        zIndex="9"
        completed={_manifestCompleted}
        active={_manifestActive}
        first={edit}
      >
        Define Services
      </ProgressbarButton>
    </ProgressbarItem>
  );

  const _environmentCompleted = stage === 'review';
  const _environmentActive = stage === 'environment';

  const _environment = (
    <ProgressbarItem>
      <ProgressbarButton
        zIndex="8"
        completed={_environmentCompleted}
        active={_environmentActive}
      >
        Define Environment
      </ProgressbarButton>
    </ProgressbarItem>
  );

  const _reviewActive = stage === 'review';

  const _review = (
    <ProgressbarItem>
      <ProgressbarButton zIndex="7" active={_reviewActive} last>
        Review and deploy
      </ProgressbarButton>
    </ProgressbarItem>
  );

  return (
    <Progressbar>
      {_name}
      {_manifest}
      {_environment}
      {_review}
    </Progressbar>
  );
};
<commit_msg>fix(cp-frontend): fix chevron extend<commit_after>import React, { Component } from 'react';
import { Field } from 'redux-form';
import styled from 'styled-components';
import { Row, Col } from 'react-styled-flexboxgrid';
import Bundle from 'react-bundle';
import remcalc from 'remcalc';
import forceArray from 'force-array';
import is from 'styled-is';

import { Loader } from '@components/messaging';

import {
  FormGroup,
  FormMeta,
  Input,
  Button,
  Card,
  Progressbar,
  ProgressbarItem,
  ProgressbarButton,
  H3,
  P,
  typography,
  Divider,
  Chevron
} from 'joyent-ui-toolkit';

const EnvironmentChevron = styled(Chevron)`
  float: right;
`;

const EnvironmentDivider = Divider.extend`
  margin-top: ${remcalc(34)};
`;

const ServiceDivider = Divider.extend`
  margin: ${remcalc(13)} ${remcalc(-20)} 0 ${remcalc(-20)};
`;

const Dl = styled.dl`
  margin: 0;
`;

const ServiceName = H3.extend`
  margin-top: 0;
  margin-bottom: ${remcalc(5)};
  line-height: 1.6;
  font-size: ${remcalc(18)};
`;

const ImageTitle = H3.extend`
  display: inline-block;
  margin: 0;
`;

const Image = styled.span`
  ${typography.fontFamily};
  font-size: ${remcalc(15)};
`;

const ServiceEnvironmentTitle = P.extend`
  margin: ${remcalc(13)} 0 0 0;

  ${is('expanded')`
    margin-bottom: ${remcalc(13)};
  `};
`;

const ButtonsRow = Row.extend`
  margin: ${remcalc(29)} 0 ${remcalc(60)} 0;
`;

const FilenameContainer = styled.span`
  display: flex;
  flex-direction: row;
  flex-wrap: nowrap;
  justify-content: space-between;
  align-content: stretch;
  align-items: stretch;
`;

const FilenameInput = styled(Input)`
  order: 0;
  flex: 1 1 auto;
  align-self: stretch;
  margin: 0 0 ${remcalc(13)} 0;
`;

const FilenameRemove = Button.extend`
  order: 0;
  flex: 0 1 auto;
  align-self: auto;
  margin: 0 0 0 ${remcalc(8)};
  height: ${remcalc(48)};
`;

const FileCard = Card.extend`
  padding: ${remcalc(24)} ${remcalc(19)};
`;

const ServiceCard = Card.extend`
  padding: ${remcalc(13)} ${remcalc(19)};
  min-height: initial;
`;

const Subtitle = H3.extend`
  margin-top: ${remcalc(34)};
  margin-bottom: ${remcalc(3)};
`;

const Description = P.extend`
  margin-top: ${remcalc(3)};
  margin-bottom: ${remcalc(20)};
`;

class ManifestEditorBundle extends Component {
  constructor() {
    super();

    this.state = {};

    this.handleRender = this.handleRender.bind(this);
  }
  handleRender(ManifestEditor) {
    if (ManifestEditor) {
      setTimeout(() => {
        this.setState({ ManifestEditor });
      }, 80);
    }

    return <Loader />;
  }
  render() {
    if (!this.state.ManifestEditor) {
      return (
        <Bundle load={() => import('joyent-manifest-editor')}>
          {this.handleRender}
        </Bundle>
      );
    }

    const { ManifestEditor } = this.state;
    const { children, ...rest } = this.props;

    return <ManifestEditor {...rest}>{children}</ManifestEditor>;
  }
}

const MEditor = ({ input, defaultValue, readOnly }) => (
  <ManifestEditorBundle
    mode="yaml"
    {...input}
    value={input.value || defaultValue}
    readOnly={readOnly}
  />
);

const EEditor = ({ input, defaultValue, readOnly }) => (
  <ManifestEditorBundle
    mode="ini"
    {...input}
    value={input.value || defaultValue}
    readOnly={readOnly}
  />
);

export const Name = ({ handleSubmit, onCancel, dirty }) => (
  <form onSubmit={handleSubmit}>
    <Row>
      <Col xs={12} md={3} lg={3}>
        <FormGroup name="name" reduxForm>
          <FormMeta left />
          <Input type="text" />
        </FormGroup>
      </Col>
    </Row>
    <ButtonsRow>
      <Button type="button" onClick={onCancel} secondary>
        Cancel
      </Button>
      <Button type="submit" disabled={!dirty}>
        Next
      </Button>
    </ButtonsRow>
  </form>
);

export const Manifest = ({
  handleSubmit,
  onCancel,
  dirty,
  defaultValue = '',
  loading
}) => (
  <form onSubmit={handleSubmit}>
    <Field name="manifest" defaultValue={defaultValue} component={MEditor} />
    <ButtonsRow>
      <Button type="button" onClick={onCancel} secondary>
        Cancel
      </Button>
      <Button
        disabled={!(dirty || !loading || defaultValue.length)}
        loading={loading}
        type="submit"
      >
        Environment
      </Button>
    </ButtonsRow>
  </form>
);

const File = ({ id, name, value, onRemoveFile, readOnly }) => {
  const removeButton = !readOnly ? (
    <FilenameRemove type="button" onClick={onRemoveFile} secondary>
      Remove
    </FilenameRemove>
  ) : null;

  const fileEditor = !readOnly ? (
    <Field name={`file-value-${id}`} defaultValue={value} component={EEditor} />
  ) : (
    <EEditor input={{ value }} readOnly />
  );

  const input = !readOnly ? (
    <FilenameInput type="text" placeholder="Filename including extension…" />
  ) : (
    <FilenameInput
      type="text"
      placeholder="Filename including extension…"
      value={name}
    />
  );

  return (
    <FileCard>
      <FormGroup name={`file-name-${id}`} reduxForm={!readOnly}>
        <FilenameContainer>
          {input}
          {removeButton}
        </FilenameContainer>
      </FormGroup>
      {fileEditor}
    </FileCard>
  );
};

const Files = ({ files, onAddFile, onRemoveFile, readOnly }) => {
  const footer = !readOnly ? (
    <Button type="button" onClick={onAddFile} secondary>
      Create new .env file
    </Button>
  ) : null;

  return (

      {files.map(({ id, ...rest }) => (
        <File
          key={id}
          id={id}
          onRemoveFile={() => onRemoveFile(id)}
          readOnly={readOnly}
          {...rest}
        />
      ))}
      {footer}

  );
};

export const Environment = ({
  handleSubmit,
  onCancel,
  onAddFile,
  onRemoveFile,
  dirty,
  defaultValue = '',
  files = [],
  readOnly = false,
  loading
}) => {
  const envEditor = !readOnly ? (
    <Field name="environment" defaultValue={defaultValue} component={EEditor} />
  ) : (
    <EEditor input={{ value: defaultValue }} readOnly />
  );

  const footerDivider = !readOnly ? <EnvironmentDivider /> : null;

  const footer = !readOnly ? (
    <ButtonsRow>
      <Button type="button" onClick={onCancel} secondary>
        Cancel
      </Button>
      <Button
        disabled={!(dirty || !loading || defaultValue.length)}
        loading={loading}
        type="submit"
      >
        Continue
      </Button>
    </ButtonsRow>
  ) : null;

  return (
    <form onSubmit={handleSubmit}>
      <Subtitle>Global variables</Subtitle>
      <Description>
        These variables are going to be availabe for interpolation in the
        manifest
      </Description>
      {envEditor}
      <EnvironmentDivider />
      <Subtitle>Enviroment files</Subtitle>
      <Description>
        The variables from this files will be applied to the services that
        require them
      </Description>
      <Files
        files={files}
        onAddFile={onAddFile}
        onRemoveFile={onRemoveFile}
        readOnly={readOnly}
      />
      {footerDivider}
      {footer}
    </form>
  );
};

const EnvironmentReview = ({ environment }) => {
  const value = environment
    .map(({ name, value }) => `${name}=${value}`)
    .join('\n');

  return <EEditor input={{ value }} />;
};

export const Review = ({
  handleSubmit,
  onEnvironmentToggle = () => null,
  onCancel,
  dirty,
  loading,
  environmentToggles,
  ...state
}) => {
  const serviceList = forceArray(state.services).map(({ name, config }) => (
    <ServiceCard key={name}>
      <ServiceName>{name}</ServiceName>

          <ImageTitle>Image:</ImageTitle> <Image>{config.image}</Image>

      {config.environment && config.environment.length ? (
        <ServiceDivider />
      ) : null}
      {config.environment && config.environment.length ? (
        <ServiceEnvironmentTitle
          expanded={environmentToggles[name]}
          onClick={() => onEnvironmentToggle(name)}
        >
          Environment variables{' '}
          <EnvironmentChevron
            down={!environmentToggles[name]}
            up={environmentToggles[name]}
          />
        </ServiceEnvironmentTitle>
      ) : null}
      {config.environment &&
      config.environment.length &&
      environmentToggles[name] ? (
        <EnvironmentReview environment={config.environment} />
      ) : null}
    </ServiceCard>
  ));

  return (
    <form onSubmit={handleSubmit}>
      {serviceList}
      <ButtonsRow>
        <Button type="button" onClick={onCancel} disabled={loading} secondary>
          Cancel
        </Button>
        <Button disabled={loading} loading={loading} type="submit">
          Confirm and Deploy
        </Button>
      </ButtonsRow>
    </form>
  );
};

export const Progress = ({ stage, create, edit }) => {
  const _nameCompleted = stage !== 'name';
  const _nameActive = stage === 'name';

  const _name = !create ? null : (
    <ProgressbarItem>
      <ProgressbarButton
        zIndex="10"
        completed={_nameCompleted}
        active={_nameActive}
        first
      >
        Name the group
      </ProgressbarButton>
    </ProgressbarItem>
  );

  const _manifestCompleted = ['environment', 'review'].indexOf(stage) >= 0;
  const _manifestActive = create ? stage === 'manifest' : stage === 'edit';

  const _manifest = (
    <ProgressbarItem>
      <ProgressbarButton
        zIndex="9"
        completed={_manifestCompleted}
        active={_manifestActive}
        first={edit}
      >
        Define Services
      </ProgressbarButton>
    </ProgressbarItem>
  );

  const _environmentCompleted = stage === 'review';
  const _environmentActive = stage === 'environment';

  const _environment = (
    <ProgressbarItem>
      <ProgressbarButton
        zIndex="8"
        completed={_environmentCompleted}
        active={_environmentActive}
      >
        Define Environment
      </ProgressbarButton>
    </ProgressbarItem>
  );

  const _reviewActive = stage === 'review';

  const _review = (
    <ProgressbarItem>
      <ProgressbarButton zIndex="7" active={_reviewActive} last>
        Review and deploy
      </ProgressbarButton>
    </ProgressbarItem>
  );

  return (
    <Progressbar>
      {_name}
      {_manifest}
      {_environment}
      {_review}
    </Progressbar>
  );
};
<|endoftext|>


<commit_before>'use strict';

var tape = require('../lib/thaliTape');
var sinon = require('sinon');

var fs = require('fs-extra-promise');
var PouchDB = require('pouchdb')
  .plugin(require('pouchdb-size'))
  .plugin(require('thali/NextGeneration/utils/pouchDBCheckpointsPlugin'))
  .defaults({
     db: require('leveldown-mobile')
   });

// DB directory should be unique among all tests and any instance of this test.
// This is especially required for tape.coordinated.
var db;
var dbName = 'pouchdb-uuid:' + Math.random();
var dbOptions = {
  checkpoint: 900
};
var checkpointPluginDelay = 500;

var test = tape({
  setup: function (t) {
    db = new PouchDB(dbName, dbOptions);
    t.end()
  },
  teardown: function (t) {
    db.destroy()
      .then(function () {
        fs.removeSync(dbName);
        t.end();
      })
      .catch(function (error) {
        t.fail(error);
      });
  }
});

var Doc = function () {
  this._id = Date.now().toString()
};

test('onCheckpointReached callback calling on a single change', function (t) {
  var spy = sinon.spy();

  db.onCheckpointReached(spy);

  db.put(new Doc())
    .then(function () {
      // A small latency is needed to calculate database size after put
      setTimeout(function () {
        t.ok(spy.calledOnce, 'checkpointReached handler should be called once. Called ' + spy.callCount + ' time(s)');
        t.end();
      }, checkpointPluginDelay + 300);
    })
    .catch(function (error) {
      t.fail(error);
    });
});

test('onCheckpointReached callback calling on multiple changes' +
'that are in checkpoints plugin delay interval', function (t) {
    var spy = sinon.spy();

    db.onCheckpointReached(spy);

    db.put(new Doc())
      // put some extra docs for 'change' event emission
      .then(function () {
        return db.put(new Doc());
      })
      .then(function () {
        return db.put(new Doc());
      })
      .then(function () {
        // A small latency is needed to calculate database size after put
        setTimeout(function () {
          t.ok(spy.calledOnce, 'checkpointReached handler should be called once. Called ' + spy.callCount + ' time(s)');
          t.end();
        }, checkpointPluginDelay + 300);
      })
      .catch(function (error) {
        t.fail(error);
      });
    });

//  TODO Investigate how to force PouchDB to shrink database size
//  Atfer that test can be enabled
/*
test('onCheckpointReached callback calling after database shrinks', function (t) {
  var spy = sinon.spy();
  var doc = new Doc();

  db.onCheckpointReached(spy);

  db.put(doc)
    .then(function (res) {
      return db.get(res.id);
    })
    .then(function (doc) {
      return db.remove(doc);
    })
    .then(function () {
      return db.compact();
    })
    .then(function () {
      return db.put(new Doc());
    })
    .then(function () {
      // A small latency is needed to calculate database size after put
      setTimeout(function () {
        t.ok(spu.calledTwice, 'checkpointReached reached handler should be called twice');
        t.end();
      }, 200)
    })
    .catch(function (error) {
      t.fail(error);
    });
});
*/
<commit_msg>Added test case for multiple callbacks<commit_after>'use strict';

var tape = require('../lib/thaliTape');
var sinon = require('sinon');

var fs = require('fs-extra-promise');
var PouchDB = require('pouchdb')
  .plugin(require('pouchdb-size'))
  .plugin(require('thali/NextGeneration/utils/pouchDBCheckpointsPlugin'))
  .defaults({
     db: require('leveldown-mobile')
   });

// DB directory should be unique among all tests and any instance of this test.
// This is especially required for tape.coordinated.
var db;
var dbName = 'pouchdb-uuid:' + Math.random();
var dbOptions = {
  checkpoint: 900
};
var checkpointPluginDelay = 500;

var test = tape({
  setup: function (t) {
    db = new PouchDB(dbName, dbOptions);
    t.end()
  },
  teardown: function (t) {
    db.destroy()
      .then(function () {
        fs.removeSync(dbName);
        t.end();
      })
      .catch(function (error) {
        t.fail(error);
      });
  }
});

var Doc = function () {
  this._id = Date.now().toString()
};

test('onCheckpointReached callback calling on a single change', function (t) {
  var spy = sinon.spy();

  db.onCheckpointReached(spy);

  db.put(new Doc())
    .then(function () {
      // A small latency is needed to calculate database size after put
      setTimeout(function () {
        t.ok(spy.calledOnce, 'checkpointReached handler should be called once. Called ' + spy.callCount + ' time(s)');
        t.end();
      }, checkpointPluginDelay + 300);
    })
    .catch(function (error) {
      t.fail(error);
    });
});

test('onCheckpointReached multiple callbacks calling on a single change', function (t) {
  var spy = sinon.spy();
  var anotherSpy = sinon.spy();

  db.onCheckpointReached(spy);
  db.onCheckpointReached(anotherSpy);

  db.put(new Doc())
    .then(function () {
      // A small latency is needed to calculate database size after put
      setTimeout(function () {
        t.ok(spy.calledOnce, 'checkpointReached handler should be called once. Called ' + spy.callCount + ' time(s)');
        t.ok(anotherSpy.calledOnce, 'checkpointReached handler should be called once. Called ' + anotherSpy.callCount + ' time(s)');
        t.end();
      }, checkpointPluginDelay + 300);
    })
    .catch(function (error) {
      t.fail(error);
    });
});

test('onCheckpointReached callback calling on multiple changes' +
'that are in checkpoints plugin delay interval', function (t) {
    var spy = sinon.spy();

    db.onCheckpointReached(spy);

    db.put(new Doc())
      // put some extra docs for 'change' event emission
      .then(function () {
        return db.put(new Doc());
      })
      .then(function () {
        return db.put(new Doc());
      })
      .then(function () {
        // A small latency is needed to calculate database size after put
        setTimeout(function () {
          t.ok(spy.calledOnce, 'checkpointReached handler should be called once. Called ' + spy.callCount + ' time(s)');
          t.end();
        }, checkpointPluginDelay + 300);
      })
      .catch(function (error) {
        t.fail(error);
      });
    });

//  TODO Investigate how to force PouchDB to shrink database size
//  Atfer that test can be enabled
/*
test('onCheckpointReached callback calling after database shrinks', function (t) {
  var spy = sinon.spy();
  var doc = new Doc();

  db.onCheckpointReached(spy);

  db.put(doc)
    .then(function (res) {
      return db.get(res.id);
    })
    .then(function (doc) {
      return db.remove(doc);
    })
    .then(function () {
      return db.compact();
    })
    .then(function () {
      return db.put(new Doc());
    })
    .then(function () {
      // A small latency is needed to calculate database size after put
      setTimeout(function () {
        t.ok(spu.calledTwice, 'checkpointReached reached handler should be called twice');
        t.end();
      }, 200)
    })
    .catch(function (error) {
      t.fail(error);
    });
});
*/
<|endoftext|>


<commit_before>#!/usr/bin/env node

let spawn = require('child_process').spawn;

//
//USE: ./index.js <ms npm can be idle> <number of attempts> [... NPM ARGS]
//

let timeout = process.argv[2] || process.env.NPM_INSTALL_TIMEOUT || 60000;
let attempts = process.argv[3] || 3;
let args = process.argv.slice(4);
if (args.length === 0) {
  args = ['install'];
}

(function npm() {
  let timer;
  args.push('--verbose');
  let proc = spawn('npm', args);
  proc.stdout.pipe(process.stdout);
  proc.stderr.pipe(process.stderr);
  proc.stdin.end();
  proc.stdout.on('data', () => {
    setTimer();
  });
  proc.stderr.on('data', () => {
    setTimer();
  });

  // side effect: this also restarts when npm exits with a bad code even if it
  // didnt timeout
  proc.on('close', (code, signal) => {
    clearTimeout(timer);
    if (code || signal) {
      console.log('[npm-are-you-sleeping] npm exited with code ' + code + '');

      if (--attempts) {
        console.log('[npm-are-you-sleeping] restarting');
        npm();
      } else {
        console.log('[npm-are-you-sleeping] i tried lots of times. giving up.');
        throw new Error("npm install fails");
      }
    }
  });

  function setTimer() {
    clearTimeout(timer);
    timer = setTimeout(() => {
      console.log('[npm-are-you-sleeping] killing npm with SIGTERM');
      proc.kill('SIGTERM');
      // wait a couple seconds
      timer = setTimeout(() => {
        // its it's still not closed sigkill
        console.log('[npm-are-you-sleeping] killing npm with SIGKILL');
        proc.kill('SIGKILL');
      }, 2000);
    }, timeout);
  }
})();
<commit_msg>build: remove  circleCI config (#76)<commit_after><|endoftext|>


<commit_before>window.charts = window.charts || {};

charts.plotBarChart = function (divId, events, fromTime, tillTime) {
    setTimeout(function () {
        $(divId).empty();
        var margin = {
            top: 20,
            right: 0,
            bottom: 0,
            left: 0
        };
        var width = window.innerWidth;
        var height = width;
        var weekAgo = new Date(moment().subtract("days", 6).format("MM/DD/YYYY"));
        var tomorrow = new Date(moment().add('day', 1).format("MM/DD/YYYY"));
        var xWidth = width / 7;
        var x = d3.time.scale()
            .domain([weekAgo , tomorrow])
            .rangeRound([0, width - margin.left - margin.right])
            .nice();

        var maxDataValue = d3.max(events, function (d) {
            return d.value;
        });
        var y = d3.scale.linear()
            .domain([0, maxDataValue])
            .range([height - margin.top - margin.bottom, 0])
            .nice(4);

        var xAxis = d3.svg.axis()
            .scale(x);

        var yAxis = d3.svg.axis()
            .scale(y)
            .orient('right')
            .ticks(5);

        var svg = d3.select(divId).append('svg')
            .attr('class', 'chart')
            .attr('width', width)
            .attr('height', height)
            .append('g')
            .attr('id', 'bars')
            .attr('transform', 'translate(' + margin.left + ', ' + margin.top + ')');
        var tipText = function (d) {
            return "" + operation + " " + d.value +
                "  on " + moment(d.date).format("ddd MMM DD") + "";
        };
        var tooltipDivForMobile = d3.select("body").append("div")
            .attr("class", "tooltip")
            .style("opacity", 0);
        var tip = d3.tip()
            .attr('class', 'd3-tip')
            .offset([-10, 0])
            .html(function (d) {
                return tipText(d);
            });
        svg.call(tip);
        var gradient = svg.append("svg:defs")
            .append("svg:linearGradient")
            .attr("id", "gradient")
            .attr("x1", "0%")
            .attr("y1", "0%")
            .attr("x2", "0%")
            .attr("y2", "100%")
            .attr("spreadMethod", "reflect");

        gradient.append("svg:stop")
            .attr("offset", "5%")
            .attr("stop-color", "#fff")
            .attr("stop-opacity", 0.8);

        gradient.append("svg:stop")
            .attr("offset", "100%")
            .attr("stop-color", "#fff")
            .attr("stop-opacity", 0.4);

        var gradient_highlight = svg.append("svg:defs")
            .append("svg:linearGradient")
            .attr("id", "gradient_highlight")
            .attr("x1", "0%")
            .attr("y1", "0%")
            .attr("x2", "0%")
            .attr("y2", "100%")
            .attr("spreadMethod", "reflect");

        gradient_highlight.append("svg:stop")
            .attr("offset", "5%")
            .attr("stop-color", "#fff")
            .attr("stop-opacity", 0.9);

        gradient_highlight.append("svg:stop")
            .attr("offset", "100%")
            .attr("stop-color", "#fff")
            .attr("stop-opacity", 0.7);

        var filter = svg.append("svg:defs")
            .append("filter")
            .attr("id", "drop-shadow")
            .attr("height", "130%")
            .attr("width", "170%");

        filter.append("feGaussianBlur")
            .attr("in", "SourceAlpha")
            .attr("stdDeviation", 2)
            .attr("result", "blur");

        filter.append("feOffset")
            .attr("in", "blur")
            .attr("dx", 0)
            .attr("dy", 3   )
            .attr("result", "offsetBlur");

        var feMerge = filter.append("feMerge");

        feMerge.append("feMergeNode")
            .attr("in", "offsetBlur")
        feMerge.append("feMergeNode")
            .attr("in", "SourceGraphic");

        svg.selectAll('.chart')
            .data(events)
            .enter().append('rect')
            .attr('class', 'bar')
            .style("fill", "url(#gradient)")
            .style("stroke", "rgba(255,255,255,0.7)")
            .attr('x', function (d) {
                return x(new Date(d.date));
            })
            .attr('y', function (d) {
                return height - margin.top - margin.bottom - (height - margin.top - margin.bottom - y(d.value));
            })
            .attr('width', xWidth)
            .attr('height', function (d) {
                return height - margin.top - margin.bottom - y(d.value);
            })
            .on("click", function (d) {
                var bars = svg.selectAll('.bar');
                bars
                    .style("stroke", function (data) {
                        if (d === data) {
                            return null;
                        } else {
                            return "rgba(255,255,255,0.7)";
                        }
                    })
                    .style("filter", function (data) {
                        if (d === data) {
                            return "url(#drop-shadow)";
                        }
                    })
                    .style("stroke-width", function (data) {
                        if (d === data) {
                            return "5px";
                        } else {
                            return "1px";
                        }
                    })
                    .style("fill", function (data) {
                        if (d === data) {
                            return "url(#gradient_highlight)";
                        } else {
                            return "url(#gradient)"
                        }
                    })
                    .style("background-color", function (data) {
                        if (d === data) {
                            return "rgba(0, 0, 0, 0.22)";
                        }
                    });
                $(".addCommentButton").show();
                $("#date").html(moment(d.date).format("DD/MM/YY dddd"));
                $("#eventValue").html(getDataPointDescription(d.value));
                var day = moment(d.date).format("DD");
                var month = moment(d.date).format("MM");
                var year = moment(d.date).format("YYYY");
                charts.graphUrl = window.location.href.split(window.location.origin)[1].split("?")[0] + "/" + year + "/" + month + "/" + day;
                charts.selectedDate = moment(d.date).format("YYYY-MM-DD");
                charts.showComments();

                //Redraw
                for (var i = bars.data().length - 1; i >= 0; i--) {
                    if (d === bars.data()[i]) {
                        var bar = bars[0][i];
                        bar.remove();
                        $('#bars').append(bar);
                    }
                };
            });

        svg.append('g')
            .attr('class', 'y axis')
            .call(yAxis);

        svg.append('g')
            .attr('class', 'x axis')
            .attr('transform', 'translate(0, ' + (height - margin.top - margin.bottom) + ')')
            .call(xAxis);

        var getDatesForEvents = function () {
            return _.map(events, function (event) {
                return moment(event.date);
            });
        };

        var getLatestDataPointDate = function () {
            var datesForDataPoints = getDatesForEvents();
            return new Date(Math.max.apply(null, datesForDataPoints));
        };

        var getDataPointDescription = function (eventValue) {
            if (measurement === "time") {
                return humanizeDuration(eventValue * 1000)
            } else {
                return eventValue + " " + "decibels";
            }
        };

        var showDetailsForDate = function (date) {
            var eventValue;
            svg.selectAll('.bar')
                .style("stroke", function (data) {
                        if (moment(data.date).isSame(moment(date))) {
                            return null;
                        } else {
                            return "rgba(255,255,255,0.7)";
                        }
                    })
                .style("filter", function (data) {
                    if (moment(data.date).isSame(moment(date))) {
                        return "url(#drop-shadow)";
                    }
                })
                .style("stroke-width", function (data) {
                    if (moment(data.date).isSame(moment(date))) {
                        return "5px";
                    } else {
                        return "1px";
                    }
                }).style("fill", function (data) {
                    if (moment(data.date).isSame(moment(date))) {
                        return "url(#gradient_highlight)";
                    } else {
                        return "url(#gradient)"
                    }
                });
            $(".addCommentButton").show();
            if (isNaN(date)) {
                $("#date").html('No data logged for this activity');
            } else {
                $("#date").html(moment(date).format("DD/MM/YY dddd"));
            }
            $("#eventValue").html(getDataPointDescription(eventValue));

            var day = moment(date).format("DD");
            var month = moment(date).format("MM");
            var year = moment(date).format("YYYY");
            charts.graphUrl = window.location.href.split(window.location.origin)[1].split("?")[0] + "/" + year + "/" + month + "/" + day;
            charts.showComments();
        };

        var highlightLatestDataPointDate = function () {
            var date = getLatestDataPointDate();
            charts.selectedDate = moment(date).format("YYYY-MM-DD");
            showDetailsForDate(date);
        };

        highlightLatestDataPointDate();

    }, 1000);
};

<commit_msg>Let re-append handle remove for bar redraw<commit_after>window.charts = window.charts || {};

charts.plotBarChart = function (divId, events, fromTime, tillTime) {
    setTimeout(function () {
        $(divId).empty();
        var margin = {
            top: 20,
            right: 0,
            bottom: 0,
            left: 0
        };
        var width = window.innerWidth;
        var height = width;
        var weekAgo = new Date(moment().subtract("days", 6).format("MM/DD/YYYY"));
        var tomorrow = new Date(moment().add('day', 1).format("MM/DD/YYYY"));
        var xWidth = width / 7;
        var x = d3.time.scale()
            .domain([weekAgo , tomorrow])
            .rangeRound([0, width - margin.left - margin.right])
            .nice();

        var maxDataValue = d3.max(events, function (d) {
            return d.value;
        });
        var y = d3.scale.linear()
            .domain([0, maxDataValue])
            .range([height - margin.top - margin.bottom, 0])
            .nice(4);

        var xAxis = d3.svg.axis()
            .scale(x);

        var yAxis = d3.svg.axis()
            .scale(y)
            .orient('right')
            .ticks(5);

        var svg = d3.select(divId).append('svg')
            .attr('class', 'chart')
            .attr('width', width)
            .attr('height', height)
            .append('g')
            .attr('id', 'bars')
            .attr('transform', 'translate(' + margin.left + ', ' + margin.top + ')');
        var tipText = function (d) {
            return "" + operation + " " + d.value +
                "  on " + moment(d.date).format("ddd MMM DD") + "";
        };
        var tooltipDivForMobile = d3.select("body").append("div")
            .attr("class", "tooltip")
            .style("opacity", 0);
        var tip = d3.tip()
            .attr('class', 'd3-tip')
            .offset([-10, 0])
            .html(function (d) {
                return tipText(d);
            });
        svg.call(tip);
        var gradient = svg.append("svg:defs")
            .append("svg:linearGradient")
            .attr("id", "gradient")
            .attr("x1", "0%")
            .attr("y1", "0%")
            .attr("x2", "0%")
            .attr("y2", "100%")
            .attr("spreadMethod", "reflect");

        gradient.append("svg:stop")
            .attr("offset", "5%")
            .attr("stop-color", "#fff")
            .attr("stop-opacity", 0.8);

        gradient.append("svg:stop")
            .attr("offset", "100%")
            .attr("stop-color", "#fff")
            .attr("stop-opacity", 0.4);

        var gradient_highlight = svg.append("svg:defs")
            .append("svg:linearGradient")
            .attr("id", "gradient_highlight")
            .attr("x1", "0%")
            .attr("y1", "0%")
            .attr("x2", "0%")
            .attr("y2", "100%")
            .attr("spreadMethod", "reflect");

        gradient_highlight.append("svg:stop")
            .attr("offset", "5%")
            .attr("stop-color", "#fff")
            .attr("stop-opacity", 0.9);

        gradient_highlight.append("svg:stop")
            .attr("offset", "100%")
            .attr("stop-color", "#fff")
            .attr("stop-opacity", 0.7);

        var filter = svg.append("svg:defs")
            .append("filter")
            .attr("id", "drop-shadow")
            .attr("height", "130%")
            .attr("width", "170%");

        filter.append("feGaussianBlur")
            .attr("in", "SourceAlpha")
            .attr("stdDeviation", 2)
            .attr("result", "blur");

        filter.append("feOffset")
            .attr("in", "blur")
            .attr("dx", 0)
            .attr("dy", 3   )
            .attr("result", "offsetBlur");

        var feMerge = filter.append("feMerge");

        feMerge.append("feMergeNode")
            .attr("in", "offsetBlur")
        feMerge.append("feMergeNode")
            .attr("in", "SourceGraphic");

        svg.selectAll('.chart')
            .data(events)
            .enter().append('rect')
            .attr('class', 'bar')
            .style("fill", "url(#gradient)")
            .style("stroke", "rgba(255,255,255,0.7)")
            .attr('x', function (d) {
                return x(new Date(d.date));
            })
            .attr('y', function (d) {
                return height - margin.top - margin.bottom - (height - margin.top - margin.bottom - y(d.value));
            })
            .attr('width', xWidth)
            .attr('height', function (d) {
                return height - margin.top - margin.bottom - y(d.value);
            })
            .on("click", function (d) {
                var bars = svg.selectAll('.bar');
                bars
                    .style("stroke", function (data) {
                        if (d === data) {
                            return null;
                        } else {
                            return "rgba(255,255,255,0.7)";
                        }
                    })
                    .style("filter", function (data) {
                        if (d === data) {
                            return "url(#drop-shadow)";
                        }
                    })
                    .style("stroke-width", function (data) {
                        if (d === data) {
                            return "5px";
                        } else {
                            return "1px";
                        }
                    })
                    .style("fill", function (data) {
                        if (d === data) {
                            return "url(#gradient_highlight)";
                        } else {
                            return "url(#gradient)"
                        }
                    })
                    .style("background-color", function (data) {
                        if (d === data) {
                            return "rgba(0, 0, 0, 0.22)";
                        }
                    });
                $(".addCommentButton").show();
                $("#date").html(moment(d.date).format("DD/MM/YY dddd"));
                $("#eventValue").html(getDataPointDescription(d.value));
                var day = moment(d.date).format("DD");
                var month = moment(d.date).format("MM");
                var year = moment(d.date).format("YYYY");
                charts.graphUrl = window.location.href.split(window.location.origin)[1].split("?")[0] + "/" + year + "/" + month + "/" + day;
                charts.selectedDate = moment(d.date).format("YYYY-MM-DD");
                charts.showComments();

                //Redraw
                for (var i = bars.data().length - 1; i >= 0; i--) {
                    if (d === bars.data()[i]) {
                        var bar = bars[0][i];
                        $('#bars').append(bar);
                    }
                };
            });

        svg.append('g')
            .attr('class', 'y axis')
            .call(yAxis);

        svg.append('g')
            .attr('class', 'x axis')
            .attr('transform', 'translate(0, ' + (height - margin.top - margin.bottom) + ')')
            .call(xAxis);

        var getDatesForEvents = function () {
            return _.map(events, function (event) {
                return moment(event.date);
            });
        };

        var getLatestDataPointDate = function () {
            var datesForDataPoints = getDatesForEvents();
            return new Date(Math.max.apply(null, datesForDataPoints));
        };

        var getDataPointDescription = function (eventValue) {
            if (measurement === "time") {
                return humanizeDuration(eventValue * 1000)
            } else {
                return eventValue + " " + "decibels";
            }
        };

        var showDetailsForDate = function (date) {
            var eventValue;
            svg.selectAll('.bar')
                .style("stroke", function (data) {
                        if (moment(data.date).isSame(moment(date))) {
                            return null;
                        } else {
                            return "rgba(255,255,255,0.7)";
                        }
                    })
                .style("filter", function (data) {
                    if (moment(data.date).isSame(moment(date))) {
                        return "url(#drop-shadow)";
                    }
                })
                .style("stroke-width", function (data) {
                    if (moment(data.date).isSame(moment(date))) {
                        return "5px";
                    } else {
                        return "1px";
                    }
                }).style("fill", function (data) {
                    if (moment(data.date).isSame(moment(date))) {
                        return "url(#gradient_highlight)";
                    } else {
                        return "url(#gradient)"
                    }
                });
            $(".addCommentButton").show();
            if (isNaN(date)) {
                $("#date").html('No data logged for this activity');
            } else {
                $("#date").html(moment(date).format("DD/MM/YY dddd"));
            }
            $("#eventValue").html(getDataPointDescription(eventValue));

            var day = moment(date).format("DD");
            var month = moment(date).format("MM");
            var year = moment(date).format("YYYY");
            charts.graphUrl = window.location.href.split(window.location.origin)[1].split("?")[0] + "/" + year + "/" + month + "/" + day;
            charts.showComments();
        };

        var highlightLatestDataPointDate = function () {
            var date = getLatestDataPointDate();
            charts.selectedDate = moment(date).format("YYYY-MM-DD");
            showDetailsForDate(date);
        };

        highlightLatestDataPointDate();

    }, 1000);
};

<|endoftext|>


<commit_before>
define([
    'flight/lib/component',
    'flight/lib/registry',
    'data',
    'service/vertex',
    'service/ontology',
    'util/vertex/list',
    'util/vertex/formatters',
    './filters/filters',
    'tpl!./search',
    'tpl!./conceptItem',
    'tpl!./conceptSections',
    'tpl!util/alert',
    'util/jquery.ui.draggable.multiselect',
    'sf'
], function(
    defineComponent,
    registry,
    appData,
    VertexService,
    OntologyService,
    VertexList,
    F,
    Filters,
    template,
    conceptItemTemplate,
    conceptSectionsTemplate,
    alertTemplate,
    multiselect,
    sf) {
    'use strict';

    return defineComponent(Search);

    function Search() {
        this.vertexService = new VertexService();
        this.ontologyService = new OntologyService();
        this.currentQuery = null;

        this.defaultAttrs({
            formSelector: '.navbar-search',
            querySelector: '.navbar-search .search-query',
            queryValidationSelector: '.search-query-validation',
            clearSearchSelector: '.search-query-container a',
            filtersInfoSelector: '.filter-info',
            resultsSummarySelector: '.search-results-summary',
            entitiesHeaderBadgeSelector: '.search-results-summary li.entities .badge',
            summaryResultItemSelector: '.search-results-summary li',
            resultsSelector: '.search-results',
            filtersSelector: '.search-filters'
        });

        this.searchResults = null;

        this.onEntitySearchResultsForConcept = function(
            $searchResultsSummary,
            concept,
            entities,
            count,
            parentPropertyListElements
        ) {
            var self = this,
                resultsCount = count,
                badge = this.updateCountBadgeForConcept(concept.id, resultsCount),
                li = badge.closest('li').toggle(resultsCount > 0);

            parentPropertyListElements = parentPropertyListElements || $();

            this.select('entitiesHeaderBadgeSelector').removeClass('loading');

            if (resultsCount) {
                parentPropertyListElements.show();
            }

            if (concept.children && concept.children.length > 0) {
                var parentLis = parentPropertyListElements.add(li);
                concept.children.forEach(function(childConcept) {
                    self.onEntitySearchResultsForConcept(
                        $searchResultsSummary,
                        childConcept,
                        entities,
                        count,
                        parentLis
                    );
                });
            }
        };

        this.onFormSearch = function(evt) {
            evt.preventDefault();
            var $searchQueryValidation = this.select('queryValidationSelector');
            $searchQueryValidation.html('');

            var query = this.select('querySelector').val();
            if (query) {
                query = $.trim(query);
            }
            if (!query && !this.relatedToVertexId) {
                this.select('resultsSummarySelector').empty();
                return $searchQueryValidation.html(alertTemplate({ error: 'Query cannot be empty' }));
            }
            this.trigger('search', { query: query });
            return false;
        };

        var conceptToClassMap = {}, classNameToConceptMap = {}, clsIndex = 0;
        this.classSafeConceptId = function(conceptId) {
            if (conceptToClassMap[conceptId]) {
                return conceptToClassMap[conceptId];
            }

            conceptToClassMap[conceptId] = 'cId' + (clsIndex++);
            classNameToConceptMap[conceptToClassMap[conceptId]] = conceptId;

            return conceptToClassMap[conceptId];
        };

        this.getConceptChildrenHtml = function(concept, indent) {
            var self = this,
                html = '';

            (concept.children || []).forEach(function(concept) {
                html += conceptItemTemplate({
                    concept: concept,
                    classSafeConceptId: self.classSafeConceptId(concept.id),
                    indent: indent
                });
                if (concept.children && concept.children.length > 0) {
                    html += self.getConceptChildrenHtml(concept, indent + 15);
                }
            });
            return html;
        };

        this.updateCountBadgeForConcept = function(conceptId, count) {
            return this.$node.find('.' + this.classSafeConceptId(conceptId) + ' .badge')
                .removeClass('loading')
                .data('count', count)
                .attr('title', F.number.pretty(count))
                .text(F.number.prettyApproximate(count));
        };

        this.popoutIfNeeded = function() {
            if (!this.$node.closest(':data(menubarName)').hasClass('visible')) {
                this.trigger ('menubarToggleDisplay', { name: 'search' });
            }
        }

        this.onSearchByEntity = function(evt, data) {
            this.select('querySelector').val(data.query);
            this.clearFilters({triggerUpdates: false});
            this.trigger ('search', { query: data.query});
        };

        this.onSearchByRelatedEntity = function(evt, data) {
            this.select('querySelector').val('');
        };

        this.onClearSearchButton = function(event) {
            event.preventDefault();
            event.stopPropagation();
            $(event.target).blur();
            this.trigger('clearSearch')
        };

        this.onClearSearch = function() {
            this.select('resultsSummarySelector').empty();
            this.clearFilters();
            this.select('querySelector').val('').focus();
            this.select('clearSearchSelector').hide();
        }

        this.onSearch = function(evt, data) {
            data = data || {};
            var query = data.query || this.select('querySelector').val();

            if (!this.searchResults) {
                this.searchResults = {};
            }

            this.select('queryValidationSelector').empty();
            this.hideSearchResults();
            this.popoutIfNeeded();

            if (this.previousSearch) {
                this.previousSearch.abort();
            }

            if (this.infiniteSearchRequest) {
                this.infiniteSearchRequest.abort();
            }

            if (query != data.query) {
                this.select('querySelector').val(data.query);
            }

            var relatedToVertexId = this.entityFilters && this.entityFilters.relatedToVertexId;
            if (relatedToVertexId) {
                query = {
                    query: _.isUndefined(query.query) ? query : query.query,
                    relatedToVertexId: relatedToVertexId
                };
            }

            var self = this;

            this.ontologyService.concepts().done(function(concepts) {
                this.updateConceptSections(concepts);

                var paging = { offset: 0, size: 100 },
                    subTypeFilter = null;

                this.previousSearch = this.vertexService.graphVertexSearch(query, this.filters, subTypeFilter, paging)
                    .done(function(vertexResults) {
                        self.previousSearch = null;

                        var results = {},
                            sortVerticesIntoResults = function(v) {
                                var conceptType = F.vertex.prop(v, 'conceptType'),
                                    addToSearchResults = function(conceptType) {
                                        if (!results[conceptType]) results[conceptType] = [];

                                        // Check for an existing result with the same id
                                        var resultFound = results[conceptType].some(function(result) {
                                            return result.id === v.id;
                                        });

                                        // Only store unique results
                                        if (resultFound === false) {
                                            results[conceptType].push(v);
                                        }
                                    },
                                    vertexConcept = concepts.byId[conceptType];

                                if (!conceptType) {
                                    console.error('Found vertex without a conceptType: ', v);
                                }

                                while (vertexConcept) {
                                    addToSearchResults(vertexConcept.id, v);
                                    vertexConcept = vertexConcept.parentId ?
                                        concepts.byId[vertexConcept.parentId] :
                                        null;
                                }
                            };
                        vertexResults.vertices.forEach(sortVerticesIntoResults);

                        self.searchResults = results;

                        var counts = _.values(vertexResults.verticesCount);
                        if (counts.length === 0 || Math.max.apply([], counts) === 0) {
                            var headerTextNode = self.$node.find('.search-results-summary li.entities');
                            if (headerTextNode.length) {
                                headerTextNode[0].normalize();
                                headerTextNode[0].textContent = 'No Entities';
                            }
                        } else {
                            var countMap = vertexResults.verticesCount,
                                summaryNode = self.select('resultsSummarySelector');

                            concepts.byTitle.forEach(function(concept) {
                                var count = countMap[concept.id] || 0,
                                    childrenCounts = _.pick(countMap, _.pluck(concept.children, 'id')),
                                    total = _.reduce(childrenCounts, function(memo, i) {
                                        return memo + i
                                    }, 0);

                                self.onEntitySearchResultsForConcept(
                                    summaryNode,
                                    concept,
                                    results.entity,
                                    count + total
                                );
                            });
                        }

                    }).fail(function(request, reason) {
                        if (reason === 'abort') return;
                        var $searchQueryValidation = self.select('queryValidationSelector');
                        self.select('resultsSummarySelector').empty();
                        return $searchQueryValidation.html(alertTemplate({ error: 'Invalid query' }));
                    });
            }.bind(this));
        };

        this.updateConceptSections = function(concepts) {
            var $searchResultsSummary = this.select('resultsSummarySelector'),
                resultsHtml = this.getConceptChildrenHtml(concepts.entityConcept, 15);

            $searchResultsSummary.html(conceptSectionsTemplate({ resultsHtml: resultsHtml }));
            $('.badge', $searchResultsSummary).addClass('loading');
        };

        this.onSummaryResultItemClick = function(evt) {
            evt.preventDefault();

            var $target = $(evt.target).parents('li');
            if ($target.hasClass('active')) {
                return this.close(evt);
            }

            var count = $target.find('.badge').data('count');
            if (count === 0) {
                return this.close(evt);
            }

            this.$node.find('.search-results-summary .active').removeClass('active');
            $target.addClass('active');

            this.trigger('showSearchResults', {
                conceptId: $target.data('conceptId'),
                count: count
            });
        };

        this.onShowSearchResults = function(evt, data) {
            var self = this,
                $searchResults = this.select('resultsSelector'),
                vertexIds = _.pluck(this.searchResults[data.conceptId] || [], 'id'),
                vertices = appData.vertices(vertexIds);

            this.hideSearchResults();
            this.select('filtersSelector').hide();

            if (data.count) {
                VertexList.attachTo($searchResults.find('.content'), {
                    vertices: vertices,
                    infiniteScrolling: true,
                    verticesConceptId: data.conceptId,
                    total: data.count
                });
                this.makeResizable($searchResults);
                $searchResults.show();
                $searchResults.find('.multi-select').focus();
            }
            this.trigger(document, 'paneResized');
        };

        this.makeResizable = function(node) {
            var self = this;

            // Add splitbar to search results
            return node.resizable({
                handles: 'e',
                minWidth: 200,
                maxWidth: 350,
                resize: function() {
                    self.trigger(document, 'paneResized');
                }
            });
        };

        this.onKeyUp = function(evt) {
            var search = this.select('querySelector'),
                query = search.val();

            if (query != this.currentQuery) {
                this.trigger('searchQueryChanged', { query: query});
                this.currentQuery = query;
            }

            if (evt.which === 27) {
                search.blur();
            }
        };

        this.onQueryFocus = function(evt, data) {
            var filters = this.select('filtersSelector').show();
            this.hideSearchResults();
            this.$node.find('.search-results-summary .active').removeClass('active');
        };

        this.hideSearchResults = function() {
            registry.findInstanceInfoByNode(
                this.select('resultsSelector')
                    .hide()
                    .find('.content')[0]
            ).forEach(function(info) {
                info.instance.teardown();
            });
            this.trigger(document, 'paneResized');
        };

        this.close = function(e) {
            this.hideSearchResults();
            this.$node.find('.search-results-summary .active').removeClass('active');
        };

        this.onPaneVisible = function() {
            this.select('querySelector').focus().select();
        };

        this.after('initialize', function() {
            var self = this;
            this.searchResults = {};
            this.$node.html(template({}));

            this.hideSearchResults();

            var filters = this.select('filtersSelector').hide();
            Filters.attachTo(filters.find('.content'));
            this.makeResizable(filters);

            this.on('filterschange', this.onFiltersChange);
            this.on('infiniteScrollRequest', this.onInfiniteScrollRequest);

            this.on(document, 'searchByEntity', this.onSearchByEntity);
            this.on(document, 'searchByRelatedEntity', this.onSearchByRelatedEntity);
            this.on(document, 'search', this.onSearch);
            this.on(document, 'clearSearch', this.onClearSearch);
            this.on(document, 'showSearchResults', this.onShowSearchResults);
            this.on(document, 'menubarToggleDisplay', this.onMenubarToggle);
            this.on(document, 'searchPaneVisible', this.onPaneVisible);
            this.on('submit', {
                formSelector: this.onFormSearch
            });
            this.on('click', {
                summaryResultItemSelector: this.onSummaryResultItemClick,
                filtersInfoSelector: this.onFiltersInfoRemoveClick,
                clearSearchSelector: this.onClearSearchButton
            });
            this.on('keyup', {
                querySelector: this.onKeyUp
            });
            this.on('change keyup', function() {
                this.select('clearSearchSelector').toggle(
                    $.trim(this.select('querySelector').val()).length > 0
                )
            });
            this.select('clearSearchSelector').hide();

            this.select('querySelector').on('focus', this.onQueryFocus.bind(this));

            this.trigger(document, 'registerKeyboardShortcuts', {
                scope: 'Search',
                shortcuts: {
                    'meta-a': { fire: 'selectAll', desc: 'Select all search results' },
                    up: { fire: 'up', desc: 'Select previous result'},
                    down: { fire: 'down', desc: 'Select next result'}
                }
            });
        });

        this.onMenubarToggle = function(evt, data) {
            var pane = this.$node.closest(':data(menubarName)');
            if (data.name === pane.data('menubarName')) {
                if (!pane.hasClass('visible')) {
                    this.$node.find('.search-results-summary .active').removeClass('active');
                    this.select('filtersSelector').hide();
                    this.hideSearchResults();
                }
            }
        };

        this.onInfiniteScrollRequest = function(evt, data) {
            var query = this.select('querySelector').val(),
                trigger = this.trigger.bind(this,
                   this.select('resultsSelector').find('.content'),
                   'addInfiniteVertices'
                );

            if (this.infiniteSearchRequest) {
                this.infiniteSearchRequest.abort();
            }

            if (this.entityFilters && this.entityFilters.relatedToVertexId) {
                query = { query: query, relatedToVertexId: this.entityFilters.relatedToVertexId };
            }

            this.infiniteSearchRequest = this.vertexService.graphVertexSearch(
                    query,
                    this.filters,
                    data.conceptType,
                    data.paging
            )
            .fail(function() {
                trigger({ success: false });
            })
            .done(function(results) {
                trigger({ success: true, vertices: results.vertices });
            });
        };

        this.onFiltersChange = function(evt, data) {
            this.filters = data.propertyFilters;
            this.entityFilters = data.entityFilters;

            var filterInfo = this.select('filtersInfoSelector'),
                numberOfFilters = this.filters.length + _.keys(this.entityFilters).length,
                query = this.select('querySelector').val();

            filterInfo.find('.message').text(F.string.plural(numberOfFilters, 'filter') + ' applied');
            filterInfo.toggle(numberOfFilters > 0);

            if (!query && !this.entityFilters.relatedToVertexId) {
                this.select('resultsSummarySelector').empty();
                return;
            }

            this.trigger('search', { query: query || '' });
        };

        this.onFiltersInfoRemoveClick = function() {
            this.clearFilters();
        };

        this.clearFilters = function(options) {
            this.trigger(this.select('filtersSelector').find('.content'), 'clearfilters', options);
        };
    }
});
<commit_msg>Show clear search button when searching related.<commit_after>
define([
    'flight/lib/component',
    'flight/lib/registry',
    'data',
    'service/vertex',
    'service/ontology',
    'util/vertex/list',
    'util/vertex/formatters',
    './filters/filters',
    'tpl!./search',
    'tpl!./conceptItem',
    'tpl!./conceptSections',
    'tpl!util/alert',
    'util/jquery.ui.draggable.multiselect',
    'sf'
], function(
    defineComponent,
    registry,
    appData,
    VertexService,
    OntologyService,
    VertexList,
    F,
    Filters,
    template,
    conceptItemTemplate,
    conceptSectionsTemplate,
    alertTemplate,
    multiselect,
    sf) {
    'use strict';

    return defineComponent(Search);

    function Search() {
        this.vertexService = new VertexService();
        this.ontologyService = new OntologyService();
        this.currentQuery = null;

        this.defaultAttrs({
            formSelector: '.navbar-search',
            querySelector: '.navbar-search .search-query',
            queryValidationSelector: '.search-query-validation',
            clearSearchSelector: '.search-query-container a',
            filtersInfoSelector: '.filter-info',
            resultsSummarySelector: '.search-results-summary',
            entitiesHeaderBadgeSelector: '.search-results-summary li.entities .badge',
            summaryResultItemSelector: '.search-results-summary li',
            resultsSelector: '.search-results',
            filtersSelector: '.search-filters'
        });

        this.searchResults = null;

        this.onEntitySearchResultsForConcept = function(
            $searchResultsSummary,
            concept,
            entities,
            count,
            parentPropertyListElements
        ) {
            var self = this,
                resultsCount = count,
                badge = this.updateCountBadgeForConcept(concept.id, resultsCount),
                li = badge.closest('li').toggle(resultsCount > 0);

            parentPropertyListElements = parentPropertyListElements || $();

            this.select('entitiesHeaderBadgeSelector').removeClass('loading');

            if (resultsCount) {
                parentPropertyListElements.show();
            }

            if (concept.children && concept.children.length > 0) {
                var parentLis = parentPropertyListElements.add(li);
                concept.children.forEach(function(childConcept) {
                    self.onEntitySearchResultsForConcept(
                        $searchResultsSummary,
                        childConcept,
                        entities,
                        count,
                        parentLis
                    );
                });
            }
        };

        this.onFormSearch = function(evt) {
            evt.preventDefault();
            var $searchQueryValidation = this.select('queryValidationSelector');
            $searchQueryValidation.html('');

            var query = this.select('querySelector').val();
            if (query) {
                query = $.trim(query);
            }
            if (!query && !this.relatedToVertexId) {
                this.select('resultsSummarySelector').empty();
                return $searchQueryValidation.html(alertTemplate({ error: 'Query cannot be empty' }));
            }
            this.trigger('search', { query: query });
            return false;
        };

        var conceptToClassMap = {}, classNameToConceptMap = {}, clsIndex = 0;
        this.classSafeConceptId = function(conceptId) {
            if (conceptToClassMap[conceptId]) {
                return conceptToClassMap[conceptId];
            }

            conceptToClassMap[conceptId] = 'cId' + (clsIndex++);
            classNameToConceptMap[conceptToClassMap[conceptId]] = conceptId;

            return conceptToClassMap[conceptId];
        };

        this.getConceptChildrenHtml = function(concept, indent) {
            var self = this,
                html = '';

            (concept.children || []).forEach(function(concept) {
                html += conceptItemTemplate({
                    concept: concept,
                    classSafeConceptId: self.classSafeConceptId(concept.id),
                    indent: indent
                });
                if (concept.children && concept.children.length > 0) {
                    html += self.getConceptChildrenHtml(concept, indent + 15);
                }
            });
            return html;
        };

        this.updateCountBadgeForConcept = function(conceptId, count) {
            return this.$node.find('.' + this.classSafeConceptId(conceptId) + ' .badge')
                .removeClass('loading')
                .data('count', count)
                .attr('title', F.number.pretty(count))
                .text(F.number.prettyApproximate(count));
        };

        this.popoutIfNeeded = function() {
            if (!this.$node.closest(':data(menubarName)').hasClass('visible')) {
                this.trigger ('menubarToggleDisplay', { name: 'search' });
            }
        }

        this.onSearchByEntity = function(evt, data) {
            this.select('querySelector').val(data.query);
            this.clearFilters({triggerUpdates: false});
            this.trigger ('search', { query: data.query});
        };

        this.onSearchByRelatedEntity = function(evt, data) {
            this.select('querySelector').val('');
        };

        this.onClearSearchButton = function(event) {
            event.preventDefault();
            event.stopPropagation();
            $(event.target).blur();
            this.trigger('clearSearch')
        };

        this.onClearSearch = function() {
            this.select('resultsSummarySelector').empty();
            this.clearFilters();
            this.select('querySelector').val('').focus();
            this.select('clearSearchSelector').hide();
        }

        this.onSearch = function(evt, data) {
            data = data || {};
            var query = data.query || this.select('querySelector').val();

            if (!this.searchResults) {
                this.searchResults = {};
            }

            this.select('queryValidationSelector').empty();
            this.select('clearSearchSelector').show();
            this.hideSearchResults();
            this.popoutIfNeeded();

            if (this.previousSearch) {
                this.previousSearch.abort();
            }

            if (this.infiniteSearchRequest) {
                this.infiniteSearchRequest.abort();
            }

            if (query != data.query) {
                this.select('querySelector').val(data.query);
            }

            var relatedToVertexId = this.entityFilters && this.entityFilters.relatedToVertexId;
            if (relatedToVertexId) {
                query = {
                    query: _.isUndefined(query.query) ? query : query.query,
                    relatedToVertexId: relatedToVertexId
                };
            }

            var self = this;

            this.ontologyService.concepts().done(function(concepts) {
                this.updateConceptSections(concepts);

                var paging = { offset: 0, size: 100 },
                    subTypeFilter = null;

                this.previousSearch = this.vertexService.graphVertexSearch(query, this.filters, subTypeFilter, paging)
                    .done(function(vertexResults) {
                        self.previousSearch = null;

                        var results = {},
                            sortVerticesIntoResults = function(v) {
                                var conceptType = F.vertex.prop(v, 'conceptType'),
                                    addToSearchResults = function(conceptType) {
                                        if (!results[conceptType]) results[conceptType] = [];

                                        // Check for an existing result with the same id
                                        var resultFound = results[conceptType].some(function(result) {
                                            return result.id === v.id;
                                        });

                                        // Only store unique results
                                        if (resultFound === false) {
                                            results[conceptType].push(v);
                                        }
                                    },
                                    vertexConcept = concepts.byId[conceptType];

                                if (!conceptType) {
                                    console.error('Found vertex without a conceptType: ', v);
                                }

                                while (vertexConcept) {
                                    addToSearchResults(vertexConcept.id, v);
                                    vertexConcept = vertexConcept.parentId ?
                                        concepts.byId[vertexConcept.parentId] :
                                        null;
                                }
                            };
                        vertexResults.vertices.forEach(sortVerticesIntoResults);

                        self.searchResults = results;

                        var counts = _.values(vertexResults.verticesCount);
                        if (counts.length === 0 || Math.max.apply([], counts) === 0) {
                            var headerTextNode = self.$node.find('.search-results-summary li.entities');
                            if (headerTextNode.length) {
                                headerTextNode[0].normalize();
                                headerTextNode[0].textContent = 'No Entities';
                            }
                        } else {
                            var countMap = vertexResults.verticesCount,
                                summaryNode = self.select('resultsSummarySelector');

                            concepts.byTitle.forEach(function(concept) {
                                var count = countMap[concept.id] || 0,
                                    childrenCounts = _.pick(countMap, _.pluck(concept.children, 'id')),
                                    total = _.reduce(childrenCounts, function(memo, i) {
                                        return memo + i
                                    }, 0);

                                self.onEntitySearchResultsForConcept(
                                    summaryNode,
                                    concept,
                                    results.entity,
                                    count + total
                                );
                            });
                        }

                    }).fail(function(request, reason) {
                        if (reason === 'abort') return;
                        var $searchQueryValidation = self.select('queryValidationSelector');
                        self.select('resultsSummarySelector').empty();
                        return $searchQueryValidation.html(alertTemplate({ error: 'Invalid query' }));
                    });
            }.bind(this));
        };

        this.updateConceptSections = function(concepts) {
            var $searchResultsSummary = this.select('resultsSummarySelector'),
                resultsHtml = this.getConceptChildrenHtml(concepts.entityConcept, 15);

            $searchResultsSummary.html(conceptSectionsTemplate({ resultsHtml: resultsHtml }));
            $('.badge', $searchResultsSummary).addClass('loading');
        };

        this.onSummaryResultItemClick = function(evt) {
            evt.preventDefault();

            var $target = $(evt.target).parents('li');
            if ($target.hasClass('active')) {
                return this.close(evt);
            }

            var count = $target.find('.badge').data('count');
            if (count === 0) {
                return this.close(evt);
            }

            this.$node.find('.search-results-summary .active').removeClass('active');
            $target.addClass('active');

            this.trigger('showSearchResults', {
                conceptId: $target.data('conceptId'),
                count: count
            });
        };

        this.onShowSearchResults = function(evt, data) {
            var self = this,
                $searchResults = this.select('resultsSelector'),
                vertexIds = _.pluck(this.searchResults[data.conceptId] || [], 'id'),
                vertices = appData.vertices(vertexIds);

            this.hideSearchResults();
            this.select('filtersSelector').hide();

            if (data.count) {
                VertexList.attachTo($searchResults.find('.content'), {
                    vertices: vertices,
                    infiniteScrolling: true,
                    verticesConceptId: data.conceptId,
                    total: data.count
                });
                this.makeResizable($searchResults);
                $searchResults.show();
                $searchResults.find('.multi-select').focus();
            }
            this.trigger(document, 'paneResized');
        };

        this.makeResizable = function(node) {
            var self = this;

            // Add splitbar to search results
            return node.resizable({
                handles: 'e',
                minWidth: 200,
                maxWidth: 350,
                resize: function() {
                    self.trigger(document, 'paneResized');
                }
            });
        };

        this.onKeyUp = function(evt) {
            var search = this.select('querySelector'),
                query = search.val();

            if (query != this.currentQuery) {
                this.trigger('searchQueryChanged', { query: query});
                this.currentQuery = query;
            }

            if (evt.which === 27) {
                search.blur();
            }
        };

        this.onQueryFocus = function(evt, data) {
            var filters = this.select('filtersSelector').show();
            this.hideSearchResults();
            this.$node.find('.search-results-summary .active').removeClass('active');
        };

        this.hideSearchResults = function() {
            registry.findInstanceInfoByNode(
                this.select('resultsSelector')
                    .hide()
                    .find('.content')[0]
            ).forEach(function(info) {
                info.instance.teardown();
            });
            this.trigger(document, 'paneResized');
        };

        this.close = function(e) {
            this.hideSearchResults();
            this.$node.find('.search-results-summary .active').removeClass('active');
        };

        this.onPaneVisible = function() {
            this.select('querySelector').focus().select();
        };

        this.after('initialize', function() {
            var self = this;
            this.searchResults = {};
            this.$node.html(template({}));

            this.hideSearchResults();

            var filters = this.select('filtersSelector').hide();
            Filters.attachTo(filters.find('.content'));
            this.makeResizable(filters);

            this.on('filterschange', this.onFiltersChange);
            this.on('infiniteScrollRequest', this.onInfiniteScrollRequest);

            this.on(document, 'searchByEntity', this.onSearchByEntity);
            this.on(document, 'searchByRelatedEntity', this.onSearchByRelatedEntity);
            this.on(document, 'search', this.onSearch);
            this.on(document, 'clearSearch', this.onClearSearch);
            this.on(document, 'showSearchResults', this.onShowSearchResults);
            this.on(document, 'menubarToggleDisplay', this.onMenubarToggle);
            this.on(document, 'searchPaneVisible', this.onPaneVisible);
            this.on('submit', {
                formSelector: this.onFormSearch
            });
            this.on('click', {
                summaryResultItemSelector: this.onSummaryResultItemClick,
                filtersInfoSelector: this.onFiltersInfoRemoveClick,
                clearSearchSelector: this.onClearSearchButton
            });
            this.on('keyup', {
                querySelector: this.onKeyUp
            });
            this.on('change keyup', function() {
                this.select('clearSearchSelector').toggle(
                    $.trim(this.select('querySelector').val()).length > 0
                )
            });
            this.select('clearSearchSelector').hide();

            this.select('querySelector').on('focus', this.onQueryFocus.bind(this));

            this.trigger(document, 'registerKeyboardShortcuts', {
                scope: 'Search',
                shortcuts: {
                    'meta-a': { fire: 'selectAll', desc: 'Select all search results' },
                    up: { fire: 'up', desc: 'Select previous result'},
                    down: { fire: 'down', desc: 'Select next result'}
                }
            });
        });

        this.onMenubarToggle = function(evt, data) {
            var pane = this.$node.closest(':data(menubarName)');
            if (data.name === pane.data('menubarName')) {
                if (!pane.hasClass('visible')) {
                    this.$node.find('.search-results-summary .active').removeClass('active');
                    this.select('filtersSelector').hide();
                    this.hideSearchResults();
                }
            }
        };

        this.onInfiniteScrollRequest = function(evt, data) {
            var query = this.select('querySelector').val(),
                trigger = this.trigger.bind(this,
                   this.select('resultsSelector').find('.content'),
                   'addInfiniteVertices'
                );

            if (this.infiniteSearchRequest) {
                this.infiniteSearchRequest.abort();
            }

            if (this.entityFilters && this.entityFilters.relatedToVertexId) {
                query = { query: query, relatedToVertexId: this.entityFilters.relatedToVertexId };
            }

            this.infiniteSearchRequest = this.vertexService.graphVertexSearch(
                    query,
                    this.filters,
                    data.conceptType,
                    data.paging
            )
            .fail(function() {
                trigger({ success: false });
            })
            .done(function(results) {
                trigger({ success: true, vertices: results.vertices });
            });
        };

        this.onFiltersChange = function(evt, data) {
            this.filters = data.propertyFilters;
            this.entityFilters = data.entityFilters;

            var filterInfo = this.select('filtersInfoSelector'),
                numberOfFilters = this.filters.length + _.keys(this.entityFilters).length,
                query = this.select('querySelector').val();

            filterInfo.find('.message').text(F.string.plural(numberOfFilters, 'filter') + ' applied');
            filterInfo.toggle(numberOfFilters > 0);

            if (!query && !this.entityFilters.relatedToVertexId) {
                this.select('resultsSummarySelector').empty();
                return;
            }

            this.trigger('search', { query: query || '' });
        };

        this.onFiltersInfoRemoveClick = function() {
            this.clearFilters();
        };

        this.clearFilters = function(options) {
            this.trigger(this.select('filtersSelector').find('.content'), 'clearfilters', options);
        };
    }
});
<|endoftext|>