<commit_before>/* @flow */
import React from 'react';
import { lifecycle, compose, withState, withProps, mapProps } from 'recompose';
import OncoGrid from 'oncogrid';
import { uniqueId } from 'lodash';
import { connect } from 'react-redux';
import withSize from '@ncigdc/utils/withSize';
import FullScreenIcon from 'react-icons/lib/md/fullscreen';
import JSURL from 'jsurl';

import { exitFullScreen, enterFullScreen, isFullScreen } from '@ncigdc/utils/fullscreen';
import { getFilterValue, makeFilter } from '@ncigdc/utils/filters';
import withRouter from '@ncigdc/utils/withRouter';

import Loader from '@ncigdc/uikit/Loaders/Loader';
import Button from '@ncigdc/uikit/Button';
import { Row, Column } from '@ncigdc/uikit/Flex';
import { Tooltip } from '@ncigdc/uikit/Tooltip';

import { StepLegend, SwatchLegend } from '@ncigdc/components/Legends';
import SelectModal from '@ncigdc/components/Modals/SelectModal';
import DownloadVisualizationButton from '@ncigdc/components/DownloadVisualizationButton';
import Hidden from '@ncigdc/components/Hidden';

import { visualizingButton } from '@ncigdc/theme/mixins';
import { setModal } from '@ncigdc/dux/modal';
import wrapSvg from '@ncigdc/utils/wrapSvg';

import getQueries from './getQueries';
import oncoGridParams from './oncoGridParams';

import './oncogrid.css';

function refreshGridState({
  oncoGrid,
  setHeatMapMode,
  setShowGridLines,
  setCrosshairMode,
}: {
  oncoGrid: Object,
  setHeatMapMode: Function,
  setShowGridLines: Function,
  setCrosshairMode: Function,
}): void {
  setHeatMapMode(oncoGrid.heatMapMode);
  setShowGridLines(oncoGrid.drawGridLines);
  setCrosshairMode(oncoGrid.crosshairMode);
}

const GRID_CLASS = 'oncogrid-wrapper';
const MAX_CASES = 50;
const MAX_GENES = 50;

const consequenceTypes = {
  missense_variant: '#ff9b6c',
  frameshift_variant: '#57dba4',
  start_lost: '#ff2323',
  stop_lost: '#d3ec00',
  initiator_codon_variant: '#5abaff',
  stop_gained: '#af57db',
};

const styles = {
  container: {
    overflow: 'visible',
    padding: '0 30px',
  },
  fullscreen: {
    maxWidth: '100%',
    width: '100%',
    marginLeft: 0,
    padding: '100px 100px 0',
    overflow: 'scroll',
    height: '100%',
  },
  button: {
    ...visualizingButton,
    marginBottom: 12,
  },
  buttonActive: {
    backgroundColor: '#e6e6e6',
    borderColor: '#adadad',
  },
};

const containerRefs = {};
const wrapperRefs = {};

type TProps = {
  oncoGrid: Object,
  setOncoGrid: Function,
  setOncoGridData: Function,
  oncoGridPadding: number,
  oncoGridHeight: number,
  setIsLoading: Function,
  projectId: string,
  setHeatMapMode: Function,
  setShowGridLines: Function,
  setCrosshairMode: Function,
  setTrackLegends: Function,
  currentFilters: Object,
  uniqueGridClass: string,
  dispatch: Function,
  push: Function,
  impacts: Array<string>,
};

const OncoGridWrapper = compose(
  withRouter,
  withState('oncoGrid', 'setOncoGrid', {}),
  withState('oncoGridData', 'setOncoGridData', null),
  withState('crosshairMode', 'setCrosshairMode', false),
  withState('showGridLines', 'setShowGridLines', true),
  withState('heatMapMode', 'setHeatMapMode', false),
  withState('isLoading', 'setIsLoading', true),
  withState('uniqueGridClass', 'setUniqueGridClass', () => GRID_CLASS + uniqueId()),
  withState('trackLegends', 'setTrackLegends', []),
  mapProps(({ title, impacts, ...props }) => {
    const cases = props.oncoGridData ? props.oncoGridData.cases.length : MAX_CASES;
    const genes = props.oncoGridData ? props.oncoGridData.genes.length : MAX_GENES;

    const currentImpacts = getFilterValue({
      currentFilters: props.currentFilters.content,
      dotField: 'ssms.consequence.transcript.annotation.impact',
    });

    return {
      ...props,
      title: title || `${cases} Most Mutated Cases and Top ${genes} Mutated Genes`,
      impacts: impacts || (currentImpacts && currentImpacts.content.value) || [],
    };
  }),
  withProps({
    oncoGridHeight: 150,
    oncoGridPadding: 306,
    oncoGridWrapper: null,
    async getQueries(
      {
        oncoGrid,
        setOncoGrid,
        setOncoGridData,
        oncoGridPadding,
        oncoGridHeight,
        setIsLoading,
        projectId,
        setHeatMapMode,
        setShowGridLines,
        setCrosshairMode,
        setTrackLegends,
        currentFilters,
        impacts,
        uniqueGridClass,
        dispatch,
        push,
      }: TProps = {},
      previousResponses: Object
    ): Promise<*> {
      const responses = await getQueries({
        consequenceTypes: Object.keys(consequenceTypes),
        currentFilters,
        maxCases: MAX_CASES,
        maxGenes: MAX_GENES,
      });
      if (!wrapperRefs[uniqueGridClass]) return;

      const gridParams = oncoGridParams({
        colorMap: consequenceTypes,
        element: wrapperRefs[uniqueGridClass],
        donorData: responses.cases,
        geneData: responses.genes,
        occurencesData: responses.occurences,
        width: (containerRefs[uniqueGridClass] || { offsetWidth: 0 }).offsetWidth - oncoGridPadding,
        height: oncoGridHeight,
        geneClick: ({ id }: { id: string}) => push(`/genes/${id}`),
        donorClick: ({ id }: { id: string }) => push(`/cases/${id}`),
        donorHistogramClick: (data: { id: string }) => {
          push({
            pathname: '/exploration',
            query: {
              filters: JSURL.stringify(
                makeFilter([
                  { field: 'cases.case_id', value: data.id },
                ], false)
              ),
            },
          });
        },
        gridClick: (data: { id: string }) => {
          push({
            pathname: '/exploration',
            query: {
              filters: JSURL.stringify(makeFilter([{ field: 'ssms.ssm_id', value: data.id }], false)),
              facetTab: 'mutations',
              searchTableTab: 'mutations',
            },
          });
        },
        geneHistogramClick: (data: { id: string }) => {
          push({
            pathname: '/exploration',
            query: {
              filters: JSURL.stringify(
                makeFilter([
                  { field: 'genes.gene_id', value: data.id },
                ], false)
              ),
              facetTab: 'genes',
              searchTableTab: 'genes',
            },
          });
        },
        trackPadding: 30,
        addTrackFunc: (options, callback) => {
          dispatch(setModal(
            <SelectModal
              options={options}
              onClose={
                (tracks = []) => {
                  dispatch(setModal(null));
                  if (tracks.length) callback(tracks);
                }
              }
            />
          ));
        },
        consequenceTypes: Object.keys(consequenceTypes),
        impacts,
      });

      if (gridParams && !previousResponses) {
        const grid = new OncoGrid(gridParams);
        grid.render();
        setOncoGrid(grid);
        setOncoGridData(responses);
        refreshGridState({ oncoGrid: grid, setHeatMapMode, setShowGridLines, setCrosshairMode });
      } else if (gridParams && previousResponses) {
        if (oncoGrid.toggleGridLines) oncoGrid.destroy();
        const grid = new OncoGrid(gridParams);
        grid.render();
        setOncoGrid(grid);
        setOncoGridData(responses);
        refreshGridState({ oncoGrid: grid, setHeatMapMode, setShowGridLines, setCrosshairMode });
      } else if (!gridParams) {
        if (oncoGrid.toggleGridLines) oncoGrid.destroy();
        setOncoGridData(null);
      }

      if (gridParams) {
        setTrackLegends(Object.values(gridParams.trackLegends));
      }

      setIsLoading(false);
    },
  }),
  connect(),
  withSize(),
  lifecycle({
    componentWillReceiveProps(nextProps: Object): void {
      const {
        crosshairMode: lastCrosshairMode,
        showGridLines: lastShowGridLines,
        heatMapMode: lastHeadMapMode,
        size: {
          width: lastWidth,
        },
      } = this.props;

      const {
        oncoGrid,
        oncoGridPadding,
        oncoGridHeight,
        crosshairMode,
        showGridLines,
        heatMapMode,
        size: {
          width,
        },
        uniqueGridClass,
      } = nextProps;

      if (oncoGrid.toggleGridLines) {
        if (lastCrosshairMode !== crosshairMode) oncoGrid.toggleCrosshair();
        if (lastShowGridLines !== showGridLines) oncoGrid.toggleGridLines();
        if (lastHeadMapMode !== heatMapMode) oncoGrid.toggleHeatmap();
        if (width !== lastWidth) {
          oncoGrid.resize(wrapperRefs[uniqueGridClass].offsetWidth - oncoGridPadding, oncoGridHeight);
        }
      }

      if (JSON.stringify(this.props.currentFilters) !== JSON.stringify(nextProps.currentFilters)) {
        this.props.setIsLoading(true);
        this.props.getQueries(nextProps, this.props.oncoGridData);
      }
    },
    componentDidMount(): void {
      this.props.getQueries(this.props);
    },
    componentWillUnmount(): void {
      const { uniqueGridClass } = this.props;
      delete containerRefs[uniqueGridClass]; // eslint-disable-line fp/no-delete
      delete wrapperRefs[uniqueGridClass]; // eslint-disable-line fp/no-delete
    },
  })
)(({
  oncoGrid,
  oncoGridData,
  heatMapMode,
  setHeatMapMode,
  showGridLines,
  setShowGridLines,
  crosshairMode,
  setCrosshairMode,
  isLoading,
  uniqueGridClass,
  trackLegends,
  title,
}) => (
  <Loader
    loading={isLoading}
    height="800px"
  >

 { containerRefs[uniqueGridClass] = r; }} // eslint-disable-line fp/no-mutation
    >

{title}

      {oncoGridData && !isLoading &&
        <Row style={{ marginLeft: 0, minHeight: '70px' }}>

            {heatMapMode ? <StepLegend rightLabel="More Mutations" /> : <SwatchLegend colorMap={consequenceTypes} />}

          <Row style={{ justifyContent: 'flex-end', marginRight: '12px', flexWrap: 'wrap' }} spacing="1rem">
            <DownloadVisualizationButton
              svg={() => {
                const elementsAfter = trackLegends.map((html) => {
                  const div = document.createElement('div');
                  div.innerHTML = html;
                  div.style.position = 'absolute';
                  div.style.left = '-99999px';
                  document.body.appendChild(div);
                  return div;
                });

                const wrappedSvg = wrapSvg({
                  selector: `.${uniqueGridClass} svg`,
                  title,
                  className: GRID_CLASS,
                  embed: {
                    top: {
                      elements: [
                        containerRefs[uniqueGridClass].querySelector('.oncogrid-legend'),
                      ],
                    },
                    bottom: {
                      elements: elementsAfter,
                      styles: 'display: inline-block; width: 200px; vertical-align: top; ',
                    },
                  },
                });

                elementsAfter.forEach(el => document.body.removeChild(el));

                return wrappedSvg;
              }}
              data={oncoGridData}
              stylePrefix={`.${GRID_CLASS}`}
              slug="oncogrid"
              noText
              tooltipHTML="Download"
            />
            <Tooltip Component="Reload Grid">
              <Button
                style={styles.button}
                onClick={() => {
                  oncoGrid.reload();
                  refreshGridState({ oncoGrid, setHeatMapMode, setShowGridLines, setCrosshairMode });
                }}
              ><Hidden>Reload</Hidden></Button>
            </Tooltip>
            <Tooltip Component="Cluster Data">
              <Button
                style={styles.button}
                onClick={() => oncoGrid.cluster()}
              ><Hidden>Cluster</Hidden></Button>
            </Tooltip>
            <Tooltip Component="Toggle Heatmap View">
              <Button
                style={{ ...styles.button, ...(heatMapMode && styles.buttonActive) }}
                onClick={() => setHeatMapMode(!heatMapMode)}
              ><Hidden>Heatmap</Hidden></Button>
            </Tooltip>
            <Tooltip Component="Toggle Gridlines">
              <Button
                style={{ ...styles.button, ...(showGridLines && styles.buttonActive) }}
                onClick={() => setShowGridLines(!showGridLines)}
              ><Hidden>Lines</Hidden></Button>
            </Tooltip>
            <Tooltip Component="Toggle Crosshairs">
              <Button
                style={{ ...styles.button, ...(crosshairMode && styles.buttonActive) }}
                onClick={() => setCrosshairMode(!crosshairMode)}
              ><Hidden>Crosshair</Hidden></Button>
            </Tooltip>
            <Tooltip Component="Fullscreen">
              <Button
                style={{
                  ...styles.button,
                  ...(isFullScreen() && styles.buttonActive),
                  marginRight: 0,
                }}
                onClick={() => {
                  if (isFullScreen()) {
                    exitFullScreen();
                    oncoGrid.reload();
                    refreshGridState({ oncoGrid, setHeatMapMode, setShowGridLines, setCrosshairMode });
                  } else {
                    enterFullScreen(containerRefs[uniqueGridClass]);
                    oncoGrid.resize(screen.width - 400, screen.height - 400, true);
                  }
                }}
              >
                <FullScreenIcon />
                <Hidden>Fullscreen</Hidden>
              </Button>
            </Tooltip>

            {crosshairMode &&

                Click and drag to select a region on the OncoGrid to zoom in.

            }
          </Row>
        </Row>
      }
      {!oncoGridData && !isLoading &&
        <Column style={{ alignItems: 'center', padding: '2rem 0' }}>

No result found.

        </Column>
      }

      <div
        className={`${GRID_CLASS} ${uniqueGridClass}`}
        ref={n => { wrapperRefs[uniqueGridClass] = n; }} // eslint-disable-line fp/no-mutation
        style={{
          cursor: crosshairMode ? 'crosshair' : 'pointer',
          visibility: isLoading ? 'hidden' : 'visible',
        }}
      />

  </Loader>
));

export default OncoGridWrapper;
<commit_msg>update oncogrid case count (#1615)<commit_after>/* @flow */
import React from 'react';
import { lifecycle, compose, withState, withProps, mapProps } from 'recompose';
import OncoGrid from 'oncogrid';
import { uniqueId } from 'lodash';
import { connect } from 'react-redux';
import withSize from '@ncigdc/utils/withSize';
import FullScreenIcon from 'react-icons/lib/md/fullscreen';
import JSURL from 'jsurl';

import { exitFullScreen, enterFullScreen, isFullScreen } from '@ncigdc/utils/fullscreen';
import { getFilterValue, makeFilter } from '@ncigdc/utils/filters';
import withRouter from '@ncigdc/utils/withRouter';

import Loader from '@ncigdc/uikit/Loaders/Loader';
import Button from '@ncigdc/uikit/Button';
import { Row, Column } from '@ncigdc/uikit/Flex';
import { Tooltip } from '@ncigdc/uikit/Tooltip';

import { StepLegend, SwatchLegend } from '@ncigdc/components/Legends';
import SelectModal from '@ncigdc/components/Modals/SelectModal';
import DownloadVisualizationButton from '@ncigdc/components/DownloadVisualizationButton';
import Hidden from '@ncigdc/components/Hidden';

import { visualizingButton } from '@ncigdc/theme/mixins';
import { setModal } from '@ncigdc/dux/modal';
import wrapSvg from '@ncigdc/utils/wrapSvg';

import getQueries from './getQueries';
import oncoGridParams from './oncoGridParams';

import './oncogrid.css';

function refreshGridState({
  oncoGrid,
  setHeatMapMode,
  setShowGridLines,
  setCrosshairMode,
}: {
  oncoGrid: Object,
  setHeatMapMode: Function,
  setShowGridLines: Function,
  setCrosshairMode: Function,
}): void {
  setHeatMapMode(oncoGrid.heatMapMode);
  setShowGridLines(oncoGrid.drawGridLines);
  setCrosshairMode(oncoGrid.crosshairMode);
}

const GRID_CLASS = 'oncogrid-wrapper';
const MAX_CASES = 2000;
const MAX_GENES = 50;

const consequenceTypes = {
  missense_variant: '#ff9b6c',
  frameshift_variant: '#57dba4',
  start_lost: '#ff2323',
  stop_lost: '#d3ec00',
  initiator_codon_variant: '#5abaff',
  stop_gained: '#af57db',
};

const styles = {
  container: {
    overflow: 'visible',
    padding: '0 30px',
  },
  fullscreen: {
    maxWidth: '100%',
    width: '100%',
    marginLeft: 0,
    padding: '100px 100px 0',
    overflow: 'scroll',
    height: '100%',
  },
  button: {
    ...visualizingButton,
    marginBottom: 12,
  },
  buttonActive: {
    backgroundColor: '#e6e6e6',
    borderColor: '#adadad',
  },
};

const containerRefs = {};
const wrapperRefs = {};

type TProps = {
  oncoGrid: Object,
  setOncoGrid: Function,
  setOncoGridData: Function,
  oncoGridPadding: number,
  oncoGridHeight: number,
  setIsLoading: Function,
  projectId: string,
  setHeatMapMode: Function,
  setShowGridLines: Function,
  setCrosshairMode: Function,
  setTrackLegends: Function,
  currentFilters: Object,
  uniqueGridClass: string,
  dispatch: Function,
  push: Function,
  impacts: Array<string>,
};

const OncoGridWrapper = compose(
  withRouter,
  withState('oncoGrid', 'setOncoGrid', {}),
  withState('oncoGridData', 'setOncoGridData', null),
  withState('crosshairMode', 'setCrosshairMode', false),
  withState('showGridLines', 'setShowGridLines', true),
  withState('heatMapMode', 'setHeatMapMode', false),
  withState('isLoading', 'setIsLoading', true),
  withState('uniqueGridClass', 'setUniqueGridClass', () => GRID_CLASS + uniqueId()),
  withState('trackLegends', 'setTrackLegends', []),
  mapProps(({ title, impacts, ...props }) => {
    const cases = props.oncoGridData ? props.oncoGridData.cases.length : MAX_CASES;
    const genes = props.oncoGridData ? props.oncoGridData.genes.length : MAX_GENES;

    const currentImpacts = getFilterValue({
      currentFilters: props.currentFilters.content,
      dotField: 'ssms.consequence.transcript.annotation.impact',
    });

    return {
      ...props,
      title: title || `${cases} Most Mutated Cases and Top ${genes} Mutated Genes`,
      impacts: impacts || (currentImpacts && currentImpacts.content.value) || [],
    };
  }),
  withProps({
    oncoGridHeight: 150,
    oncoGridPadding: 306,
    oncoGridWrapper: null,
    async getQueries(
      {
        oncoGrid,
        setOncoGrid,
        setOncoGridData,
        oncoGridPadding,
        oncoGridHeight,
        setIsLoading,
        projectId,
        setHeatMapMode,
        setShowGridLines,
        setCrosshairMode,
        setTrackLegends,
        currentFilters,
        impacts,
        uniqueGridClass,
        dispatch,
        push,
      }: TProps = {},
      previousResponses: Object
    ): Promise<*> {
      const responses = await getQueries({
        consequenceTypes: Object.keys(consequenceTypes),
        currentFilters,
        maxCases: MAX_CASES,
        maxGenes: MAX_GENES,
      });
      if (!wrapperRefs[uniqueGridClass]) return;

      const gridParams = oncoGridParams({
        colorMap: consequenceTypes,
        element: wrapperRefs[uniqueGridClass],
        donorData: responses.cases,
        geneData: responses.genes,
        occurencesData: responses.occurences,
        width: (containerRefs[uniqueGridClass] || { offsetWidth: 0 }).offsetWidth - oncoGridPadding,
        height: oncoGridHeight,
        geneClick: ({ id }: { id: string}) => push(`/genes/${id}`),
        donorClick: ({ id }: { id: string }) => push(`/cases/${id}`),
        donorHistogramClick: (data: { id: string }) => {
          push({
            pathname: '/exploration',
            query: {
              filters: JSURL.stringify(
                makeFilter([
                  { field: 'cases.case_id', value: data.id },
                ], false)
              ),
            },
          });
        },
        gridClick: (data: { id: string }) => {
          push({
            pathname: '/exploration',
            query: {
              filters: JSURL.stringify(makeFilter([{ field: 'ssms.ssm_id', value: data.id }], false)),
              facetTab: 'mutations',
              searchTableTab: 'mutations',
            },
          });
        },
        geneHistogramClick: (data: { id: string }) => {
          push({
            pathname: '/exploration',
            query: {
              filters: JSURL.stringify(
                makeFilter([
                  { field: 'genes.gene_id', value: data.id },
                ], false)
              ),
              facetTab: 'genes',
              searchTableTab: 'genes',
            },
          });
        },
        trackPadding: 30,
        addTrackFunc: (options, callback) => {
          dispatch(setModal(
            <SelectModal
              options={options}
              onClose={
                (tracks = []) => {
                  dispatch(setModal(null));
                  if (tracks.length) callback(tracks);
                }
              }
            />
          ));
        },
        consequenceTypes: Object.keys(consequenceTypes),
        impacts,
      });

      if (gridParams && !previousResponses) {
        const grid = new OncoGrid(gridParams);
        grid.render();
        setOncoGrid(grid);
        setOncoGridData(responses);
        refreshGridState({ oncoGrid: grid, setHeatMapMode, setShowGridLines, setCrosshairMode });
      } else if (gridParams && previousResponses) {
        if (oncoGrid.toggleGridLines) oncoGrid.destroy();
        const grid = new OncoGrid(gridParams);
        grid.render();
        setOncoGrid(grid);
        setOncoGridData(responses);
        refreshGridState({ oncoGrid: grid, setHeatMapMode, setShowGridLines, setCrosshairMode });
      } else if (!gridParams) {
        if (oncoGrid.toggleGridLines) oncoGrid.destroy();
        setOncoGridData(null);
      }

      if (gridParams) {
        setTrackLegends(Object.values(gridParams.trackLegends));
      }

      setIsLoading(false);
    },
  }),
  connect(),
  withSize(),
  lifecycle({
    componentWillReceiveProps(nextProps: Object): void {
      const {
        crosshairMode: lastCrosshairMode,
        showGridLines: lastShowGridLines,
        heatMapMode: lastHeadMapMode,
        size: {
          width: lastWidth,
        },
      } = this.props;

      const {
        oncoGrid,
        oncoGridPadding,
        oncoGridHeight,
        crosshairMode,
        showGridLines,
        heatMapMode,
        size: {
          width,
        },
        uniqueGridClass,
      } = nextProps;

      if (oncoGrid.toggleGridLines) {
        if (lastCrosshairMode !== crosshairMode) oncoGrid.toggleCrosshair();
        if (lastShowGridLines !== showGridLines) oncoGrid.toggleGridLines();
        if (lastHeadMapMode !== heatMapMode) oncoGrid.toggleHeatmap();
        if (width !== lastWidth) {
          oncoGrid.resize(wrapperRefs[uniqueGridClass].offsetWidth - oncoGridPadding, oncoGridHeight);
        }
      }

      if (JSON.stringify(this.props.currentFilters) !== JSON.stringify(nextProps.currentFilters)) {
        this.props.setIsLoading(true);
        this.props.getQueries(nextProps, this.props.oncoGridData);
      }
    },
    componentDidMount(): void {
      this.props.getQueries(this.props);
    },
    componentWillUnmount(): void {
      const { uniqueGridClass } = this.props;
      delete containerRefs[uniqueGridClass]; // eslint-disable-line fp/no-delete
      delete wrapperRefs[uniqueGridClass]; // eslint-disable-line fp/no-delete
    },
  })
)(({
  oncoGrid,
  oncoGridData,
  heatMapMode,
  setHeatMapMode,
  showGridLines,
  setShowGridLines,
  crosshairMode,
  setCrosshairMode,
  isLoading,
  uniqueGridClass,
  trackLegends,
  title,
}) => (
  <Loader
    loading={isLoading}
    height="800px"
  >

 { containerRefs[uniqueGridClass] = r; }} // eslint-disable-line fp/no-mutation
    >

{title}

      {oncoGridData && !isLoading &&
        <Row style={{ marginLeft: 0, minHeight: '70px' }}>

            {heatMapMode ? <StepLegend rightLabel="More Mutations" /> : <SwatchLegend colorMap={consequenceTypes} />}

          <Row style={{ justifyContent: 'flex-end', marginRight: '12px', flexWrap: 'wrap' }} spacing="1rem">
            <DownloadVisualizationButton
              svg={() => {
                const elementsAfter = trackLegends.map((html) => {
                  const div = document.createElement('div');
                  div.innerHTML = html;
                  div.style.position = 'absolute';
                  div.style.left = '-99999px';
                  document.body.appendChild(div);
                  return div;
                });

                const wrappedSvg = wrapSvg({
                  selector: `.${uniqueGridClass} svg`,
                  title,
                  className: GRID_CLASS,
                  embed: {
                    top: {
                      elements: [
                        containerRefs[uniqueGridClass].querySelector('.oncogrid-legend'),
                      ],
                    },
                    bottom: {
                      elements: elementsAfter,
                      styles: 'display: inline-block; width: 200px; vertical-align: top; ',
                    },
                  },
                });

                elementsAfter.forEach(el => document.body.removeChild(el));

                return wrappedSvg;
              }}
              data={oncoGridData}
              stylePrefix={`.${GRID_CLASS}`}
              slug="oncogrid"
              noText
              tooltipHTML="Download"
            />
            <Tooltip Component="Reload Grid">
              <Button
                style={styles.button}
                onClick={() => {
                  oncoGrid.reload();
                  refreshGridState({ oncoGrid, setHeatMapMode, setShowGridLines, setCrosshairMode });
                }}
              ><Hidden>Reload</Hidden></Button>
            </Tooltip>
            <Tooltip Component="Cluster Data">
              <Button
                style={styles.button}
                onClick={() => oncoGrid.cluster()}
              ><Hidden>Cluster</Hidden></Button>
            </Tooltip>
            <Tooltip Component="Toggle Heatmap View">
              <Button
                style={{ ...styles.button, ...(heatMapMode && styles.buttonActive) }}
                onClick={() => setHeatMapMode(!heatMapMode)}
              ><Hidden>Heatmap</Hidden></Button>
            </Tooltip>
            <Tooltip Component="Toggle Gridlines">
              <Button
                style={{ ...styles.button, ...(showGridLines && styles.buttonActive) }}
                onClick={() => setShowGridLines(!showGridLines)}
              ><Hidden>Lines</Hidden></Button>
            </Tooltip>
            <Tooltip Component="Toggle Crosshairs">
              <Button
                style={{ ...styles.button, ...(crosshairMode && styles.buttonActive) }}
                onClick={() => setCrosshairMode(!crosshairMode)}
              ><Hidden>Crosshair</Hidden></Button>
            </Tooltip>
            <Tooltip Component="Fullscreen">
              <Button
                style={{
                  ...styles.button,
                  ...(isFullScreen() && styles.buttonActive),
                  marginRight: 0,
                }}
                onClick={() => {
                  if (isFullScreen()) {
                    exitFullScreen();
                    oncoGrid.reload();
                    refreshGridState({ oncoGrid, setHeatMapMode, setShowGridLines, setCrosshairMode });
                  } else {
                    enterFullScreen(containerRefs[uniqueGridClass]);
                    oncoGrid.resize(screen.width - 400, screen.height - 400, true);
                  }
                }}
              >
                <FullScreenIcon />
                <Hidden>Fullscreen</Hidden>
              </Button>
            </Tooltip>

            {crosshairMode &&

                Click and drag to select a region on the OncoGrid to zoom in.

            }
          </Row>
        </Row>
      }
      {!oncoGridData && !isLoading &&
        <Column style={{ alignItems: 'center', padding: '2rem 0' }}>

No result found.

        </Column>
      }

      <div
        className={`${GRID_CLASS} ${uniqueGridClass}`}
        ref={n => { wrapperRefs[uniqueGridClass] = n; }} // eslint-disable-line fp/no-mutation
        style={{
          cursor: crosshairMode ? 'crosshair' : 'pointer',
          visibility: isLoading ? 'hidden' : 'visible',
        }}
      />

  </Loader>
));

export default OncoGridWrapper;
<|endoftext|>


<commit_before>(function () {
  'use strict';

  // Suborders controller
  angular
    .module('suborders')
    .controller('SubordersController', SubordersController);

  SubordersController.$inject = ['$scope', '$state', 'Authentication', 'suborderResolve', 'IngredientsService'];

  function SubordersController ($scope, $state, Authentication, suborder, IngredientsService) {
    var vm = this;

    vm.authentication = Authentication;
    vm.suborder = suborder;
    vm.ingredients = IngredientsService.query();
    vm.error = null;
    vm.form = {};
    vm.radio = [];
    vm.added = [];
    vm.letters = ['A','B','C','D','E','F'];
    vm.remove = remove;
    vm.save = save;
    vm.addTopping = addTopping;
    vm.updateTopping = updateTopping;
    vm.removeTopping = removeTopping;
    vm.validateIngr = validateIngr;
    vm.generatePass = generatePass;
    vm.toFeedback = toFeedback;

    if(!vm.suborder._id){
      vm.suborder.toppings = [];
      vm.suborder.cost = 0;
    }

    // Remove existing Suborder
    function remove() {
      if (confirm('Are you sure you want to delete?')) {
        vm.suborder.$remove($state.go('suborders.list'));
      }
    }

    // Save Suborder
    function save(isValid) {
      if (!isValid) {
        $scope.$broadcast('show-errors-check-validity', 'vm.form.suborderForm');
        return false;
      }

      // TODO: move create/update logic to service
      if (vm.suborder._id) {
        vm.suborder.$update(successCallback, errorCallback);
      } else {

        for(var i = 0; i < vm.suborder.toppings.length; i++){
          var topid = vm.suborder.toppings[i].ingredient;
          for(var j = 0; j < vm.ingredients.length; j++){
            var ingred = vm.ingredients[j];
            if(ingred._id === topid){
              ingred.statistic = ingred.statistic + 1;
              ingred.$update();
            }
          }
        }

        vm.suborder.orderpass = generatePass();

        vm.suborder.$save(successCallback, errorCallback);
      }

      function successCallback(res) {
        $state.go('suborders.view', {
          suborderId: res._id
        });
      }

      function errorCallback(res) {
        vm.error = res.data.message;
      }
    }

    function addTopping(topping, index) {

      if(vm.radio[index] === 'mucho'){
        vm.suborder.cost = vm.suborder.cost + (topping.cost * 3);
      }
      else if(vm.radio[index] === 'mediano'){
        vm.suborder.cost = vm.suborder.cost + (topping.cost * 2);
      }
      else{
        vm.suborder.cost = vm.suborder.cost + topping.cost;
      }

      // agrega el objeto de prueba
      vm.suborder.toppings.push({ ingredient: topping._id, quantity: vm.radio[index] });
      //fue agregado
      vm.added[index] = true;
    }

    function updateTopping(topping, index){

      if(!vm.suborder._id) {
        vm.suborder.toppings.some = function(topping, length, array, nradio, cost) {
          for(var i = 0; i < length; i++){
            if(array[i].ingredient === topping._id){

              var pradio = array[i].quantity;

              var toppingCost = topping.cost;

              if(pradio === 'mediano'){
                toppingCost = toppingCost*2;
              }
              else if(pradio === 'mucho'){
                toppingCost = toppingCost*3;
              }

              array[i].quantity = nradio;

              cost = cost - toppingCost;

              if(nradio === 'poco'){
                toppingCost = topping.cost;
              }
              else if(nradio === 'mediano'){
                toppingCost = topping.cost*2;
              }
              else if(nradio === 'mucho'){
                toppingCost = topping.cost*3;
              }

              cost = cost + toppingCost;

              return cost;
            }
          }
          return cost;
        };
      }
      else{
        vm.suborder.toppings.some = function(topping, length, array, nradio, cost) {
          for(var i = 0; i < length; i++){
            if(array[i].ingredient._id === topping._id){
              var pradio = array[i].quantity;
              var toppingCost = topping.cost;

              if(pradio === 'mediano'){
                toppingCost = toppingCost*2;
              }
              else if(pradio === 'mucho'){
                toppingCost = toppingCost*3;
              }

              array[i].quantity = nradio;

              cost = cost - toppingCost;

              if(nradio === 'poco'){
                toppingCost = topping.cost;
              }
              else if(nradio === 'mediano'){
                toppingCost = topping.cost*2;
              }
              else if(nradio === 'mucho'){
                toppingCost = topping.cost*3;
              }

              cost = cost + toppingCost;

              return cost;
            }
          }
          return cost;
        };
      }

      vm.suborder.cost = vm.suborder.toppings.some(topping, vm.suborder.toppings.length,
        vm.suborder.toppings, vm.radio[index], vm.suborder.cost);
    }

    function removeTopping(topping, index) {
      // se le agrega fucion
      if(!vm.suborder._id){
        vm.suborder.toppings.some = function(topping, length, array) {
          for(var i = 0; i < length; i++){
            if(array[i].ingredient === topping._id){
              var radio = array[i].quantity;
              var cost = topping.cost;
              if(radio === 'mediano'){
                cost = cost*2;
              }
              else if(radio === 'mucho'){
                cost = cost*3;
              }
              array.splice(i,1);
              return cost;
            }
          }
          return 0;
        };
      }
      else{
        vm.suborder.toppings.some = function(topping, length, array) {
          for(var i = 0; i < length; i++){
            if(array[i].ingredient._id === topping._id){
              var radio = array[i].quantity;
              var cost = topping.cost;
              if(radio === 'mediano'){
                cost = cost*2;
              }
              else if(radio === 'mucho'){
                cost = cost*3;
              }
              array.splice(i,1);
              return cost;
            }
          }
          return 0;
        };
      }

      vm.suborder.cost -= vm.suborder.toppings.some(topping, vm.suborder.toppings.length, vm.suborder.toppings);

      vm.added[index] = false;
    }

    function validateIngr(id, index) {
      //crear
      if(!vm.suborder._id){

        vm.radio[index] = 'poco';

        vm.added[index] = false;
      }
      //editar
      else{
        //se agrega función
        vm.suborder.toppings.some = function (idTop, length, array) {
          for(var i = 0; i < length; i++){
            if(array[i].ingredient._id === idTop){
              return i;
            }
          }
          return -1;
        };

          //si esta en el arreglo
        var toppingIndex = vm.suborder.toppings.some(id, vm.suborder.toppings.length, vm.suborder.toppings);

        if(toppingIndex !== -1){
          vm.added[index] = true;

          vm.radio[index] = vm.suborder.toppings[toppingIndex].quantity;
        }
        else{
          vm.added[index] = false;

          vm.radio[index] = 'poco';
        }
      }
    }

    function generatePass(){
      var intPart = Math.round( Math.random() * 10000 );

      var strPart = '';

      for(var i = 0; i < 4; i++){
        var index = Math.round( Math.random() * 10 ) % vm.letters.length;
        strPart = strPart.concat(vm.letters[index]);
      }

      strPart = strPart.concat(intPart.toString());

      return strPart;
    }

    function toFeedback(){
      vm.suborder.state = 'en proceso';
      vm.suborder.$update();

      $state.go('suborders.feedback', {
        suborderId: vm.suborder._id
      });
    }

  }
})();
<commit_msg>soluciones en el eslint del controlador<commit_after>(function () {
  'use strict';

  // Suborders controller
  angular
    .module('suborders')
    .controller('SubordersController', SubordersController);

  SubordersController.$inject = ['$scope', '$state', 'Authentication', 'suborderResolve', 'IngredientsService'];

  function SubordersController ($scope, $state, Authentication, suborder, IngredientsService) {
    var vm = this;

    vm.authentication = Authentication;
    vm.suborder = suborder;
    vm.ingredients = IngredientsService.query();
    vm.error = null;
    vm.form = {};
    vm.radio = [];
    vm.added = [];
    vm.letters = ['A','B','C','D','E','F'];
    vm.remove = remove;
    vm.save = save;
    vm.addTopping = addTopping;
    vm.updateTopping = updateTopping;
    vm.removeTopping = removeTopping;
    vm.validateIngr = validateIngr;
    vm.generatePass = generatePass;
    vm.toFeedback = toFeedback;

    if(!vm.suborder._id){
      vm.suborder.toppings = [];
      vm.suborder.cost = 0;
    }

    // Remove existing Suborder
    function remove() {
      if (confirm('Are you sure you want to delete?')) {
        vm.suborder.$remove($state.go('suborders.list'));
      }
    }

    // Save Suborder
    function save(isValid) {
      if (!isValid) {
        $scope.$broadcast('show-errors-check-validity', 'vm.form.suborderForm');
        return false;
      }

      // TODO: move create/update logic to service
      if (vm.suborder._id) {
        vm.suborder.$update(successCallback, errorCallback);
      } else {

        for(var i = 0; i < vm.suborder.toppings.length; i++){
          var topid = vm.suborder.toppings[i].ingredient;
          for(var j = 0; j < vm.ingredients.length; j++){
            var ingred = vm.ingredients[j];
            if(ingred._id === topid){
              ingred.statistic = ingred.statistic + 1;
              ingred.$update();
            }
          }
        }

        vm.suborder.orderpass = generatePass();

        vm.suborder.$save(successCallback, errorCallback);
      }

      function successCallback(res) {
        $state.go('suborders.view', {
          suborderId: res._id
        });
      }

      function errorCallback(res) {
        vm.error = res.data.message;
      }
    }

    function addTopping(topping, index) {

      if(vm.radio[index] === 'mucho'){
        vm.suborder.cost = vm.suborder.cost + (topping.cost * 3);
      }
      else if(vm.radio[index] === 'mediano'){
        vm.suborder.cost = vm.suborder.cost + (topping.cost * 2);
      }
      else{
        vm.suborder.cost = vm.suborder.cost + topping.cost;
      }

      // agrega el objeto de prueba
      vm.suborder.toppings.push({ ingredient: topping._id, quantity: vm.radio[index] });
      //fue agregado
      vm.added[index] = true;
    }

    function updateTopping(topping, index){

      if(!vm.suborder._id) {
        vm.suborder.toppings.some = function(topping, length, array, nradio, cost) {
          for(var i = 0; i < length; i++){
            if(array[i].ingredient === topping._id){

              var pradio = array[i].quantity;

              var toppingCost = topping.cost;

              if(pradio === 'mediano'){
                toppingCost = toppingCost*2;
              }
              else if(pradio === 'mucho'){
                toppingCost = toppingCost*3;
              }

              array[i].quantity = nradio;

              cost = cost - toppingCost;

              if(nradio === 'poco'){
                toppingCost = topping.cost;
              }
              else if(nradio === 'mediano'){
                toppingCost = topping.cost*2;
              }
              else if(nradio === 'mucho'){
                toppingCost = topping.cost*3;
              }

              cost = cost + toppingCost;

              return cost;
            }
          }
          return cost;
        };
      }
      else{
        vm.suborder.toppings.some = function(topping, length, array, nradio, cost) {
          for(var i = 0; i < length; i++){
            if(array[i].ingredient._id === topping._id){
              var pradio = array[i].quantity;
              var toppingCost = topping.cost;

              if(pradio === 'mediano'){
                toppingCost = toppingCost*2;
              }
              else if(pradio === 'mucho'){
                toppingCost = toppingCost*3;
              }

              array[i].quantity = nradio;

              cost = cost - toppingCost;

              if(nradio === 'poco'){
                toppingCost = topping.cost;
              }
              else if(nradio === 'mediano'){
                toppingCost = topping.cost*2;
              }
              else if(nradio === 'mucho'){
                toppingCost = topping.cost*3;
              }

              cost = cost + toppingCost;

              return cost;
            }
          }
          return cost;
        };
      }

      vm.suborder.cost = vm.suborder.toppings.some(topping, vm.suborder.toppings.length,
        vm.suborder.toppings, vm.radio[index], vm.suborder.cost);
    }

    function removeTopping(topping, index) {
      // se le agrega fucion
      if(!vm.suborder._id){
        vm.suborder.toppings.some = function(topping, length, array) {
          for(var i = 0; i < length; i++){
            if(array[i].ingredient === topping._id){
              var radio = array[i].quantity;
              var cost = topping.cost;
              if(radio === 'mediano'){
                cost = cost*2;
              }
              else if(radio === 'mucho'){
                cost = cost*3;
              }
              array.splice(i,1);
              return cost;
            }
          }
          return 0;
        };
      }
      else{
        vm.suborder.toppings.some = function(topping, length, array) {
          for(var i = 0; i < length; i++){
            if(array[i].ingredient._id === topping._id){
              var radio = array[i].quantity;
              var cost = topping.cost;
              if(radio === 'mediano'){
                cost = cost*2;
              }
              else if(radio === 'mucho'){
                cost = cost*3;
              }
              array.splice(i,1);
              return cost;
            }
          }
          return 0;
        };
      }

      vm.suborder.cost -= vm.suborder.toppings.some(topping, vm.suborder.toppings.length, vm.suborder.toppings);

      vm.added[index] = false;
    }

    function validateIngr(id, index) {
      //crear
      if(!vm.suborder._id){

        vm.radio[index] = 'poco';

        vm.added[index] = false;
      }
      //editar
      else{
        //se agrega función
        vm.suborder.toppings.some = function (idTop, length, array) {
          for(var i = 0; i < length; i++){
            if(array[i].ingredient._id === idTop){
              return i;
            }
          }
          return -1;
        };

          //si esta en el arreglo
        var toppingIndex = vm.suborder.toppings.some(id, vm.suborder.toppings.length, vm.suborder.toppings);

        if(toppingIndex !== -1){
          vm.added[index] = true;

          vm.radio[index] = vm.suborder.toppings[toppingIndex].quantity;
        }
        else{
          vm.added[index] = false;

          vm.radio[index] = 'poco';
        }
      }
    }

    function generatePass(){
      var intPart = Math.round(Math.random() * 10000);

      var strPart = '';

      for(var i = 0; i < 4; i++){
        var index = Math.round(Math.random() * 10) % vm.letters.length;
        strPart = strPart.concat(vm.letters[index]);
      }

      strPart = strPart.concat(intPart.toString());

      return strPart;
    }

    function toFeedback(){
      vm.suborder.state = 'en proceso';
      vm.suborder.$update();

      $state.go('suborders.feedback', {
        suborderId: vm.suborder._id
      });
    }

  }
})();
<|endoftext|>


<commit_before>/*
The MIT License (MIT)

Copyright (c) 2016 sebakrau

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

module.exports = function(RED) {
  var YamahaAPI = require("yamaha-nodejs");
  var xml2js = require('xml2js');
  var deferred = require('deferred');

	/* ---------------------------------------------------------------------------
	 * CONFIG node
	 * -------------------------------------------------------------------------*/
  function AvrYamahaNodeConfig(config) {
    RED.nodes.createNode(this, config);

		// Configuration options passed by Node Red
    this.address = config.address;
		this.name = config.name;
    this.debug = config.debug;

    // Config node state
    this.connected = false;
    this.connecting = false;
    this.closing = false;
    this.subscriptions = {};
    this.inputSocket = undefined;
    this.devDesc = undefined;
    this.yamaha = new YamahaAPI(this.address);

    // Define functions called by nodes
    var node = this;
    this.sendGetCommand = function(topic) {
      var def = deferred();

      // Build command string from topic
      var command = '<YAMAHA_AV cmd="GET">';
      var elements = topic.split('.');
      elements.forEach(function(element) { command += '<' + element + '>' });
      command += 'GetParam';
      elements.reverse().forEach(function(element) { command += '</' + element + '>' });
      command += '</YAMAHA_AV>';

      // Request the data using yamaha get
      if (node.debug) {
        node.log('sending GET command:' + command);
      }
      node.yamaha.SendXMLToReceiver(command).then(function(response) {
        xml2js.Parser({ explicitArray: false }).parseString(response, function (err, result) {
          if (err) {
            def.reject("Failed to parse the response with error: " + err);
            return;
          }

          // Remove reference node path from payload
          var payload = result['YAMAHA_AV'];
          elements.reverse().forEach(function(element) { payload = payload[element]; });

          def.resolve(payload);
        });
      })
      .catch(function(error){
        def.reject(error);
      });

      return def.promise;
    }

    this.sendPutCommand = function(topic, payload) {
      var def = deferred();

      // Build command string from topic
      var command = '<YAMAHA_AV cmd="PUT">';
      var elements = topic.split('.');
      elements.forEach(function(element) { command += '<' + element + '>' });
      command += payload.toString().trim();
      elements.reverse().forEach(function(element) { command += '</' + element + '>' });
      command += '</YAMAHA_AV>';

      // Write the data using yamaha put
      if (node.debug) {
        node.log('sending PUT command:' + command);
      }
      node.yamaha.SendXMLToReceiver(command).then(function(response){
        xml2js.Parser({ explicitArray: false }).parseString(response, function (err, result) {
          if (err) {
            def.reject('Failed to parse the response with error: ' + err);
            return;
          }

          // Traverse the response data and check for valid resonse.
          var payload = result['YAMAHA_AV'];
          elements.reverse().forEach(function(element) {
            if (!payload.hasOwnProperty(element)) {
              node.error('Received unexpected response data: ' + result);
            }
            payload = payload[element];
          });

          def.resolve(payload);
        });
      })
      .catch(function(error){
        def.reject('Failed to write data to AVR with error: ' + error);
      });

      return def.promise;
    }

    this.users = {};
    this.register = function(mqttNode){
      node.users[mqttNode.id] = mqttNode;
      if (Object.keys(node.users).length === 1) {
          node.connect();
      }
    };

    this.deregister = function(mqttNode,done){
      delete node.users[mqttNode.id];
      if (node.closing) {
        return done();
      }
      if (Object.keys(node.users).length === 0) {
        node.disconnect();
      }
      done();
    };

    this.disconnect = function(done) {
      node.log("STOPING event Listener!");
      if (node.inputSocket) {
        return node.inputSocket.close();
      }
    }

    this.connect = function () {
      if (!node.connected && !node.connecting) {
        node.connecting = true;

        // Try to read the UPNP device description
        var http = require('http');
        var req = http.get({
          host: node.address,
          port: 8080,
          path: '/MediaRenderer/desc.xml'
          },
          function(response) {
            var body = '';
            response.on('data', function(data) {
              body += data;
            });
            response.on('end', function() {

              // Parse device description
              var parseString = require('xml2js').parseString;
              parseString(body, function (err, result) {
                if (err) {
                  node.log("Failed to parse the Device Description with error: " + err);
                  return;
                }
                node.devDesc = {
                  modelName: result.root.device[0].modelName[0],
                  modelNumber: result.root.device[0].modelNumber[0],
                  serialNumber: result.root.device[0].serialNumber[0],
                  modelDescription: result.root.device[0].modelDescription[0],
                  manufacturer: result.root.device[0].manufacturer[0],
                  friendlyName: result.root.device[0].friendlyName[0],
                  presentationURL: result.root.device[0].presentationURL[0],
                  udn: result.root.device[0].UDN[0]
                };
              });

              // Update state of all nodes
              node.connecting = false;
              node.connected = true;
              for (var id in node.users) {
                if (node.users.hasOwnProperty(id)) {
                  node.users[id].status({fill:"green", shape:"dot", text:"connected"});
                }
              }

              // Start Multicast listener for event notification
              startEventListener();
            });
        });

        req.on('error', function(err) {
          node.log("Failed to get device description with error: " + err);
          if (node.connecting) {
            node.connecting = false;
          }
        });

      }
    };

    this.subscribe = function(callback, ref) {
      ref = ref || 0;
      var sub = {
        handler:function(topic, payload) {
          callback(topic, payload);
        },
        ref: ref
      };
      node.subscriptions[ref] = sub;
    };

    this.unsubscribe = function(ref) {
      ref = ref || 0;
      var sub = node.subscriptions[ref];
      if (sub) {
        delete node.subscriptions[ref];
      }
    };

    // Internal sub routines
    var startEventListener = function() {
      node.log("STARTING event listener");

      var net = require('net');
      var dgram = require('dgram');
      node.inputSocket = dgram.createSocket({type:'udp4', reuseAddr: true});
      node.inputSocket.on('message', function (msg, rinfo) {

        node.log("[" + rinfo.address + "] --> " + msg.toString());

        if (rinfo.address == node.address) {
          //node.log("[" + rinfo.address + "] --> " + msg.toString());

          // Split to header and body
          msg = msg.toString().split("\r\n\r\n");
          var header = msg[0];
          var body = msg[1];

          // Ignore UPNP search requests
          var method = header.split("\r\n").shift().split(' ').shift().trim();
          if (method == 'M-SEARCH'){
            return;
          }

          // Parse rest of header
          var arr = header.match(/[^\r\n]+/g);
          var headerInfo = {};
          for (var i = 1; i < arr.length; ++i){
            var tem = arr[i].split(/:(.+)?/);
            if (typeof(tem[1]) == 'string'){ tem[1] = tem[1].trim(); }
            headerInfo[tem[0].toLowerCase()] = tem[1];
          };
          //node.log("METHOD: " + method);
          node.log("BODY: " + body);
          //node.log("NTS: " + headerInfo['nts']);

          // NOTIFY messages tell us about the properties that changed
          if (method == "NOTIFY" && headerInfo['nts'] == "yamaha:propchange") {

            var parseString = require('xml2js').parseString;
            parseString(body, function (err, result) {
              if (err) {
                node.error("Failed to parse the event with error: " + err);
                return;
              }

              for (var i in result.YAMAHA_AV.Main_Zone[0].Property) {
                var prop = result.YAMAHA_AV.Main_Zone[0].Property[i];
                node.log('Property-Change: ' + prop);

                // Event Notification only notifies that there is a change in each item.
                // It is necessary to issue the GET command to obtain the content of the item with the change.
                switch (prop) {
                  case 'Volume':

                    break;
                  default:

                }

                for (var s in node.subscriptions) {
                  node.subscriptions[s].handler("NOTIFY", body);
                }

              }

            });

          }
        }
      });

      node.inputSocket.on('listening', function () {
        var address = node.inputSocket.address();
        node.log('UDP client listening on ' + address.address + ":" + address.port);
        node.inputSocket.setBroadcast(true)
        node.inputSocket.setMulticastTTL(128);
        node.inputSocket.addMembership('239.255.255.250', '192.168.0.101');
        node.inputSocket.addMembership('239.255.255.250', '127.0.0.1');

/*
          var message = new Buffer(
            "M-SEARCH * HTTP/1.1\r\n" +
            "HOST: 239.255.255.250:1900\r\n" +
            "MAN: \"ssdp:discover\"\r\n" +
            "ST: "+'ssdp:all'+"\r\n" + // Essential, used by the client to specify what they want to discover, eg 'ST:ge:fridge'
            "MX: 3\r\n" + // 1 second to respond (but they all respond immediately?)
            "\r\n");

          node.inputSocket.send(message, 0, message.length, 1900, "239.255.255.250");
*/
      });

      node.inputSocket.bind(1900);
    }

    // Define config node event listeners
    node.on("close", function(done){
      node.closing = true;
      node.disconnect();
      done();
    });
  }
  RED.nodes.registerType("avr-yamaha", AvrYamahaNodeConfig);

	/* ---------------------------------------------------------------------------
	 * INPUT node
	 * -------------------------------------------------------------------------*/
  function AvrYamahaNodeIn(config) {
    RED.nodes.createNode(this, config);

		// Save settings in local node
		this.device = config.device;
    this.deviceNode = RED.nodes.getNode(this.device);
		this.name = config.name;
		this.devdesc = config.devdesc;

    // Register at config node to receive new events
    var node = this;
    if (this.deviceNode) {
      this.status({fill:"red", shape:"ring", text:"disconnected"});
      if (this.deviceNode.connected) {
          this.status({fill:"green", shape:"dot", text:"connected"});
      }

      this.deviceNode.register(this);
      this.deviceNode.subscribe(function(topic, payload) {
        var msg = {topic:topic, payload:payload};
        if (node.devdesc) {
          msg.devDesc = node.deviceNode.devDesc;
        }
        node.send(msg);
      }, this.id);

      this.on('close', function(done) {
        if (node.deviceNode) {
          node.deviceNode.unsubscribe(node.id);
          node.deviceNode.deregister(node, done);
          node.yamaha = null;
        }
      });
    } else {
      this.error(RED._("avr-yamaha.errors.missing-config"));
    }
  }
  RED.nodes.registerType("AVR-Yamaha-in", AvrYamahaNodeIn);

	/* ---------------------------------------------------------------------------
	 * GET node
	 * -------------------------------------------------------------------------*/
  function AvrYamahaNodeGet(config) {
    RED.nodes.createNode(this, config);

		// Save settings in local node
    this.device = config.device;
    this.deviceNode = RED.nodes.getNode(this.device);
		this.name = config.name;
		this.topic = config.topic;

    var node = this;
    if (this.deviceNode) {

  		// Input handler, called on incoming flow
      this.on('input', function(msg) {

        // If no topic is given in the config, then we us the topic in the msg.
        var topic = (node.topic) ? node.topic : msg.topic;
        if (!topic) {
          node.error('No topic given. Specify either in the config or via msg.topic!');
          return;
        }

        // Get data from the device.
        node.deviceNode.sendGetCommand(topic).then(function(value) {
          msg.payload = value;
          node.send(msg);
        }).catch(function(error) {
          node.error("Failed to request data from AVR with error: " + error);
        });

      });

    } else {
      this.error(RED._("avr-yamaha.errors.missing-config"));
    }
  }
  RED.nodes.registerType("AVR-Yamaha-get", AvrYamahaNodeGet);

	/* ---------------------------------------------------------------------------
	 * PUT node
	 * -------------------------------------------------------------------------*/
	function AvrYamahaNodePut(config) {
		RED.nodes.createNode(this, config);

		// Save settings in local node
    this.device = config.device;
    this.deviceNode = RED.nodes.getNode(this.device);
		this.name = config.name;
    this.topic = config.topic;
    this.payload = config.payload;

    var node = this;
    if (this.deviceNode) {

  		// Input handler, called on incoming flow
  		this.on('input', function(msg) {

        // If no topic is given in the config, then we us the topic in the msg.
        var topic = (node.topic) ? node.topic : msg.topic;
        if (!topic) {
          node.error('No topic given. Specify either in the config or via msg.topic!');
          return;
        }

        // If no payload is given in the config, then we us the payload in the msg.
        var payload = (node.payload) ? node.payload : msg.payload;
        if (payload === null || payload === undefined) {
          node.error('invalid payload: ' + payload.toString());
          return;
        }

        // Put data to the device.
        node.deviceNode.sendPutCommand(topic, payload).then(function(value) {
          // Continue the flow when data has been put...
          node.send(msg);
        }).catch(function(error) {
          node.error("Failed to put data to AVR with error: " + error);
        });

  		});

    } else {
      this.error(RED._("avr-yamaha.errors.missing-config"));
    }
	}
	RED.nodes.registerType("AVR-Yamaha-put", AvrYamahaNodePut);

  /* ---------------------------------------------------------------------------
	 * Backend informations
	 * -------------------------------------------------------------------------*/
  require('./references.js').provideReferences(RED);

};
<commit_msg>input node emits data on volume change, power mode and input change<commit_after>/*
The MIT License (MIT)

Copyright (c) 2016 sebakrau

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

module.exports = function(RED) {
  var YamahaAPI = require("yamaha-nodejs");
  var xml2js = require('xml2js');
  var deferred = require('deferred');

	/* ---------------------------------------------------------------------------
	 * CONFIG node
	 * -------------------------------------------------------------------------*/
  function AvrYamahaNodeConfig(config) {
    RED.nodes.createNode(this, config);

		// Configuration options passed by Node Red
    this.address = config.address;
		this.name = config.name;
    this.debug = config.debug;

    // Config node state
    this.connected = false;
    this.connecting = false;
    this.closing = false;
    this.subscriptions = {};
    this.inputSocket = undefined;
    this.devDesc = undefined;
    this.yamaha = new YamahaAPI(this.address);

    // Define functions called by nodes
    var node = this;
    this.sendGetCommand = function(topic) {
      var def = deferred();

      // Build command string from topic
      var command = '<YAMAHA_AV cmd="GET">';
      var elements = topic.split('.');
      elements.forEach(function(element) { command += '<' + element + '>' });
      command += 'GetParam';
      elements.reverse().forEach(function(element) { command += '</' + element + '>' });
      command += '</YAMAHA_AV>';

      // Request the data using yamaha get
      if (node.debug) {
        node.log('sending GET command:' + command);
      }
      node.yamaha.SendXMLToReceiver(command).then(function(response) {
        xml2js.Parser({ explicitArray: false }).parseString(response, function (err, result) {
          if (err) {
            def.reject("Failed to parse the response with error: " + err);
            return;
          }

          // Remove reference node path from payload
          var payload = result['YAMAHA_AV'];
          elements.reverse().forEach(function(element) { payload = payload[element]; });

          def.resolve(payload);
        });
      })
      .catch(function(error){
        def.reject(error);
      });

      return def.promise;
    }

    this.sendPutCommand = function(topic, payload) {
      var def = deferred();

      // Build command string from topic
      var command = '<YAMAHA_AV cmd="PUT">';
      var elements = topic.split('.');
      elements.forEach(function(element) { command += '<' + element + '>' });
      command += payload.toString().trim();
      elements.reverse().forEach(function(element) { command += '</' + element + '>' });
      command += '</YAMAHA_AV>';

      // Write the data using yamaha put
      if (node.debug) {
        node.log('sending PUT command:' + command);
      }
      node.yamaha.SendXMLToReceiver(command).then(function(response){
        xml2js.Parser({ explicitArray: false }).parseString(response, function (err, result) {
          if (err) {
            def.reject('Failed to parse the response with error: ' + err);
            return;
          }

          // Traverse the response data and check for valid resonse.
          var payload = result['YAMAHA_AV'];
          elements.reverse().forEach(function(element) {
            if (!payload.hasOwnProperty(element)) {
              node.error('Received unexpected response data: ' + result);
            }
            payload = payload[element];
          });

          def.resolve(payload);
        });
      })
      .catch(function(error){
        def.reject('Failed to write data to AVR with error: ' + error);
      });

      return def.promise;
    }

    this.users = {};
    this.register = function(mqttNode){
      node.users[mqttNode.id] = mqttNode;
      if (Object.keys(node.users).length === 1) {
          node.connect();
      }
    };

    this.deregister = function(mqttNode,done){
      delete node.users[mqttNode.id];
      if (node.closing) {
        return done();
      }
      if (Object.keys(node.users).length === 0) {
        node.disconnect();
      }
      done();
    };

    this.disconnect = function(done) {
      node.log("STOPING event Listener!");
      if (node.inputSocket) {
        return node.inputSocket.close();
      }
    }

    this.connect = function () {
      if (!node.connected && !node.connecting) {
        node.connecting = true;

        // Try to read the UPNP device description
        var http = require('http');
        var req = http.get({
          host: node.address,
          port: 8080,
          path: '/MediaRenderer/desc.xml'
          },
          function(response) {
            var body = '';
            response.on('data', function(data) {
              body += data;
            });
            response.on('end', function() {

              // Parse device description
              var parseString = require('xml2js').parseString;
              parseString(body, function (err, result) {
                if (err) {
                  node.log("Failed to parse the Device Description with error: " + err);
                  return;
                }
                node.devDesc = {
                  modelName: result.root.device[0].modelName[0],
                  modelNumber: result.root.device[0].modelNumber[0],
                  serialNumber: result.root.device[0].serialNumber[0],
                  modelDescription: result.root.device[0].modelDescription[0],
                  manufacturer: result.root.device[0].manufacturer[0],
                  friendlyName: result.root.device[0].friendlyName[0],
                  presentationURL: result.root.device[0].presentationURL[0],
                  udn: result.root.device[0].UDN[0]
                };
              });

              // Update state of all nodes
              node.connecting = false;
              node.connected = true;
              for (var id in node.users) {
                if (node.users.hasOwnProperty(id)) {
                  node.users[id].status({fill:"green", shape:"dot", text:"connected"});
                }
              }

              // Start Multicast listener for event notification
              startEventListener();
            });
        });

        req.on('error', function(err) {
          node.log('Failed to get device description with error: ' + err);
          if (node.connecting) {
            node.connecting = false;
          }
        });

      }
    };

    this.subscribe = function(callback, ref) {
      ref = ref || 0;
      var sub = {
        handler:function(topic, payload) {
          callback(topic, payload);
        },
        ref: ref
      };
      node.subscriptions[ref] = sub;
    };

    this.unsubscribe = function(ref) {
      ref = ref || 0;
      var sub = node.subscriptions[ref];
      if (sub) {
        delete node.subscriptions[ref];
      }
    };

    // Internal sub routines
    var startEventListener = function() {
      node.log('STARTING event listener');

      var net = require('net');
      var dgram = require('dgram');
      node.inputSocket = dgram.createSocket({type:'udp4', reuseAddr: true});
      node.inputSocket.on('message', function (msg, rinfo) {

        // node.log("[" + rinfo.address + "] --> " + msg.toString());

        if (rinfo.address == node.address) {
          //node.log("[" + rinfo.address + "] --> " + msg.toString());

          // Split to header and body
          msg = msg.toString().split('\r\n\r\n');
          var header = msg[0];
          var body = msg[1];

          // Ignore UPNP search requests
          var method = header.split('\r\n').shift().split(' ').shift().trim();
          if (method == 'M-SEARCH'){
            return;
          }

          // Parse rest of header
          var arr = header.match(/[^\r\n]+/g);
          var headerInfo = {};
          for (var i = 1; i < arr.length; ++i){
            var tem = arr[i].split(/:(.+)?/);
            if (typeof(tem[1]) == 'string'){ tem[1] = tem[1].trim(); }
            headerInfo[tem[0].toLowerCase()] = tem[1];
          };
          //node.log("METHOD: " + method);
          //node.log("BODY: " + body);
          //node.log("NTS: " + headerInfo['nts']);

          // NOTIFY messages tell us about the properties that changed
          if (method == 'NOTIFY' && headerInfo['nts'] == 'yamaha:propchange') {

            var parseString = require('xml2js').parseString;
            parseString(body, function (err, result) {
              if (err) {
                node.error('Failed to parse the event with error: ' + err);
                return;
              }

              for (var i in result.YAMAHA_AV.Main_Zone[0].Property) {
                var prop = result.YAMAHA_AV.Main_Zone[0].Property[i];
                if (node.debug) {
                  node.log('Property-Change: ' + prop);
                }

                // Event Notification only notifies that there is a change in each item.
                // It is necessary to issue the GET command to obtain the content of the item with the change.
                var topics = {
                  'Power': 'System.Power_Control.Power',
                  'Volume': 'Main_Zone.Volume.Lvl',
                  'Input': 'Main_Zone.Input.Input_Sel'
                };
                if (topics[prop]) {
                  node.sendGetCommand(topics[prop]).then(function(value) {
                    for (var s in node.subscriptions) {
                      node.subscriptions[s].handler(topics[prop], value);
                    }
                  }).catch(function(error) {
                    node.error("Failed to request data from AVR with error: " + error);
                  });
                } else if (prop == 'Play_Info') {
                  // TODO
                  if (node.debug) {
                    node.log('Received unsupported Property-Change via mulitcast: ' + prop);
                  }
                } else if (prop == 'List_info') {
                  // TODO
                  if (node.debug) {
                    node.log('Received unsupported Property-Change via mulitcast: ' + prop);
                  }
                } else {
                  if (node.debug) {
                    node.log('Received unsupported Property-Change via mulitcast: ' + prop);
                  }
                  return;
                }
              }
            });
          }
        }
      });

      node.inputSocket.on('listening', function () {
        var address = node.inputSocket.address();
        node.log('UDP client listening on ' + address.address + ":" + address.port);
        node.inputSocket.setBroadcast(true)
        node.inputSocket.setMulticastTTL(128);
        node.inputSocket.addMembership('239.255.255.250', '192.168.0.101');
        node.inputSocket.addMembership('239.255.255.250', '127.0.0.1');

/*
          var message = new Buffer(
            "M-SEARCH * HTTP/1.1\r\n" +
            "HOST: 239.255.255.250:1900\r\n" +
            "MAN: \"ssdp:discover\"\r\n" +
            "ST: "+'ssdp:all'+"\r\n" + // Essential, used by the client to specify what they want to discover, eg 'ST:ge:fridge'
            "MX: 3\r\n" + // 1 second to respond (but they all respond immediately?)
            "\r\n");

          node.inputSocket.send(message, 0, message.length, 1900, "239.255.255.250");
*/
      });

      node.inputSocket.bind(1900);
    }

    // Define config node event listeners
    node.on("close", function(done){
      node.closing = true;
      node.disconnect();
      done();
    });
  }
  RED.nodes.registerType("avr-yamaha", AvrYamahaNodeConfig);

	/* ---------------------------------------------------------------------------
	 * INPUT node
	 * -------------------------------------------------------------------------*/
  function AvrYamahaNodeIn(config) {
    RED.nodes.createNode(this, config);

		// Save settings in local node
		this.device = config.device;
    this.deviceNode = RED.nodes.getNode(this.device);
		this.name = config.name;
		this.devdesc = config.devdesc;

    // Register at config node to receive new events
    var node = this;
    if (this.deviceNode) {
      this.status({fill:"red", shape:"ring", text:"disconnected"});
      if (this.deviceNode.connected) {
          this.status({fill:"green", shape:"dot", text:"connected"});
      }

      this.deviceNode.register(this);
      this.deviceNode.subscribe(function(topic, payload) {
        var msg = {topic:topic, payload:payload};
        if (node.devdesc) {
          msg.devDesc = node.deviceNode.devDesc;
        }
        node.send(msg);
      }, this.id);

      this.on('close', function(done) {
        if (node.deviceNode) {
          node.deviceNode.unsubscribe(node.id);
          node.deviceNode.deregister(node, done);
          node.yamaha = null;
        }
      });
    } else {
      this.error(RED._("avr-yamaha.errors.missing-config"));
    }
  }
  RED.nodes.registerType("AVR-Yamaha-in", AvrYamahaNodeIn);

	/* ---------------------------------------------------------------------------
	 * GET node
	 * -------------------------------------------------------------------------*/
  function AvrYamahaNodeGet(config) {
    RED.nodes.createNode(this, config);

		// Save settings in local node
    this.device = config.device;
    this.deviceNode = RED.nodes.getNode(this.device);
		this.name = config.name;
		this.topic = config.topic;

    var node = this;
    if (this.deviceNode) {

  		// Input handler, called on incoming flow
      this.on('input', function(msg) {

        // If no topic is given in the config, then we us the topic in the msg.
        var topic = (node.topic) ? node.topic : msg.topic;
        if (!topic) {
          node.error('No topic given. Specify either in the config or via msg.topic!');
          return;
        }

        // Get data from the device.
        node.deviceNode.sendGetCommand(topic).then(function(value) {
          msg.payload = value;
          node.send(msg);
        }).catch(function(error) {
          node.error("Failed to request data from AVR with error: " + error);
        });

      });

    } else {
      this.error(RED._("avr-yamaha.errors.missing-config"));
    }
  }
  RED.nodes.registerType("AVR-Yamaha-get", AvrYamahaNodeGet);

	/* ---------------------------------------------------------------------------
	 * PUT node
	 * -------------------------------------------------------------------------*/
	function AvrYamahaNodePut(config) {
		RED.nodes.createNode(this, config);

		// Save settings in local node
    this.device = config.device;
    this.deviceNode = RED.nodes.getNode(this.device);
		this.name = config.name;
    this.topic = config.topic;
    this.payload = config.payload;

    var node = this;
    if (this.deviceNode) {

  		// Input handler, called on incoming flow
  		this.on('input', function(msg) {

        // If no topic is given in the config, then we us the topic in the msg.
        var topic = (node.topic) ? node.topic : msg.topic;
        if (!topic) {
          node.error('No topic given. Specify either in the config or via msg.topic!');
          return;
        }

        // If no payload is given in the config, then we us the payload in the msg.
        var payload = (node.payload) ? node.payload : msg.payload;
        if (payload === null || payload === undefined) {
          node.error('invalid payload: ' + payload.toString());
          return;
        }

        // Put data to the device.
        node.deviceNode.sendPutCommand(topic, payload).then(function(value) {
          // Continue the flow when data has been put...
          node.send(msg);
        }).catch(function(error) {
          node.error("Failed to put data to AVR with error: " + error);
        });

  		});

    } else {
      this.error(RED._("avr-yamaha.errors.missing-config"));
    }
	}
	RED.nodes.registerType("AVR-Yamaha-put", AvrYamahaNodePut);

  /* ---------------------------------------------------------------------------
	 * Backend informations
	 * -------------------------------------------------------------------------*/
  require('./references.js').provideReferences(RED);

};
<|endoftext|>


<commit_before>'use strict';

(function() {
  var app = angular.module('portal.main.controllers', []);

  app.controller('MainController', ['$localStorage','$scope', function($localStorage, $scope) {

    $scope.$storage = $localStorage.$default( {
      showSidebar: true,
      sidebarQuicklinks: false,
      showKeywordsInMarketplace : false,
      homeImg : "img/square.jpg",
      sidebarShowProfile: false,
      profileImg: "img/terrace.jpg",
      notificationsDemo : false,
      pithyContentOnHome : false,
      typeaheadSearch: false,
      view : 'list'} );
  } ]);

  /* Username */

  app.controller('SessionCheckController', [ 'mainService', function(mainService) {
    var that = this;
    that.user = [];
    mainService.getUser().then(function(result){
      that.user = result;
    });
  }]);

  /* Header */
  app.controller('HeaderController', ['$scope','$location', function($scope, $location) {
    this.navbarCollapsed = true;
    $scope.showSearch = false;
    $scope.showSearchFocus = false;

    this.toggleSearch = function() {
        $scope.showSearch = !$scope.showSearch;
        $scope.showSearchFocus = !$scope.showSearchFocus;
    }
  }]);

  app.controller('SidebarController',[ '$localStorage', '$scope', 'mainService', function($localStorage, $scope, mainService) {
      $scope.$storage = $localStorage;
      $scope.sidebar = [];
      mainService.getSidebar().then(function(result){
          $scope.sidebar = result.data.sidebar;
      });

      this.canSee = function(storageVar) {
          if(storageVar === '')
              return true;
          else {
              return $scope.$storage[storageVar];
          }
      }
  }]);

})();
<commit_msg>remove storage.view<commit_after>'use strict';

(function() {
  var app = angular.module('portal.main.controllers', []);

  app.controller('MainController', ['$localStorage','$scope', function($localStorage, $scope) {

    $scope.$storage = $localStorage.$default( {
      showSidebar: true,
      sidebarQuicklinks: false,
      showKeywordsInMarketplace : false,
      homeImg : "img/square.jpg",
      sidebarShowProfile: false,
      profileImg: "img/terrace.jpg",
      notificationsDemo : false,
      pithyContentOnHome : false,
      typeaheadSearch: false
      } );
  } ]);

  /* Username */

  app.controller('SessionCheckController', [ 'mainService', function(mainService) {
    var that = this;
    that.user = [];
    mainService.getUser().then(function(result){
      that.user = result;
    });
  }]);

  /* Header */
  app.controller('HeaderController', ['$scope','$location', function($scope, $location) {
    this.navbarCollapsed = true;
    $scope.showSearch = false;
    $scope.showSearchFocus = false;

    this.toggleSearch = function() {
        $scope.showSearch = !$scope.showSearch;
        $scope.showSearchFocus = !$scope.showSearchFocus;
    }
  }]);

  app.controller('SidebarController',[ '$localStorage', '$scope', 'mainService', function($localStorage, $scope, mainService) {
      $scope.$storage = $localStorage;
      $scope.sidebar = [];
      mainService.getSidebar().then(function(result){
          $scope.sidebar = result.data.sidebar;
      });

      this.canSee = function(storageVar) {
          if(storageVar === '')
              return true;
          else {
              return $scope.$storage[storageVar];
          }
      }
  }]);

})();
<|endoftext|>


<commit_before>/*
 * Licensed to Apereo under one or more contributor license
 * agreements. See the NOTICE file distributed with this work
 * for additional information regarding copyright ownership.
 * Apereo licenses this file to you under the Apache License,
 * Version 2.0 (the "License"); you may not use this file
 * except in compliance with the License.  You may obtain a
 * copy of the License at the following location:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
/*
 * This file leverages Modal Bootstrap to display a single notice, like an Term of Service
 */

var upmodal_notice = upmodal_notice || {};

if (!upmodal_notice.init) {
  upmodal_notice.init = true;

  (function() { // necessary?

    var defaults = {
      selectors: {
        content: '.np-content',
        title: '.np-title',
        form: '.np-action-form',
        body: '.np-body',
        link: '.np-link',
        actions: '.np-actions',
        actionTemplate: '.np-action-template',
        closeButton: '.np-close'
      }
    };

    upmodal_notice.launch = function ($, container, options) {
      var settings = $.extend({}, defaults, options);

      // First 'prime-the-pump' with an ActionURL
      var initNotices = function(settings, callback) {
        $.ajax({
          type: 'POST',
          url: settings.invokeNotificationServiceUrl,
          async: false,
          success: function() {
            fetchNotices(settings, callback);
          },
          error: console.error
        });
      }

      // Then fetch the notifications with a ResourceURL
      var fetchNotices = function(settings, callback) {
        $.ajax({
          url: settings.getNotificationsUrl,
          type: 'POST',
          dataType: 'json',
          success: function (data) {
            var feed = data.feed;
            callback(feed);
          },
          error: console.error
        });
      }

      var handleAction = function() {
        var actionButton = $(this);
        var formElement = container.find(settings.selectors.form);
        $.post(actionButton.attr('data-url'), formElement.serialize(), console.log);
        // Any action closes the modal window
        container.modal('hide');
      }

      var resetDialog = function() {
        container.off('hidden.bs.modal'); // Clear previous event handler(s)
        container.find(settings.selectors.closeButton).hide();
        container.find(settings.selectors.actions).find('.np-action').remove();
      }

      var drawActions = function(alert) {
        var actionsContainer = container.find(settings.selectors.actions);
        var availableActions = alert.availableActions;
        var actionTemplate = actionsContainer.find(settings.selectors.actionTemplate);

        if (availableActions && availableActions.length > 0) {
          // Draw actions if we have them...
          availableActions.forEach(function (action, index) {
            var actionUrl = settings
              .invokeActionUrlTemplate
              .replace('NOTIFICATIONID', alert.id)
              .replace('ACTIONID', action.id);

            var actionElement = actionTemplate.clone();
            actionElement.removeClass('np-action-template');
            actionElement.toggleClass('hidden');
            actionElement.addClass('np-action');

            if (index === 0) {
              // Only the first button is btn-primary
              actionElement.find('a').addClass('btn-primary');
            }

            actionElement.find('a').attr('data-url', actionUrl).html(action.label).click(handleAction);
            actionElement.appendTo(actionsContainer);
          })
        } else {
          // Or offer a close (x) button if we don't...
          container.find(settings.selectors.closeButton).show();
        }
      }

      var drawAlert = function(alert) {
        var content = container.find(settings.selectors.content);
        // Insert context
        content.find(settings.selectors.title).html(alert.title);

        if (alert.body) {
          content.find(settings.selectors.body).html(alert.body);
        }

        if (alert.url) {
          var linkText = alert.linkText || alert.url;
          content.find(settings.selectors.link).attr('href', alert.url).html(linkText);
        }

        // Add the actions
        drawActions(alert);
      }

      var showNextNotice = function(feed, index) {
        var alert = feed[index];
        resetDialog();
        drawAlert(alert);

        if (feed.length > index + 1) {
          // There are more notices after this one...
          container.on('hidden.bs.modal', function() {
            showNextNotice(feed, index + 1);
          });
        }
        container.modal("show");
      }

      var showEachNoticeInTurn = function(feed) {
        // Do we have any notices to show?
        if (feed && feed.length !== 0) {
          showNextNotice(feed, 0);
        }
      }

      // Invoke notifications
      initNotices(settings, showEachNoticeInTurn);
    }

  })();

}
<commit_msg>NOTIFPLT-87:  Update modal-notice.js per dismcussion on the PR -- wrap 'if (){}' inside '(function(){})();' (not the other way around)<commit_after>/*
 * Licensed to Apereo under one or more contributor license
 * agreements. See the NOTICE file distributed with this work
 * for additional information regarding copyright ownership.
 * Apereo licenses this file to you under the Apache License,
 * Version 2.0 (the "License"); you may not use this file
 * except in compliance with the License.  You may obtain a
 * copy of the License at the following location:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
/*
 * This file leverages Modal Bootstrap to display a single notice, like an Term of Service
 */
var upmodal_notice = upmodal_notice || {};

(function() {

    if (!upmodal_notice.init) {
        upmodal_notice.init = true;

        var defaults = {
            selectors: {
                content: '.np-content',
                title: '.np-title',
                form: '.np-action-form',
                body: '.np-body',
                link: '.np-link',
                actions: '.np-actions',
                actionTemplate: '.np-action-template',
                closeButton: '.np-close'
            }
        };

        upmodal_notice.launch = function($, container, options) {
            var settings = $.extend({}, defaults, options);

            // First 'prime-the-pump' with an ActionURL
            var initNotices = function(settings, callback) {
                $.ajax({
                    type: 'POST',
                    url: settings.invokeNotificationServiceUrl,
                    async: false,
                    success: function() {
                        fetchNotices(settings, callback);
                    },
                    error: console.error
                });
            }

            // Then fetch the notifications with a ResourceURL
            var fetchNotices = function(settings, callback) {
                $.ajax({
                    url: settings.getNotificationsUrl,
                    type: 'POST',
                    dataType: 'json',
                    success: function(data) {
                        var feed = data.feed;
                        callback(feed);
                    },
                    error: console.error
                });
            }

            var handleAction = function() {
                var actionButton = $(this);
                var formElement = container.find(settings.selectors.form);
                $.post(actionButton.attr('data-url'), formElement.serialize(), console.log);
                // Any action closes the modal window
                container.modal('hide');
            }

            var resetDialog = function() {
                container.off('hidden.bs.modal'); // Clear previous event handler(s)
                container.find(settings.selectors.closeButton).hide();
                container.find(settings.selectors.actions).find('.np-action').remove();
            }

            var drawActions = function(alert) {
                var actionsContainer = container.find(settings.selectors.actions);
                var availableActions = alert.availableActions;
                var actionTemplate = actionsContainer.find(settings.selectors.actionTemplate);

                if (availableActions && availableActions.length > 0) {
                    // Draw actions if we have them...
                    availableActions.forEach(function(action, index) {
                        var actionUrl = settings
                            .invokeActionUrlTemplate
                            .replace('NOTIFICATIONID', alert.id)
                            .replace('ACTIONID', action.id);

                        var actionElement = actionTemplate.clone();
                        actionElement.removeClass('np-action-template');
                        actionElement.toggleClass('hidden');
                        actionElement.addClass('np-action');

                        if (index === 0) {
                            // Only the first button is btn-primary
                            actionElement.find('a').addClass('btn-primary');
                        }

                        actionElement.find('a').attr('data-url', actionUrl).html(action.label)
                                .click(handleAction);
                        actionElement.appendTo(actionsContainer);
                    })
                } else {
                    // Or offer a close (x) button if we don't...
                    container.find(settings.selectors.closeButton).show();
                }
            }

            var drawAlert = function(alert) {
                var content = container.find(settings.selectors.content);
                // Insert context
                content.find(settings.selectors.title).html(alert.title);

                if (alert.body) {
                    content.find(settings.selectors.body).html(alert.body);
                }

                if (alert.url) {
                    var linkText = alert.linkText || alert.url;
                    content.find(settings.selectors.link).attr('href', alert.url).html(linkText);
                }

                // Add the actions
                drawActions(alert);
            }

            var showNextNotice = function(feed, index) {
                var alert = feed[index];
                resetDialog();
                drawAlert(alert);

                if (feed.length > index + 1) {
                    // There are more notices after this one...
                    container.on('hidden.bs.modal', function() {
                        showNextNotice(feed, index + 1);
                    });
                }
                container.modal("show");
            }

            var showEachNoticeInTurn = function(feed) {
                // Do we have any notices to show?
                if (feed && feed.length !== 0) {
                    showNextNotice(feed, 0);
                }
            }

            // Invoke notifications
            initNotices(settings, showEachNoticeInTurn);
        }

    }

})();
<|endoftext|>