<commit_before>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import uuid from 'uuid';
import __isEmpty from 'lodash/isEmpty';
import queryString from 'query-string';
import './App.css';
import {Log, Session} from './shared/data.js';
import {error} from './shared/log.js';
import MobileSimulator from './components/MobileSimulator.js';
import Lifecycle from './components/Lifecycle.js';
import WorkshopCode from './WorkshopCode.js';
import Title from './Title.js';
import ConsentPhase from './ConsentPhase.js';
import IntroductionPhase from './IntroductionPhase.js';
import StudentsPhase from './StudentsPhase.js';
import DiscussPhase from './DiscussPhase.js';
import ReviewPhase from './ReviewPhase.js';
import ThanksPhase from './ThanksPhase.js';
import {loadDataForCohort, defaultOptions} from './loaders/loadDataForCohort.js';

// Describes the major phases of the whole game
const Phases = {
  WORKSHOP_CODE: 'WORKSHOP_CODE',
  TITLE: 'TITLE',
  CONSENT: 'CONSENT',
  INTRODUCTION: 'INTRODUCTION',
  STUDENTS: 'STUDENTS',
  DISCUSS: 'DISCUSS',
  REVIEW: 'REVIEW',
  THANKS: 'THANKS'
};

// See https://github.com/sindresorhus/query-string/issues/50
function queryHasKey(query, key) {
  return Object.prototype.hasOwnProperty.call(query, key);
}

class GamePage extends Component {
  constructor(props) {
    super(props);
    const {isCodeOrg, defaultWorkshopCode} = props;
    const query = queryString.parse(window.location.search) || {};

    // 12/8 This is a patch to rebalance cells for remaining trials, oversampling from
    // those that have been undersampled to this point due to randomness.  Going forward
    // with other experiments, we should switch strategies so that we generate the
    // experimental cells and then pull from them for each new workshop identifier we see,
    // to force balance across experimental cells.  This is a workaround for the remaining
    // sessions of the study that's already underway.
    const forceCodeOrgBuckets = (isCodeOrg) ? [2, 3, 3, 4, 6, 7] : false;

    this.state = {
      isCodeOrg,
      config: {
        ...defaultOptions,
        forceCodeOrgBuckets
      },
      sessionId: uuid.v4(),
      reviewPhaseOptions: {
        showPercents: !queryHasKey(query, 'reviewnopercents'),
        copyVersion: query['reviewcopyversion'] || '0'
      },
      identifier: (isCodeOrg)
        ? query['cuid'] || Session.unknownIdentifier()
        : Session.unknownIdentifier(),
      workshopCode: (defaultWorkshopCode !== undefined)
        ? defaultWorkshopCode
        : ['DEMO', uuid.v4()].join(':'), // for code.org, set in initial screens
      cohortNumber: null, // set when data is loaded, based on workhopCode
      phase: (isCodeOrg) ? Phases.WORKSHOP_CODE : Phases.TITLE,
      students: null,
      logs: []
    };
    this.onDataLoaded = this.onDataLoaded.bind(this);
    this.onDataError = this.onDataError.bind(this);
    this.onInteraction = this.onInteraction.bind(this);
    this.renderDemo = this.renderDemo.bind(this);
    this.renderCodeOrg = this.renderCodeOrg.bind(this);
  }

  // Describe context of the game session
  session() {
    const {isCodeOrg, identifier, workshopCode, cohortNumber, sessionId} = this.state;
    return Session.create({
      isCodeOrg,
      identifier,
      workshopCode,
      cohortNumber,
      sessionId,
      clientTimestampMs: new Date().getTime(),
      location: window.location.toString()
    });
  }

  shouldWarnAboutCodeStudio() {
    const {identifier} = this.state;
    return (
      (identifier === Session.unknownIdentifier()) ||
      (__isEmpty(identifier))
    );
  }

  doFetchData() {
    const {workshopCode, config} = this.state;
    loadDataForCohort(workshopCode, config)
      .then(this.onDataLoaded)
      .catch(this.onDataError);
  }

  // Optimization
  doPrefetchStudentImages(students) {
    const imageUrls = students.map(s => s.profileImageSrc);
    imageUrls.forEach(imageUrl => {
      const image = new Image();
      image.src = imageUrl;
    });
  }

  // Log to the server
  doLog(log) {
    fetch('/api/log', {
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json'
      },
      method: 'POST',
      body: JSON.stringify(log)
    });
  }

  onWorkshopCodeDone(nextPhase, workshopCode) {
    this.setState({workshopCode, phase: nextPhase});
  }

  onTitleDone(nextPhase) {
    this.setState({phase: nextPhase});
  }

  onDataLoaded(loadedData) {
    const {cohortNumber, students} = loadedData;
    this.setState({cohortNumber, students});
    this.doPrefetchStudentImages(students);
  }

  onDataError(err) {
    error(err);
  }

  // Log the interaction, along with context about the session.
  onInteraction(interaction) {
    const {logs} = this.state;
    const session = this.session();
    const log = Log.create(session, interaction);
    this.setState({ logs: logs.concat(log) });
    this.doLog(log);
  }

  render() {
    const {isCodeOrg} = this.state;
    return (
      <MobileSimulator minWidth={800} minHeight={400}>
        {isCodeOrg ? this.renderCodeOrg() : this.renderDemo()}
      </MobileSimulator>
    );
  }

  // From code.org Code Studio, with identifier on query string
  renderCodeOrg() {
    const {phase, students} = this.state;
    if (phase === Phases.WORKSHOP_CODE) return this.renderWorkshopCode(Phases.TITLE);
    if (phase === Phases.TITLE) return this.renderTitle(Phases.CONSENT);
    if (phase === Phases.CONSENT) return this.renderConsent(Phases.INTRODUCTION);
    if (phase === Phases.INTRODUCTION) return this.renderIntroduction(Phases.STUDENTS);
    if (!students) return this.renderLoading();
    if (phase === Phases.STUDENTS) return this.renderStudents(Phases.DISCUSS);
    if (phase === Phases.DISCUSS) return this.renderDiscuss(Phases.REVIEW);
    if (phase === Phases.REVIEW) return this.renderReview(Phases.THANKS);
    if (phase === Phases.THANKS) return this.renderThanks();
  }

  // Publicly open demo
  renderDemo() {
    const {phase, students} = this.state;
    if (phase === Phases.TITLE) return this.renderTitle(Phases.INTRODUCTION);
    if (phase === Phases.INTRODUCTION) return this.renderIntroduction(Phases.STUDENTS);
    if (!students) return this.renderLoading();
    if (phase === Phases.STUDENTS) return this.renderStudents(Phases.REVIEW);
    if (phase === Phases.REVIEW) return this.renderReview(Phases.THANKS);
    if (phase === Phases.THANKS) return this.renderThanks();
  }

  renderWorkshopCode(phase) {
    return <WorkshopCode
      shouldWarnAboutCodeStudio={this.shouldWarnAboutCodeStudio()}
      onInteraction={this.onInteraction}
      onDone={this.onWorkshopCodeDone.bind(this, phase)} />;
  }

  renderTitle(phase) {
    return (
      <Lifecycle componentWillMount={() => this.doFetchData()}>
        <Title
          onInteraction={this.onInteraction}
          onDone={this.onTitleDone.bind(this, phase)} />
      </Lifecycle>
    );
  }

  renderConsent(phase) {
    return <ConsentPhase
      onInteraction={this.onInteraction}
      onDone={() => this.setState({phase})} />;
  }

  renderIntroduction(phase) {
    return <IntroductionPhase
      onInteraction={this.onInteraction}
      onDone={() => this.setState({phase})} />;
  }

  renderLoading() {
    return
Loading...
;
  }

  renderStudents(phase) {
    const {shouldAskOpenResponse} = this.props;
    const {students, config} = this.state;
    const {forcedProfileCount} = config;
    return (
      <StudentsPhase
        students={students}
        allowSkipAfter={forcedProfileCount}
        shouldAskOpenResponse={shouldAskOpenResponse}
        onInteraction={this.onInteraction}
        onDone={() => this.setState({phase})} />
    );
  }

  renderDiscuss(phase) {
    const {students} = this.state;
    return <DiscussPhase
      students={students}
      onInteraction={this.onInteraction}
      onDone={() => this.setState({phase})} />;
  }

  renderReview(phase) {
    const {students, workshopCode, reviewPhaseOptions} = this.state;
    return <ReviewPhase
      workshopCode={workshopCode}
      students={students}
      onInteraction={this.onInteraction}
      reviewPhaseOptions={reviewPhaseOptions}
      onDone={() => this.setState({phase})} />;
  }

  renderThanks(phase) {
    const {logs} = this.state;
    return <ThanksPhase
      logs={logs}
      onInteraction={this.onInteraction} />;
  }
}
GamePage.propTypes = {
  isCodeOrg: PropTypes.bool.isRequired,
  shouldAskOpenResponse: PropTypes.bool,
  defaultWorkshopCode: PropTypes.string
};
GamePage.defaultProps = {
  defaultWorkshopCode: undefined
};

export default GamePage;
<commit_msg>Remove forceCodeOrgBuckets<commit_after>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import uuid from 'uuid';
import __isEmpty from 'lodash/isEmpty';
import queryString from 'query-string';
import './App.css';
import {Log, Session} from './shared/data.js';
import {error} from './shared/log.js';
import MobileSimulator from './components/MobileSimulator.js';
import Lifecycle from './components/Lifecycle.js';
import WorkshopCode from './WorkshopCode.js';
import Title from './Title.js';
import ConsentPhase from './ConsentPhase.js';
import IntroductionPhase from './IntroductionPhase.js';
import StudentsPhase from './StudentsPhase.js';
import DiscussPhase from './DiscussPhase.js';
import ReviewPhase from './ReviewPhase.js';
import ThanksPhase from './ThanksPhase.js';
import {loadDataForCohort, defaultOptions} from './loaders/loadDataForCohort.js';

// Describes the major phases of the whole game
const Phases = {
  WORKSHOP_CODE: 'WORKSHOP_CODE',
  TITLE: 'TITLE',
  CONSENT: 'CONSENT',
  INTRODUCTION: 'INTRODUCTION',
  STUDENTS: 'STUDENTS',
  DISCUSS: 'DISCUSS',
  REVIEW: 'REVIEW',
  THANKS: 'THANKS'
};

// See https://github.com/sindresorhus/query-string/issues/50
function queryHasKey(query, key) {
  return Object.prototype.hasOwnProperty.call(query, key);
}

class GamePage extends Component {
  constructor(props) {
    super(props);
    const {isCodeOrg, defaultWorkshopCode} = props;
    const query = queryString.parse(window.location.search) || {};

    // 12/8/17 This is a patch to rebalance cells for remaining trials, oversampling from
    // those that have been undersampled to this point due to randomness.  Going forward
    // with other experiments, we should switch strategies so that we generate the
    // experimental cells and then pull from them for each new workshop identifier we see,
    // to force balance across experimental cells.  This is a workaround for the remaining
    // sessions of the study that's already underway.
    // const forceCodeOrgBuckets = (isCodeOrg) ? [2, 3, 3, 4, 6, 7] : false;

    // 9/12/18 This patch was removed for the Q2 2018 run.
    const forceCodeOrgBuckets = false;

    this.state = {
      isCodeOrg,
      config: {
        ...defaultOptions,
        forceCodeOrgBuckets
      },
      sessionId: uuid.v4(),
      reviewPhaseOptions: {
        showPercents: !queryHasKey(query, 'reviewnopercents'),
        copyVersion: query['reviewcopyversion'] || '0'
      },
      identifier: (isCodeOrg)
        ? query['cuid'] || Session.unknownIdentifier()
        : Session.unknownIdentifier(),
      workshopCode: (defaultWorkshopCode !== undefined)
        ? defaultWorkshopCode
        : ['DEMO', uuid.v4()].join(':'), // for code.org, set in initial screens
      cohortNumber: null, // set when data is loaded, based on workhopCode
      phase: (isCodeOrg) ? Phases.WORKSHOP_CODE : Phases.TITLE,
      students: null,
      logs: []
    };
    this.onDataLoaded = this.onDataLoaded.bind(this);
    this.onDataError = this.onDataError.bind(this);
    this.onInteraction = this.onInteraction.bind(this);
    this.renderDemo = this.renderDemo.bind(this);
    this.renderCodeOrg = this.renderCodeOrg.bind(this);
  }

  // Describe context of the game session
  session() {
    const {isCodeOrg, identifier, workshopCode, cohortNumber, sessionId} = this.state;
    return Session.create({
      isCodeOrg,
      identifier,
      workshopCode,
      cohortNumber,
      sessionId,
      clientTimestampMs: new Date().getTime(),
      location: window.location.toString()
    });
  }

  shouldWarnAboutCodeStudio() {
    const {identifier} = this.state;
    return (
      (identifier === Session.unknownIdentifier()) ||
      (__isEmpty(identifier))
    );
  }

  doFetchData() {
    const {workshopCode, config} = this.state;
    loadDataForCohort(workshopCode, config)
      .then(this.onDataLoaded)
      .catch(this.onDataError);
  }

  // Optimization
  doPrefetchStudentImages(students) {
    const imageUrls = students.map(s => s.profileImageSrc);
    imageUrls.forEach(imageUrl => {
      const image = new Image();
      image.src = imageUrl;
    });
  }

  // Log to the server
  doLog(log) {
    fetch('/api/log', {
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json'
      },
      method: 'POST',
      body: JSON.stringify(log)
    });
  }

  onWorkshopCodeDone(nextPhase, workshopCode) {
    this.setState({workshopCode, phase: nextPhase});
  }

  onTitleDone(nextPhase) {
    this.setState({phase: nextPhase});
  }

  onDataLoaded(loadedData) {
    const {cohortNumber, students} = loadedData;
    this.setState({cohortNumber, students});
    this.doPrefetchStudentImages(students);
  }

  onDataError(err) {
    error(err);
  }

  // Log the interaction, along with context about the session.
  onInteraction(interaction) {
    const {logs} = this.state;
    const session = this.session();
    const log = Log.create(session, interaction);
    this.setState({ logs: logs.concat(log) });
    this.doLog(log);
  }

  render() {
    const {isCodeOrg} = this.state;
    return (
      <MobileSimulator minWidth={800} minHeight={400}>
        {isCodeOrg ? this.renderCodeOrg() : this.renderDemo()}
      </MobileSimulator>
    );
  }

  // From code.org Code Studio, with identifier on query string
  renderCodeOrg() {
    const {phase, students} = this.state;
    if (phase === Phases.WORKSHOP_CODE) return this.renderWorkshopCode(Phases.TITLE);
    if (phase === Phases.TITLE) return this.renderTitle(Phases.CONSENT);
    if (phase === Phases.CONSENT) return this.renderConsent(Phases.INTRODUCTION);
    if (phase === Phases.INTRODUCTION) return this.renderIntroduction(Phases.STUDENTS);
    if (!students) return this.renderLoading();
    if (phase === Phases.STUDENTS) return this.renderStudents(Phases.DISCUSS);
    if (phase === Phases.DISCUSS) return this.renderDiscuss(Phases.REVIEW);
    if (phase === Phases.REVIEW) return this.renderReview(Phases.THANKS);
    if (phase === Phases.THANKS) return this.renderThanks();
  }

  // Publicly open demo
  renderDemo() {
    const {phase, students} = this.state;
    if (phase === Phases.TITLE) return this.renderTitle(Phases.INTRODUCTION);
    if (phase === Phases.INTRODUCTION) return this.renderIntroduction(Phases.STUDENTS);
    if (!students) return this.renderLoading();
    if (phase === Phases.STUDENTS) return this.renderStudents(Phases.REVIEW);
    if (phase === Phases.REVIEW) return this.renderReview(Phases.THANKS);
    if (phase === Phases.THANKS) return this.renderThanks();
  }

  renderWorkshopCode(phase) {
    return <WorkshopCode
      shouldWarnAboutCodeStudio={this.shouldWarnAboutCodeStudio()}
      onInteraction={this.onInteraction}
      onDone={this.onWorkshopCodeDone.bind(this, phase)} />;
  }

  renderTitle(phase) {
    return (
      <Lifecycle componentWillMount={() => this.doFetchData()}>
        <Title
          onInteraction={this.onInteraction}
          onDone={this.onTitleDone.bind(this, phase)} />
      </Lifecycle>
    );
  }

  renderConsent(phase) {
    return <ConsentPhase
      onInteraction={this.onInteraction}
      onDone={() => this.setState({phase})} />;
  }

  renderIntroduction(phase) {
    return <IntroductionPhase
      onInteraction={this.onInteraction}
      onDone={() => this.setState({phase})} />;
  }

  renderLoading() {
    return
Loading...
;
  }

  renderStudents(phase) {
    const {shouldAskOpenResponse} = this.props;
    const {students, config} = this.state;
    const {forcedProfileCount} = config;
    return (
      <StudentsPhase
        students={students}
        allowSkipAfter={forcedProfileCount}
        shouldAskOpenResponse={shouldAskOpenResponse}
        onInteraction={this.onInteraction}
        onDone={() => this.setState({phase})} />
    );
  }

  renderDiscuss(phase) {
    const {students} = this.state;
    return <DiscussPhase
      students={students}
      onInteraction={this.onInteraction}
      onDone={() => this.setState({phase})} />;
  }

  renderReview(phase) {
    const {students, workshopCode, reviewPhaseOptions} = this.state;
    return <ReviewPhase
      workshopCode={workshopCode}
      students={students}
      onInteraction={this.onInteraction}
      reviewPhaseOptions={reviewPhaseOptions}
      onDone={() => this.setState({phase})} />;
  }

  renderThanks(phase) {
    const {logs} = this.state;
    return <ThanksPhase
      logs={logs}
      onInteraction={this.onInteraction} />;
  }
}
GamePage.propTypes = {
  isCodeOrg: PropTypes.bool.isRequired,
  shouldAskOpenResponse: PropTypes.bool,
  defaultWorkshopCode: PropTypes.string
};
GamePage.defaultProps = {
  defaultWorkshopCode: undefined
};

export default GamePage;
<|endoftext|>


<commit_before>import fp from 'lodash/fp'
import { handleActions, concat } from 'redux-fp'

import config from './config'
import { searchQuerySelector } from './selectors'

const appUpdater = handleActions({
  SET_BROKEN: () => fp.set('isBroken', true),
  SET_VISIBILITY: ({ payload }) => fp.set('isVisible', payload),

  SAVE_LAST_SCROLL_POSITION: ({ payload: { id, position } }) => fp.set(
    ['scrollPositions', id],
    position
  ),
})

const historyUpdater = handleActions({
  NAVIGATED: ({ payload }) => fp.set('route', payload)
})

const socketUpdater = handleActions({
  SOCKET_CONNECTED: () => fp.set('isSocketConnected', true),

  SOCKET_DISCONNECTED: () => fp.pipe(
    fp.set('isSocketConnected', false),
    fp.update('isSubscribedToChannels', fp.mapValues(() => false)),
    fp.update('isSubscribedToUsers', fp.mapValues(() => false)),
    fp.update('isSubscribedToMessages', fp.mapValues(() => false)),
    fp.update('isViewingArchive', fp.mapValues(() => true)),
  ),

  SOCKET_RECONNECTED: () => state => fp.pipe(
    fp.set('isSocketReconnected', true),
    fp.set('resetChannels', true),
    fp.set('resetUsers', fp.mapValues(() => true, state.users)),
  )(state),
})

const channelUpdater = handleActions({
  NAVIGATED: ({ payload }) => fp.set('channelName', (
    config.embedChannel
    ? config.embedChannel
    : payload.params.channelName || null
  )),

  'client/CHANNEL_CHANGES': ({ payload: { changes }}) => state => fp.pipe(
    fp.update('channels', prevChannels => {
      prevChannels = state.resetChannels ? {} : prevChannels

      const nextChannels = fp.reduce((channels, { new_val, old_val }) => (
        new_val
        ? fp.set(new_val.name, new_val, channels)
        : fp.unset(old_val.name, channels)
      ), prevChannels, changes)

      return nextChannels
    }),
    fp.set('resetChannels', false)
  )(state),
})

const usersUpdater = handleActions({
  'server/SUBSCRIBE_TO_USERS': ({ payload }) => fp.set(
    ['isSubscribedToUsers', payload.channelName],
    true
  ),

  'client/USER_CHANGES': ({ payload: { channelName, changes }}) => state => fp.pipe(
    fp.update(['users', channelName], prevUsers => {
      prevUsers = state.resetUsers[channelName] ? {} : prevUsers

      const nextUsers = fp.reduce((users, { new_val, old_val }) => (
        new_val
        ? fp.set([new_val.nick], new_val, users)
        : fp.unset([old_val.nick], users)
      ), prevUsers, changes)

      return nextUsers
    }),
    fp.set(['resetUsers', channelName], false)
  )(state),
})

const messagesUpdater = handleActions({
  SET_MESSAGES: ({ payload }) => fp.pipe(
    fp.set(['messages', payload.channel], payload.messages),
    fp.set(['isViewingArchive', payload.channel], false)
  ),

  SET_MESSAGES_BEFORE: ({ payload }) => fp.pipe(
    fp.update(
      ['messages', payload.channel],
      currentMessages => fp.concat(payload.messages, currentMessages)
    ),
    fp.set(
      ['hasReachedBeginning', payload.channel],
      payload.messages.length === 0,
    )
  ),

  SET_MESSAGES_AFTER: ({ payload }) => fp.pipe(
    fp.update(
      ['messages', payload.channel],
      currentMessages => fp.concat(currentMessages, payload.messages)
    ),
    fp.set(
      ['isViewingArchive', payload.channel],
      payload.messages.length !== 0
    )
  ),

  GET_MESSAGES_AROUND: () => state => fp.pipe(
    fp.set(['messages', state.channelName], []),
    fp.unset(['scrollPositions', `messages.${state.channelName}`])
  )(state),
  SET_MESSAGES_AROUND: ({ payload }) => fp.pipe(
    fp.set(['messages', payload.channel], payload.messages),
    fp.set(['isViewingArchive', payload.channel], true)
  ),

  'server/SUBSCRIBE_TO_MESSAGES': ({ payload }) => fp.set(
    ['isSubscribedToMessages', payload.channelName],
    true
  ),

  'client/ADD_MESSAGE': ({ payload }) => state => {
    if (state.isViewingArchive[payload.to]) {
      return state
    }

    const prevMessages = state.messages[payload.to]

    if (!prevMessages || !prevMessages.length) {
      return state
    }

    // Message order is not guaranteed.
    const messageDatetime = Date.parse(payload.timestamp)
    const newerMessages = fp.takeRightWhile(
      m => Date.parse(m.timestamp) > messageDatetime,
      prevMessages
    )

    return fp.update(
      ['messages', payload.to],
      messages => (
        !newerMessages.length
        // If there are no newer messages, append it.
        ? fp.concat(messages, payload)
        // If there are newer messages, insert the message in right place.
        : fp.concat(
          messages.slice(0, messages.length - newerMessages.length),
          fp.concat(payload, newerMessages)
        )
      )
    )(state)
  },

  INPUT_SEARCH: ({ payload }) => fp.set(['search', 'hasReachedBeginning'], false),

  FOUND_MESSAGES: ({ payload }) => state => {
    const { messages, channel, query, limit, messageId } = payload

    const isOutdated = (
      channel !== state.channelName
      || !fp.isEqual(query, searchQuerySelector(state))
    )
    if (isOutdated) {
      return state
    }

    return fp.update('search', search => ({
      channelName: channel,
      query,
      messageId,
      hasReachedBeginning: messages.length < limit,
      messages: (
        !messageId
        ? messages
        : fp.concat(messages, search.messages)
      ),
    }))(state)
  },

  LEAVE_ARCHIVE: () => state => fp.pipe(
    fp.set(['messages', state.channelName], []),
    fp.set(['isViewingArchive', state.channelName], false),
    fp.set(['hasReachedBeginning', state.channelName], false),
  )(state),
})

const faviconUpdater = handleActions({
  UPDATE_UNREAD: () => fp.update('unread', count => count + 1),
  RESET_UNREAD: () => fp.set('unread', 0),
})

const rootReducer = (state, action) => concat(
  appUpdater,
  historyUpdater,
  socketUpdater,
  channelUpdater,
  usersUpdater,
  messagesUpdater,
  faviconUpdater,
)(action)(state)

export {
  rootReducer,
}
<commit_msg>Prevent SET_MESSAGES_AFTER with duplicate messages<commit_after>import fp from 'lodash/fp'
import { handleActions, concat } from 'redux-fp'

import config from './config'
import { searchQuerySelector } from './selectors'

const appUpdater = handleActions({
  SET_BROKEN: () => fp.set('isBroken', true),
  SET_VISIBILITY: ({ payload }) => fp.set('isVisible', payload),

  SAVE_LAST_SCROLL_POSITION: ({ payload: { id, position } }) => fp.set(
    ['scrollPositions', id],
    position
  ),
})

const historyUpdater = handleActions({
  NAVIGATED: ({ payload }) => fp.set('route', payload)
})

const socketUpdater = handleActions({
  SOCKET_CONNECTED: () => fp.set('isSocketConnected', true),

  SOCKET_DISCONNECTED: () => fp.pipe(
    fp.set('isSocketConnected', false),
    fp.update('isSubscribedToChannels', fp.mapValues(() => false)),
    fp.update('isSubscribedToUsers', fp.mapValues(() => false)),
    fp.update('isSubscribedToMessages', fp.mapValues(() => false)),
    fp.update('isViewingArchive', fp.mapValues(() => true)),
  ),

  SOCKET_RECONNECTED: () => state => fp.pipe(
    fp.set('isSocketReconnected', true),
    fp.set('resetChannels', true),
    fp.set('resetUsers', fp.mapValues(() => true, state.users)),
  )(state),
})

const channelUpdater = handleActions({
  NAVIGATED: ({ payload }) => fp.set('channelName', (
    config.embedChannel
    ? config.embedChannel
    : payload.params.channelName || null
  )),

  'client/CHANNEL_CHANGES': ({ payload: { changes }}) => state => fp.pipe(
    fp.update('channels', prevChannels => {
      prevChannels = state.resetChannels ? {} : prevChannels

      const nextChannels = fp.reduce((channels, { new_val, old_val }) => (
        new_val
        ? fp.set(new_val.name, new_val, channels)
        : fp.unset(old_val.name, channels)
      ), prevChannels, changes)

      return nextChannels
    }),
    fp.set('resetChannels', false)
  )(state),
})

const usersUpdater = handleActions({
  'server/SUBSCRIBE_TO_USERS': ({ payload }) => fp.set(
    ['isSubscribedToUsers', payload.channelName],
    true
  ),

  'client/USER_CHANGES': ({ payload: { channelName, changes }}) => state => fp.pipe(
    fp.update(['users', channelName], prevUsers => {
      prevUsers = state.resetUsers[channelName] ? {} : prevUsers

      const nextUsers = fp.reduce((users, { new_val, old_val }) => (
        new_val
        ? fp.set([new_val.nick], new_val, users)
        : fp.unset([old_val.nick], users)
      ), prevUsers, changes)

      return nextUsers
    }),
    fp.set(['resetUsers', channelName], false)
  )(state),
})

const messagesUpdater = handleActions({
  SET_MESSAGES: ({ payload }) => fp.pipe(
    fp.set(['messages', payload.channel], payload.messages),
    fp.set(['isViewingArchive', payload.channel], false)
  ),

  SET_MESSAGES_BEFORE: ({ payload }) => fp.pipe(
    fp.update(
      ['messages', payload.channel],
      currentMessages => fp.concat(payload.messages, currentMessages)
    ),
    fp.set(
      ['hasReachedBeginning', payload.channel],
      payload.messages.length === 0,
    )
  ),

  SET_MESSAGES_AFTER: ({ payload }) => fp.pipe(
    fp.update(
      ['messages', payload.channel],
      currentMessages => {
        // For some reason, on rare occasions, SET_MESSAGES_AFTER action is dispatched twice when reconnecting.
        // It should not happen and I can't reproduce it. Nevertheless, it happens.
        // Quick-fix below should prevent duplicate messages.
        const firstMessage = payload.messages.length ? payload.messages[0] : null
        if (firstMessage && fp.find({ id: firstMessage.id }, currentMessages)) {
          console.log('SET_MESSAGES_AFTER with duplicate messages')
          return currentMessages
        }

        return fp.concat(currentMessages, payload.messages)
      },
    ),
    fp.set(
      ['isViewingArchive', payload.channel],
      payload.messages.length !== 0
    )
  ),

  GET_MESSAGES_AROUND: () => state => fp.pipe(
    fp.set(['messages', state.channelName], []),
    fp.unset(['scrollPositions', `messages.${state.channelName}`])
  )(state),
  SET_MESSAGES_AROUND: ({ payload }) => fp.pipe(
    fp.set(['messages', payload.channel], payload.messages),
    fp.set(['isViewingArchive', payload.channel], true)
  ),

  'server/SUBSCRIBE_TO_MESSAGES': ({ payload }) => fp.set(
    ['isSubscribedToMessages', payload.channelName],
    true
  ),

  'client/ADD_MESSAGE': ({ payload }) => state => {
    if (state.isViewingArchive[payload.to]) {
      return state
    }

    const prevMessages = state.messages[payload.to]

    if (!prevMessages || !prevMessages.length) {
      return state
    }

    // Message order is not guaranteed.
    const messageDatetime = Date.parse(payload.timestamp)
    const newerMessages = fp.takeRightWhile(
      m => Date.parse(m.timestamp) > messageDatetime,
      prevMessages
    )

    return fp.update(
      ['messages', payload.to],
      messages => (
        !newerMessages.length
        // If there are no newer messages, append it.
        ? fp.concat(messages, payload)
        // If there are newer messages, insert the message in right place.
        : fp.concat(
          messages.slice(0, messages.length - newerMessages.length),
          fp.concat(payload, newerMessages)
        )
      )
    )(state)
  },

  INPUT_SEARCH: ({ payload }) => fp.set(['search', 'hasReachedBeginning'], false),

  FOUND_MESSAGES: ({ payload }) => state => {
    const { messages, channel, query, limit, messageId } = payload

    const isOutdated = (
      channel !== state.channelName
      || !fp.isEqual(query, searchQuerySelector(state))
    )
    if (isOutdated) {
      return state
    }

    return fp.update('search', search => ({
      channelName: channel,
      query,
      messageId,
      hasReachedBeginning: messages.length < limit,
      messages: (
        !messageId
        ? messages
        : fp.concat(messages, search.messages)
      ),
    }))(state)
  },

  LEAVE_ARCHIVE: () => state => fp.pipe(
    fp.set(['messages', state.channelName], []),
    fp.set(['isViewingArchive', state.channelName], false),
    fp.set(['hasReachedBeginning', state.channelName], false),
  )(state),
})

const faviconUpdater = handleActions({
  UPDATE_UNREAD: () => fp.update('unread', count => count + 1),
  RESET_UNREAD: () => fp.set('unread', 0),
})

const rootReducer = (state, action) => concat(
  appUpdater,
  historyUpdater,
  socketUpdater,
  channelUpdater,
  usersUpdater,
  messagesUpdater,
  faviconUpdater,
)(action)(state)

export {
  rootReducer,
}
<|endoftext|>


<commit_before>/*globals window, document */

var app = {};
app.REDIRECT_URIS = [
  "http://localhost:10101"
];

app.initialize = function() {
  "use strict";
  // 'load', 'deviceready', 'offline', and 'online'.
  document.addEventListener('deviceready', app.onDeviceReady, false);
};

app.onDeviceReady = function() {
  "use strict";
  var parentElement = document.getElementById('deviceready');
  var listeningElement = parentElement.querySelector('.listening');
  var receivedElement = parentElement.querySelector('.received');
  listeningElement.setAttribute('style', 'display:none;');
  receivedElement.setAttribute('style', 'display:block;');
  console.log('Received Event: deviceready');

  window.oauth.getCode().then(function(code) {
    app.onOAuthToken(code);
  }).catch(function(err) {
    app.setupOAuthListener();
  });

  // For debugging, an exit button
  document.getElementById("exitbutton").addEventListener("click", function(evt) {
    evt.preventDefault();
    navigator.app.exitApp();
  });
};

app.setupOAuthListener = function() {
  "use strict";
  var elts = document.getElementsByClassName("cloudlogo");
  var onclickHandler = function(e) {
    window.oauth.initiateOAuth(app.REDIRECT_URIS).then(function(obj) {
      var url = "https://cloud.digitalocean.com/v1/oauth/authorize?" +
        "client_id=24cb4fd7317204781602be3b19cce72d9258bc59ba08a50b815f65adfc6ca534&" +
        "response_type=token&" +
        "redirect_uri=" + encodeURIComponent(obj.redirect) + "&" +
        "state=" + encodeURIComponent(obj.state) + "&" +
        "scope=read%20write";
      return window.oauth.launchAuthFlow(url, obj);
    }).then(function(redirectUrl) {
      console.log("Ignoring code: " + redirectUrl);
      // app.onOAuthToken(redirectUrl);
      // There's a new activity launched with the auth code.
      console.log("Exiting");
      navigator.app.exitApp();
    }).catch(function(err) {
      console.log("launchAuthFlow error: " + err);
      // @todo handle error
    });
  };
  for (var i = 0; i < elts.length; i++) {
    elts[i].addEventListener("click", onclickHandler);
  }
};

<<<<<<< Updated upstream
=======
<<<<<<< Updated upstream
=======
>>>>>>> Stashed changes
app.onOAuthToken = function(responseUrl) {
  console.log("Got token: " + responseUrl);
  var query = responseUrl.substr(responseUrl.indexOf('?') + 1),
    param,
    params = {},
    keys = query.split('&'),
    i = 0;

  for (i = 0; i < keys.length; i += 1) {
    param = keys[i].substr(0, keys[i].indexOf('='));
    params[param] = keys[i].substr(keys[i].indexOf('=') + 1);
  }

<<<<<<< Updated upstream
  document.getElementById("title").appendChild(document.createTextNode(params["token"]));
};

=======
  var provisionServer = require('provision');
  document.getElementById("title").appendChild(document.createTextNode(params["access_token"]));
};

>>>>>>> Stashed changes
>>>>>>> Stashed changes

app.initialize();
<commit_msg>Fix merge error<commit_after>/*globals window, document */

var app = {};
app.REDIRECT_URIS = [
  "http://localhost:10101"
];

app.initialize = function() {
  "use strict";
  // 'load', 'deviceready', 'offline', and 'online'.
  document.addEventListener('deviceready', app.onDeviceReady, false);
};

app.onDeviceReady = function() {
  "use strict";
  var parentElement = document.getElementById('deviceready');
  var listeningElement = parentElement.querySelector('.listening');
  var receivedElement = parentElement.querySelector('.received');
  listeningElement.setAttribute('style', 'display:none;');
  receivedElement.setAttribute('style', 'display:block;');
  console.log('Received Event: deviceready');

  window.oauth.getCode().then(function(code) {
    app.onOAuthToken(code);
  }).catch(function(err) {
    app.setupOAuthListener();
  });

  // For debugging, an exit button
  document.getElementById("exitbutton").addEventListener("click", function(evt) {
    evt.preventDefault();
    navigator.app.exitApp();
  });
};

app.setupOAuthListener = function() {
  "use strict";
  var elts = document.getElementsByClassName("cloudlogo");
  var onclickHandler = function(e) {
    window.oauth.initiateOAuth(app.REDIRECT_URIS).then(function(obj) {
      var url = "https://cloud.digitalocean.com/v1/oauth/authorize?" +
        "client_id=24cb4fd7317204781602be3b19cce72d9258bc59ba08a50b815f65adfc6ca534&" +
        "response_type=token&" +
        "redirect_uri=" + encodeURIComponent(obj.redirect) + "&" +
        "state=" + encodeURIComponent(obj.state) + "&" +
        "scope=read%20write";
      return window.oauth.launchAuthFlow(url, obj);
    }).then(function(redirectUrl) {
      console.log("Ignoring code: " + redirectUrl);
      // app.onOAuthToken(redirectUrl);
      // There's a new activity launched with the auth code.
      console.log("Exiting");
      navigator.app.exitApp();
    }).catch(function(err) {
      console.log("launchAuthFlow error: " + err);
      // @todo handle error
    });
  };
  for (var i = 0; i < elts.length; i++) {
    elts[i].addEventListener("click", onclickHandler);
  }
};

app.onOAuthToken = function(responseUrl) {
  console.log("Got token: " + responseUrl);
  var query = responseUrl.substr(responseUrl.indexOf('?') + 1),
    param,
    params = {},
    keys = query.split('&'),
    i = 0;

  for (i = 0; i < keys.length; i += 1) {
    param = keys[i].substr(0, keys[i].indexOf('='));
    params[param] = keys[i].substr(keys[i].indexOf('=') + 1);
  }

  var provisionServer = require('provision');
  document.getElementById("title").appendChild(document.createTextNode(params["access_token"]));
};

app.initialize();
<|endoftext|>


<commit_before>/**
 * Created by tdiprima.
 */
log('Hello from mugshots.js');

mugshots = function () {
    log('location: ' + location);
    var url = '';
    var hash = '';
    if (location.hash.length > 1) {
        hash = location.hash.slice(1);

        mugshots.n = getQueryVariable('n', hash);
        mugshots.m = getQueryVariable('m', hash);

        url = buildQueryString(hash);

    }
    else {
        // Default data url
        var rand = Math.random();

        var _static = config.quot + 'provenance.analysis.execution_id' + config.quot + ':' + config.quot + config.analysis_execution_id + config.quot;
        url = mugshots.findApi + "?collection=objects&limit=100&find={" + _static + ',' + config.quot + "randval" + config.quot + ":{" + config.quot + "$gte" + config.quot + ":" + rand + "}}";
        log('*** Random nuclei were selected for you. ***');

    }

    log('url: ' + url);

    mugshots.loadData(url);
};

function buildQueryString(q) {

    var caseid = getQueryVariable('caseid', q);
    var fx = getQueryVariable('fx', q);
    var xmin = getQueryVariable('xmin', q);
    var xmax = getQueryVariable('xmax', q);
    var fy = getQueryVariable('fy', q);
    var ymin = getQueryVariable('ymin', q);
    var ymax = getQueryVariable('ymax', q);

    // Remember it stops at '='
    // https://falcon.bmi.stonybrook.edu:4500/?limit
    var base = getQueryVariable('url', q);
    base = base + '=500';

    // "find1: {"randval":{"$gte":0.399},"provenance.analysis.execution_id":"luad:20160215","provenance.image.case_id":"TCGA-05-4244-01Z-00-DX1"}"
    var find = getQueryVariable('find', q);
    // remove the last '}'
    find = find.substring(0, find.length - 1);

    // Build our "find"

    var range_a = config.quot + '$and' + config.quot + ':[{'
        + config.quot + 'properties.scalar_features' + config.quot + ':{'
        + config.quot + '$elemMatch' + config.quot + ':{'
        + config.quot + 'nv' + config.quot + ':{'
        + config.quot + '$elemMatch' + config.quot + ':{'
        + config.quot + 'name' + config.quot + ':' + config.quot + fx + config.quot + ','
        + config.quot + 'value' + config.quot + ':{'
        + config.quot + '$gte' + config.quot + ':' + xmin +
        '}}}}}},{'
        + config.quot + 'properties.scalar_features' + config.quot + ':{'
        + config.quot + '$elemMatch' + config.quot + ':{'
        + config.quot + 'nv' + config.quot + ':{'
        + config.quot + '$elemMatch' + config.quot + ':{'
        + config.quot + 'name' + config.quot + ':' + config.quot + fx + config.quot + ','
        + config.quot + 'value' + config.quot + ':{'
        + config.quot + '$lte' + config.quot + ':' + xmax +
        '}}}}}}]';

    var range_b = config.quot + '$and' + config.quot + ':[{'
        + config.quot + 'properties.scalar_features' + config.quot + ':{'
        + config.quot + '$elemMatch' + config.quot + ':{'
        + config.quot + 'nv' + config.quot + ':{'
        + config.quot + '$elemMatch' + config.quot + ':{'
        + config.quot + 'name' + config.quot + ':' + config.quot + fy + config.quot + ','
        + config.quot + 'value' + config.quot + ':{'
        + config.quot + '$gte' + config.quot + ':' + ymin +
        '}}}}}},{'
        + config.quot + 'properties.scalar_features' + config.quot + ':{'
        + config.quot + '$elemMatch' + config.quot + ':{'
        + config.quot + 'nv' + config.quot + ':{'
        + config.quot + '$elemMatch' + config.quot + ':{'
        + config.quot + 'name' + config.quot + ':' + config.quot + fy + config.quot + ','
        + config.quot + 'value' + config.quot + ':{'
        + config.quot + '$lte' + config.quot + ':' + ymax +
        '}}}}}}]';

    var myUrl = base + '&find=' + find + ',' + config.quot + '$and' + config.quot + ':[{' + range_a + '},{' + range_b + '}]}';
    if (config.mongoUrl) {
        myUrl = myUrl + '&mongoUrl=' + config.mongoUrl;

    }

    return myUrl;

}

function getQueryVariable(variable, queryString) {
    var vars = queryString.split('&');
    for (var i = 0; i < vars.length; i++) {
        var pair = vars[i].split('=');
        if (decodeURIComponent(pair[0]) == variable) {
            return decodeURIComponent(pair[1]);
        }
    }
}

mugshots.loadData = function (url) {
    /*
     $.getJSON(url)
     .done(function (data) {
     log("Got JSON Data");
     //document.write(JSON.stringify(data));
     mugshots.draw('mugshotDiv', data);

     })
     .error(function (jqXHR, textStatus, error) {
     log('jqXHR.responseText: ' + jqXHR.responseText);
     var err = textStatus + ", " + error;
     log("Request Failed: " + err);
     });
     // .fail in jQuery 1.8
     */
    $.ajax({
        url: url,
        async: true,
        dataType: 'json',
        success: function (json) {
            mugshots.draw('mugshotDiv', json);
        }
    });
};

function getRandomSubarrayPartialShuffle(arr, size) {
    var shuffled = arr.slice(0),
        i = arr.length,
        min = i - size,
        temp,
        index;
    while (i-- > min) {
        index = Math.floor((i + 1) * Math.random());
        temp = shuffled[index];
        shuffled[index] = shuffled[i];
        shuffled[i] = temp;
    }
    return shuffled.slice(min);
}

mugshots.fun = function (data, size) {
    var newData = [];

    var randomMembers = getRandomSubarrayPartialShuffle(data, size);
    log(randomMembers);

    var sameCaseId = true;
    var prevCaseId = '';
    randomMembers.forEach(function (doc) {
        if (prevCaseId != '' && prevCaseId != doc.provenance.image.case_id) {
            sameCaseId = false;
        }
        prevCaseId = doc.provenance.image.case_id;
    });

    var identifier = '';
    var slideWidth = 0;
    var slideHeight = 0;

    if (sameCaseId) {
        var url = mugshots.findApi + '?collection=' + config.imgcoll + '&limit=1&find={' + config.quot + 'caseid' + config.quot + ':' + config.quot + prevCaseId + config.quot + '}';
        $.ajax({
            url: url,
            async: false,
            dataType: 'json',
            success: function (json) {
                identifier = json[0].filename;
                slideWidth = json[0].width;
                slideHeight = json[0].height;
            }
        });

    }

    randomMembers.forEach(function (doc) {
        var url = mugshots.findApi + '?collection=' + config.imgcoll + '&limit=1&find={' + config.quot + 'caseid' + config.quot + ':' + config.quot + doc.provenance.image.case_id + config.quot + '}';

        if (!sameCaseId) {
            $.ajax({
                url: url,
                async: false,
                dataType: 'json',
                success: function (json) {
                    identifier = json[0].filename;
                    slideWidth = json[0].width;
                    slideHeight = json[0].height;
                }
            });

        }

        newData.push({
            markup: doc.bbox,
            normalized: doc.normalized,
            caseid: doc.provenance.image.case_id,
            identifier: identifier,
            slideWidth: slideWidth,
            slideHeight: slideHeight
        })
    });

    return newData;

};

mugshots.draw = function (targetDiv, data, layout) {

    if (!layout) {
        layout = {
            title: 'Nuclei Mugshots',
            rows: 3,
            columns: 4
        };
    }

    //m rows and n columns
    if (!mugshots.m) {
        mugshots.m = layout.rows;
    }
    if (!mugshots.n) {
        mugshots.n = layout.columns;
    }
    size = mugshots.m * mugshots.n;

    if (!document.getElementById(targetDiv)) {
        $('

').appendTo(document.body);
    }

    if (!data) {
        document.getElementById(targetDiv).innerHTML = 'Alright now... you need to give me some data in order to get some nuclei!';
        return false;
    }
    else {
        data = mugshots.fun(data, size);
    }

    var div = document.getElementById(targetDiv);

    var k = 0, tableRows = mugshots.m,
        tds = mugshots.n,
        fragment = document.createDocumentFragment();

    var tbl = document.createElement('table');
    fragment.appendChild(tbl);

    var ind = 0;
    for (var i = 0; i < tableRows; i++) {
        //str += '
';
        var row = document.createElement('tr');
        tbl.appendChild(row);

        for (var j = 0; j < tds; j++) {
            //log(ind + ': ' + JSON.stringify(data[ind]));

            var obj = {
                x: data[ind].markup[0],
                y: data[ind].markup[1],
                w: (data[ind].markup[2] - data[ind].markup[0]),
                h: (data[ind].markup[3] - data[ind].markup[1])
            };

            var normalized = data[ind].normalized;
            if (normalized === 'true') {
                obj.x = obj.x * data[ind].slideWidth;
                obj.y = obj.y * data[ind].slideHeight;
                obj.w = obj.w * data[ind].slideWidth;
                obj.h = obj.h * data[ind].slideHeight;
            }

            // IIIF wants whole numbers.
            obj.x = Math.round(obj.x);
            obj.y = Math.round(obj.y);
            obj.w = Math.round(obj.w);
            obj.h = Math.round(obj.h);

            // IIIF Image Request URI Syntax
            var scheme = 'http',
                server = 'quip.bmi.stonybrook.edu',
                prefix = 'fastcgi-bin/iipsrv.fcgi?iiif=',
                region = '',
                size = 'full',
                rotation = '0',
                quality = 'default',
                format = 'jpg';

            var quipUrl = config.quipUrl + '?tissueId=';

            var canvas = document.createElement('canvas');
            canvas.width = 150;
            canvas.height = 150;

            // Expand bounding box, with nucleus in center
            var w = (canvas.width / 2),
                h = (canvas.height / 2);

            var new_x = ((obj.x + (obj.w / 2)) - w),
                new_y = ((obj.y + (obj.h / 2)) - h);

            region = new_x + "," + new_y + "," + canvas.width + "," + canvas.height;

            var uri = scheme + "://" + server + "/" + prefix + data[ind].identifier + "/" + region + "/" + size + "/" + rotation + "/" + quality + "." + format;

            var link = document.createElement('a');
            link.setAttribute("href", quipUrl + data[ind].caseid + "&x=" + obj.x + "&y=" + obj.y);
            link.setAttribute("target", "_blank");
            var col = document.createElement('td');

            canvas.setAttribute('id', 'canvas' + j);
            link.appendChild(canvas);
            col.appendChild(link);
            var context = canvas.getContext("2d");

            var imgSrc = uri;

            context.clearRect(0, 0, canvas.width, canvas.height);
            drawBackground(canvas, context, imgSrc, obj);
            row.appendChild(col);

            ind = ind + 1;
        }
    }

    // Write the table to the html div.
    div.appendChild(fragment);

    // Clean hash.
    location.hash = '';

};

function drawBackground(canvas, context, imgSrc, obj) {

    var imagePaper = new Image();

    imagePaper.onload = function () {
        context.drawImage(imagePaper, 0, 0);
        drawLines(canvas, context, obj);
    };
    imagePaper.src = imgSrc;
}

function drawLines(canvas, context, obj) {
    var x = (canvas.width / 2),
        y = (canvas.height / 2);

    var new_x = (x - (obj.w / 2)),
        new_y = (y - (obj.h / 2));

    // yellow rectangle
    context.beginPath();
    context.lineWidth = "2";
    context.strokeStyle = "yellow";
    context.rect(new_x, new_y, obj.w, obj.h);
    context.stroke();
}

// ini
$(document).ready(function () {
    mugshots.findApi = config.findAPI + ':' + config.port + '/';
    mugshots();
});
<commit_msg>Demo 12.<commit_after>/**
 * Created by tdiprima.
 */
log('Hello from mugshots.js');

mugshots = function () {
    log('location: ' + location);
    var url = '';
    var hash = '';
    if (location.hash.length > 1) {
        hash = location.hash.slice(1);

        mugshots.n = getQueryVariable('n', hash);
        mugshots.m = getQueryVariable('m', hash);

        url = buildQueryString(hash);

    }
    else {
        // Default data url
        var rand = Math.random();

        var _static = config.quot + 'provenance.analysis.execution_id' + config.quot + ':' + config.quot + config.analysis_execution_id + config.quot;
        url = mugshots.findApi + "?collection=objects&limit=12&find={" + _static + ',' + config.quot + "randval" + config.quot + ":{" + config.quot + "$gte" + config.quot + ":" + rand + "}}";
        log('*** Random nuclei were selected for you. ***');

    }

    log('url: ' + url);

    mugshots.loadData(url);
};

function buildQueryString(q) {

    var caseid = getQueryVariable('caseid', q);
    var fx = getQueryVariable('fx', q);
    var xmin = getQueryVariable('xmin', q);
    var xmax = getQueryVariable('xmax', q);
    var fy = getQueryVariable('fy', q);
    var ymin = getQueryVariable('ymin', q);
    var ymax = getQueryVariable('ymax', q);

    // Remember it stops at '='
    // https://falcon.bmi.stonybrook.edu:4500/?limit
    var base = getQueryVariable('url', q);
    base = base + '=500';

    // "find1: {"randval":{"$gte":0.399},"provenance.analysis.execution_id":"luad:20160215","provenance.image.case_id":"TCGA-05-4244-01Z-00-DX1"}"
    var find = getQueryVariable('find', q);
    // remove the last '}'
    find = find.substring(0, find.length - 1);

    // Build our "find"

    var range_a = config.quot + '$and' + config.quot + ':[{'
        + config.quot + 'properties.scalar_features' + config.quot + ':{'
        + config.quot + '$elemMatch' + config.quot + ':{'
        + config.quot + 'nv' + config.quot + ':{'
        + config.quot + '$elemMatch' + config.quot + ':{'
        + config.quot + 'name' + config.quot + ':' + config.quot + fx + config.quot + ','
        + config.quot + 'value' + config.quot + ':{'
        + config.quot + '$gte' + config.quot + ':' + xmin +
        '}}}}}},{'
        + config.quot + 'properties.scalar_features' + config.quot + ':{'
        + config.quot + '$elemMatch' + config.quot + ':{'
        + config.quot + 'nv' + config.quot + ':{'
        + config.quot + '$elemMatch' + config.quot + ':{'
        + config.quot + 'name' + config.quot + ':' + config.quot + fx + config.quot + ','
        + config.quot + 'value' + config.quot + ':{'
        + config.quot + '$lte' + config.quot + ':' + xmax +
        '}}}}}}]';

    var range_b = config.quot + '$and' + config.quot + ':[{'
        + config.quot + 'properties.scalar_features' + config.quot + ':{'
        + config.quot + '$elemMatch' + config.quot + ':{'
        + config.quot + 'nv' + config.quot + ':{'
        + config.quot + '$elemMatch' + config.quot + ':{'
        + config.quot + 'name' + config.quot + ':' + config.quot + fy + config.quot + ','
        + config.quot + 'value' + config.quot + ':{'
        + config.quot + '$gte' + config.quot + ':' + ymin +
        '}}}}}},{'
        + config.quot + 'properties.scalar_features' + config.quot + ':{'
        + config.quot + '$elemMatch' + config.quot + ':{'
        + config.quot + 'nv' + config.quot + ':{'
        + config.quot + '$elemMatch' + config.quot + ':{'
        + config.quot + 'name' + config.quot + ':' + config.quot + fy + config.quot + ','
        + config.quot + 'value' + config.quot + ':{'
        + config.quot + '$lte' + config.quot + ':' + ymax +
        '}}}}}}]';

    var myUrl = base + '&find=' + find + ',' + config.quot + '$and' + config.quot + ':[{' + range_a + '},{' + range_b + '}]}';
    if (config.mongoUrl) {
        myUrl = myUrl + '&mongoUrl=' + config.mongoUrl;

    }

    return myUrl;

}

function getQueryVariable(variable, queryString) {
    var vars = queryString.split('&');
    for (var i = 0; i < vars.length; i++) {
        var pair = vars[i].split('=');
        if (decodeURIComponent(pair[0]) == variable) {
            return decodeURIComponent(pair[1]);
        }
    }
}

mugshots.loadData = function (url) {
    /*
     $.getJSON(url)
     .done(function (data) {
     log("Got JSON Data");
     //document.write(JSON.stringify(data));
     mugshots.draw('mugshotDiv', data);

     })
     .error(function (jqXHR, textStatus, error) {
     log('jqXHR.responseText: ' + jqXHR.responseText);
     var err = textStatus + ", " + error;
     log("Request Failed: " + err);
     });
     // .fail in jQuery 1.8
     */
    $.ajax({
        url: url,
        async: true,
        dataType: 'json',
        success: function (json) {
            mugshots.draw('mugshotDiv', json);
        }
    });
};

function getRandomSubarrayPartialShuffle(arr, size) {
    var shuffled = arr.slice(0),
        i = arr.length,
        min = i - size,
        temp,
        index;
    while (i-- > min) {
        index = Math.floor((i + 1) * Math.random());
        temp = shuffled[index];
        shuffled[index] = shuffled[i];
        shuffled[i] = temp;
    }
    return shuffled.slice(min);
}

mugshots.fun = function (data, size) {
    var newData = [];

    var randomMembers = getRandomSubarrayPartialShuffle(data, size);
    log(randomMembers);

    var sameCaseId = true;
    var prevCaseId = '';
    randomMembers.forEach(function (doc) {
        if (prevCaseId != '' && prevCaseId != doc.provenance.image.case_id) {
            sameCaseId = false;
        }
        prevCaseId = doc.provenance.image.case_id;
    });

    var identifier = '';
    var slideWidth = 0;
    var slideHeight = 0;

    if (sameCaseId) {
        var url = mugshots.findApi + '?collection=' + config.imgcoll + '&limit=1&find={' + config.quot + 'caseid' + config.quot + ':' + config.quot + prevCaseId + config.quot + '}';
        $.ajax({
            url: url,
            async: false,
            dataType: 'json',
            success: function (json) {
                identifier = json[0].filename;
                slideWidth = json[0].width;
                slideHeight = json[0].height;
            }
        });

    }

    randomMembers.forEach(function (doc) {
        var url = mugshots.findApi + '?collection=' + config.imgcoll + '&limit=1&find={' + config.quot + 'caseid' + config.quot + ':' + config.quot + doc.provenance.image.case_id + config.quot + '}';

        if (!sameCaseId) {
            $.ajax({
                url: url,
                async: false,
                dataType: 'json',
                success: function (json) {
                    identifier = json[0].filename;
                    slideWidth = json[0].width;
                    slideHeight = json[0].height;
                }
            });

        }

        newData.push({
            markup: doc.bbox,
            normalized: doc.normalized,
            caseid: doc.provenance.image.case_id,
            identifier: identifier,
            slideWidth: slideWidth,
            slideHeight: slideHeight
        })
    });

    return newData;

};

mugshots.draw = function (targetDiv, data, layout) {

    if (!layout) {
        layout = {
            title: 'Nuclei Mugshots',
            rows: 3,
            columns: 4
        };
    }

    //m rows and n columns
    if (!mugshots.m) {
        mugshots.m = layout.rows;
    }
    if (!mugshots.n) {
        mugshots.n = layout.columns;
    }
    size = mugshots.m * mugshots.n;

    if (!document.getElementById(targetDiv)) {
        $('

').appendTo(document.body);
    }

    if (!data) {
        document.getElementById(targetDiv).innerHTML = 'Alright now... you need to give me some data in order to get some nuclei!';
        return false;
    }
    else {
        data = mugshots.fun(data, size);
    }

    var div = document.getElementById(targetDiv);

    var k = 0, tableRows = mugshots.m,
        tds = mugshots.n,
        fragment = document.createDocumentFragment();

    var tbl = document.createElement('table');
    fragment.appendChild(tbl);

    var ind = 0;
    for (var i = 0; i < tableRows; i++) {
        //str += '
';
        var row = document.createElement('tr');
        tbl.appendChild(row);

        for (var j = 0; j < tds; j++) {
            //log(ind + ': ' + JSON.stringify(data[ind]));

            var obj = {
                x: data[ind].markup[0],
                y: data[ind].markup[1],
                w: (data[ind].markup[2] - data[ind].markup[0]),
                h: (data[ind].markup[3] - data[ind].markup[1])
            };

            var normalized = data[ind].normalized;
            if (normalized === 'true') {
                obj.x = obj.x * data[ind].slideWidth;
                obj.y = obj.y * data[ind].slideHeight;
                obj.w = obj.w * data[ind].slideWidth;
                obj.h = obj.h * data[ind].slideHeight;
            }

            // IIIF wants whole numbers.
            obj.x = Math.round(obj.x);
            obj.y = Math.round(obj.y);
            obj.w = Math.round(obj.w);
            obj.h = Math.round(obj.h);

            // IIIF Image Request URI Syntax
            var scheme = 'http',
                server = 'quip.bmi.stonybrook.edu',
                prefix = 'fastcgi-bin/iipsrv.fcgi?iiif=',
                region = '',
                size = 'full',
                rotation = '0',
                quality = 'default',
                format = 'jpg';

            var quipUrl = config.quipUrl + '?tissueId=';

            var canvas = document.createElement('canvas');
            canvas.width = 150;
            canvas.height = 150;

            // Expand bounding box, with nucleus in center
            var w = (canvas.width / 2),
                h = (canvas.height / 2);

            var new_x = ((obj.x + (obj.w / 2)) - w),
                new_y = ((obj.y + (obj.h / 2)) - h);

            region = new_x + "," + new_y + "," + canvas.width + "," + canvas.height;

            var uri = scheme + "://" + server + "/" + prefix + data[ind].identifier + "/" + region + "/" + size + "/" + rotation + "/" + quality + "." + format;

            var link = document.createElement('a');
            link.setAttribute("href", quipUrl + data[ind].caseid + "&x=" + obj.x + "&y=" + obj.y);
            link.setAttribute("target", "_blank");
            var col = document.createElement('td');

            canvas.setAttribute('id', 'canvas' + j);
            link.appendChild(canvas);
            col.appendChild(link);
            var context = canvas.getContext("2d");

            var imgSrc = uri;

            context.clearRect(0, 0, canvas.width, canvas.height);
            drawBackground(canvas, context, imgSrc, obj);
            row.appendChild(col);

            ind = ind + 1;
        }
    }

    // Write the table to the html div.
    div.appendChild(fragment);

    // Clean hash.
    location.hash = '';

};

function drawBackground(canvas, context, imgSrc, obj) {

    var imagePaper = new Image();

    imagePaper.onload = function () {
        context.drawImage(imagePaper, 0, 0);
        drawLines(canvas, context, obj);
    };
    imagePaper.src = imgSrc;
}

function drawLines(canvas, context, obj) {
    var x = (canvas.width / 2),
        y = (canvas.height / 2);

    var new_x = (x - (obj.w / 2)),
        new_y = (y - (obj.h / 2));

    // yellow rectangle
    context.beginPath();
    context.lineWidth = "2";
    context.strokeStyle = "yellow";
    context.rect(new_x, new_y, obj.w, obj.h);
    context.stroke();
}

// ini
$(document).ready(function () {
    mugshots.findApi = config.findAPI + ':' + config.port + '/';
    mugshots();
});
<|endoftext|>


<commit_before>ManageIQ.angular.app.service('playbookReusableCodeMixin', playbookReusableCodeMixin);
playbookReusableCodeMixin.$inject = ['API', '$q', 'miqService'];

function playbookReusableCodeMixin(API, $q, miqService) {
  var sortOptions = "&sort_by=name&sort_order=ascending";
  var allApiPromises = [];

  var getSortedHash = function(inputHash) {
    var sortedHash = Object.keys(inputHash)
      .map(function(key) {
        return ({"k": key, "v": inputHash[key]});
      })
      .sort(function(a, b) {
        return a.v.localeCompare(b.v);
      })
      .reduce(function(o, e) {
        o[e.k] = e.v;
        return o;
      }, {});
    return sortedHash;
  };

  var getVerbosityTypes = function() {
    return {
      "0": "0 (Normal)",
      "1": "1 (Verbose)",
      "2": "2 (More Verbose)",
      "3": "3 (Debug)",
      "4": "4 (Connection Debug)",
      "5": "5 (WinRM Debug)",
    };
  };

  var getLogOutputTypes = function() {
    return {
      "on_error": __("On Error"),
      "always": __("Always"),
      "never": __("Never"),
    };
  };

  var checkFormPristine = function(model, modelCopy, form) {
    if (angular.equals(model, modelCopy)) {
      form.$setPristine();
    } else {
      form.$setDirty();
    }
  };

  var getCloudCredentialsforType = function(prefix, typ, vm) {
    // list of cloud credentials based upon selected cloud type
    var url = '/api/authentications?collection_class=' + typ + '&expand=resources&attributes=id,name' + sortOptions;
    allApiPromises.push(API.get(url)
      .then(function(data) {
        vm[prefix + '_cloud_credentials'] = data.resources;
        findObjectForDropDown(prefix, '_cloud_credential', '_cloud_credentials', vm);
      })
      .catch(miqService.handleFailure)
    );
  };

  var findObjectForDropDown = function(prefix, fieldName, listName, vm) {
    vm['_' + prefix + fieldName] = _.find(vm[prefix + listName], {id: vm[vm.model][prefix + fieldName + '_id']});
  };

  var setIfDefined = function(value) {
    return (typeof value !== 'undefined') ? value : '';
  };

  var cloudCredentialsList = function(vm, provisionCredentialId, retirementCredentialId) {
    if (provisionCredentialId) {
      getCredentialType('provisioning', provisionCredentialId, vm);
    } else {
      vm._provisioning_cloud_type = '';
      vm._provisioning_cloud_credential_id = '';
    }
    if (vm[vm.model].retirement_cloud_credential_id !== undefined) {
      if (retirementCredentialId) {
        getCredentialType('retirement', retirementCredentialId, vm);
      } else {
        vm._retirement_cloud_type = '';
        vm._retirement_cloud_credential_id = '';
      }
    }
  };

  var getCredentialType = function(prefix, credentialId, vm) {
    var url = '/api/authentications/' + credentialId;
    allApiPromises.push(API.get(url)
      .then(function(data) {
        vm[prefix + '_cloud_type'] = data.type;
        if (vm.cloudTypes[vm[prefix + '_cloud_type']] !== 'undefined') {
          vm['_' + prefix + '_cloud_type'] = data.type;
          getCloudCredentialsforType(prefix, data.type, vm);
        }
      })
      .catch(miqService.handleFailure)
    );
  };

  // list of service catalogs
  var formOptions = function(vm) {
    miqService.sparkleOn();
    if (vm[vm.model].catalog_id !== undefined) {
      allApiPromises.push(API.get('/api/service_catalogs/?expand=resources&attributes=id,name' + sortOptions)
        .then(function(data) {
          vm.catalogs = data.resources;
          vm.catalogs.unshift({"href": "", "id": "", "name": "<Unassigned>"});
          vm._catalog = _.find(vm.catalogs, {id: vm[vm.model].catalog_id});
        })
        .catch(miqService.handleFailure)
      );
    }

    // list of service dialogs
    if (vm[vm.model].provisioning_dialog_id !== undefined) {
      allApiPromises.push(API.get('/api/service_dialogs/?expand=resources&attributes=id,label&sort_by=label&sort_order=ascending')
        .then(function(data) {
          vm.dialogs = data.resources;
          vm._provisioning_dialog = _.find(vm.dialogs, {id: vm[vm.model].provisioning_dialog_id});
        })
        .catch(miqService.handleFailure)
      );
    }

    // list of repositories
    allApiPromises.push(API.get('/api/configuration_script_sources?collection_class=ManageIQ::Providers::EmbeddedAnsible::AutomationManager::ConfigurationScriptSource&expand=resources&attributes=id,name&filter[]=region_number=' + vm.currentRegion + sortOptions)
      .then(function(data) {
        vm.repositories = data.resources;
        vm._retirement_repository = _.find(vm.repositories, {id: vm[vm.model].retirement_repository_id});
        vm._provisioning_repository = _.find(vm.repositories, {id: vm[vm.model].provisioning_repository_id});
      })
      .catch(miqService.handleFailure)
    );

    // list of machine credentials
    allApiPromises.push(API.get('/api/authentications?collection_class=ManageIQ::Providers::EmbeddedAnsible::AutomationManager::MachineCredential&expand=resources&attributes=id,name' + sortOptions)
      .then(function(data) {
        vm.machine_credentials = data.resources;
        vm._retirement_machine_credential = _.find(vm.machine_credentials, {id: vm[vm.model].retirement_machine_credential_id});
        vm._provisioning_machine_credential = _.find(vm.machine_credentials, {id: vm[vm.model].provisioning_machine_credential_id});
      })
      .catch(miqService.handleFailure)
    );

    // list of network credentials
    allApiPromises.push(API.get('/api/authentications?collection_class=ManageIQ::Providers::EmbeddedAnsible::AutomationManager::NetworkCredential&expand=resources&attributes=id,name' + sortOptions)
      .then(function(data) {
        vm.network_credentials = data.resources;
        vm._retirement_network_credential = _.find(vm.network_credentials, {id: vm[vm.model].retirement_network_credential_id});
        vm._provisioning_network_credential = _.find(vm.network_credentials, {id: vm[vm.model].provisioning_network_credential_id});
      })
      .catch(miqService.handleFailure)
    );
  };

  function retrievedFormData(vm) {
    vm.afterGet = true;
    miqService.sparkleOff();
  }

  // list of cloud credentials
  var formCloudCredentials = function(vm, provisionCredentialId, retirementCredentialId) {
    allApiPromises.push(API.options('/api/authentications')
      .then(function(data) {
        var cloudTypes = {};
        angular.forEach(data.data.credential_types.embedded_ansible_credential_types, function(credObject, credType) {
          if (credObject.type === 'cloud') {
            cloudTypes[credType] = credObject.label;
          }
        });
        vm.cloudTypes = getSortedHash(cloudTypes);
        cloudCredentialsList(vm, provisionCredentialId, retirementCredentialId);
      })
      .catch(miqService.handleFailure)
    );
    $q.all(allApiPromises)
      .then(retrievedFormData(vm));
  };

  // get playbooks for selected repository
  var repositoryChanged = function(vm, prefix, id) {
    API.get('/api/configuration_script_sources/' + id + '/configuration_script_payloads?expand=resources&filter[]=region_number=' + vm.currentRegion + sortOptions)
      .then(function(data) {
        vm[prefix + '_playbooks'] = data.resources;
        // if repository has changed
        if (id !== vm[vm.model][prefix + '_repository_id']) {
          vm[vm.model][prefix + '_playbook_id'] = '';
          vm[vm.model][prefix + '_repository_id'] = id;
          if (vm[vm.model].retirement_remove_resources !== undefined) {
            getRemoveResourcesTypes(vm);
          }
        } else {
          findObjectForDropDown(prefix, '_playbook', '_playbooks', vm);
        }
      })
      .catch(miqService.handleFailure);
  };

  var getRemoveResourcesTypes = function(vm) {
    if (vm[vm.model].retirement_repository_id === undefined || vm[vm.model].retirement_repository_id === '') {
      vm[vm.model].retirement_remove_resources = 'yes_without_playbook';
      vm.remove_resources_types = {
        'No': 'no_without_playbook',
        'Yes': 'yes_without_playbook',
      };
    } else {
      vm[vm.model].retirement_remove_resources = 'no_with_playbook';
      vm.remove_resources_types = {
        'No': 'no_with_playbook',
        'Before Playbook runs': 'pre_with_playbook',
        'After Playbook runs': 'post_with_playbook',
      };
    }
  };

  var cloudTypeChanged = function(vm, prefix, value) {
    var valueChanged = (value !== vm.provisioning_cloud_type);
    if (value) {
      vm.provisioning_cloud_type = value;
    } else {
      vm.provisioning_cloud_type = '';
    }
    if (valueChanged) {
      var typ = vm.provisioning_cloud_type;
      vm[vm.model].provisioning_cloud_credential_id = '';
      getCloudCredentialsforType(prefix, typ, vm);
    }
  };

  return {
    checkFormPristine: checkFormPristine,
    cloudCredentialsList: cloudCredentialsList,
    cloudTypeChanged: cloudTypeChanged,
    getVerbosityTypes: getVerbosityTypes,
    getLogOutputTypes: getLogOutputTypes,
    getSortedHash: getSortedHash,
    getCloudCredentialsforType: getCloudCredentialsforType,
    getRemoveResourcesTypes: getRemoveResourcesTypes,
    findObjectForDropDown: findObjectForDropDown,
    formOptions: formOptions,
    formCloudCredentials: formCloudCredentials,
    repositoryChanged: repositoryChanged,
    setIfDefined: setIfDefined,
  };
}
<commit_msg>Fix to show "Cloud Credentials" drop down on Retirement tab<commit_after>ManageIQ.angular.app.service('playbookReusableCodeMixin', playbookReusableCodeMixin);
playbookReusableCodeMixin.$inject = ['API', '$q', 'miqService'];

function playbookReusableCodeMixin(API, $q, miqService) {
  var sortOptions = "&sort_by=name&sort_order=ascending";
  var allApiPromises = [];

  var getSortedHash = function(inputHash) {
    var sortedHash = Object.keys(inputHash)
      .map(function(key) {
        return ({"k": key, "v": inputHash[key]});
      })
      .sort(function(a, b) {
        return a.v.localeCompare(b.v);
      })
      .reduce(function(o, e) {
        o[e.k] = e.v;
        return o;
      }, {});
    return sortedHash;
  };

  var getVerbosityTypes = function() {
    return {
      "0": "0 (Normal)",
      "1": "1 (Verbose)",
      "2": "2 (More Verbose)",
      "3": "3 (Debug)",
      "4": "4 (Connection Debug)",
      "5": "5 (WinRM Debug)",
    };
  };

  var getLogOutputTypes = function() {
    return {
      "on_error": __("On Error"),
      "always": __("Always"),
      "never": __("Never"),
    };
  };

  var checkFormPristine = function(model, modelCopy, form) {
    if (angular.equals(model, modelCopy)) {
      form.$setPristine();
    } else {
      form.$setDirty();
    }
  };

  var getCloudCredentialsforType = function(prefix, typ, vm) {
    // list of cloud credentials based upon selected cloud type
    var url = '/api/authentications?collection_class=' + typ + '&expand=resources&attributes=id,name' + sortOptions;
    allApiPromises.push(API.get(url)
      .then(function(data) {
        vm[prefix + '_cloud_credentials'] = data.resources;
        findObjectForDropDown(prefix, '_cloud_credential', '_cloud_credentials', vm);
      })
      .catch(miqService.handleFailure)
    );
  };

  var findObjectForDropDown = function(prefix, fieldName, listName, vm) {
    vm['_' + prefix + fieldName] = _.find(vm[prefix + listName], {id: vm[vm.model][prefix + fieldName + '_id']});
  };

  var setIfDefined = function(value) {
    return (typeof value !== 'undefined') ? value : '';
  };

  var cloudCredentialsList = function(vm, provisionCredentialId, retirementCredentialId) {
    if (provisionCredentialId) {
      getCredentialType('provisioning', provisionCredentialId, vm);
    } else {
      vm._provisioning_cloud_type = '';
      vm._provisioning_cloud_credential_id = '';
    }
    if (vm[vm.model].retirement_cloud_credential_id !== undefined) {
      if (retirementCredentialId) {
        getCredentialType('retirement', retirementCredentialId, vm);
      } else {
        vm._retirement_cloud_type = '';
        vm._retirement_cloud_credential_id = '';
      }
    }
  };

  var getCredentialType = function(prefix, credentialId, vm) {
    var url = '/api/authentications/' + credentialId;
    allApiPromises.push(API.get(url)
      .then(function(data) {
        vm[prefix + '_cloud_type'] = data.type;
        if (vm.cloudTypes[vm[prefix + '_cloud_type']] !== 'undefined') {
          vm['_' + prefix + '_cloud_type'] = data.type;
          getCloudCredentialsforType(prefix, data.type, vm);
        }
      })
      .catch(miqService.handleFailure)
    );
  };

  // list of service catalogs
  var formOptions = function(vm) {
    miqService.sparkleOn();
    if (vm[vm.model].catalog_id !== undefined) {
      allApiPromises.push(API.get('/api/service_catalogs/?expand=resources&attributes=id,name' + sortOptions)
        .then(function(data) {
          vm.catalogs = data.resources;
          vm.catalogs.unshift({"href": "", "id": "", "name": "<Unassigned>"});
          vm._catalog = _.find(vm.catalogs, {id: vm[vm.model].catalog_id});
        })
        .catch(miqService.handleFailure)
      );
    }

    // list of service dialogs
    if (vm[vm.model].provisioning_dialog_id !== undefined) {
      allApiPromises.push(API.get('/api/service_dialogs/?expand=resources&attributes=id,label&sort_by=label&sort_order=ascending')
        .then(function(data) {
          vm.dialogs = data.resources;
          vm._provisioning_dialog = _.find(vm.dialogs, {id: vm[vm.model].provisioning_dialog_id});
        })
        .catch(miqService.handleFailure)
      );
    }

    // list of repositories
    allApiPromises.push(API.get('/api/configuration_script_sources?collection_class=ManageIQ::Providers::EmbeddedAnsible::AutomationManager::ConfigurationScriptSource&expand=resources&attributes=id,name&filter[]=region_number=' + vm.currentRegion + sortOptions)
      .then(function(data) {
        vm.repositories = data.resources;
        vm._retirement_repository = _.find(vm.repositories, {id: vm[vm.model].retirement_repository_id});
        vm._provisioning_repository = _.find(vm.repositories, {id: vm[vm.model].provisioning_repository_id});
      })
      .catch(miqService.handleFailure)
    );

    // list of machine credentials
    allApiPromises.push(API.get('/api/authentications?collection_class=ManageIQ::Providers::EmbeddedAnsible::AutomationManager::MachineCredential&expand=resources&attributes=id,name' + sortOptions)
      .then(function(data) {
        vm.machine_credentials = data.resources;
        vm._retirement_machine_credential = _.find(vm.machine_credentials, {id: vm[vm.model].retirement_machine_credential_id});
        vm._provisioning_machine_credential = _.find(vm.machine_credentials, {id: vm[vm.model].provisioning_machine_credential_id});
      })
      .catch(miqService.handleFailure)
    );

    // list of network credentials
    allApiPromises.push(API.get('/api/authentications?collection_class=ManageIQ::Providers::EmbeddedAnsible::AutomationManager::NetworkCredential&expand=resources&attributes=id,name' + sortOptions)
      .then(function(data) {
        vm.network_credentials = data.resources;
        vm._retirement_network_credential = _.find(vm.network_credentials, {id: vm[vm.model].retirement_network_credential_id});
        vm._provisioning_network_credential = _.find(vm.network_credentials, {id: vm[vm.model].provisioning_network_credential_id});
      })
      .catch(miqService.handleFailure)
    );
  };

  function retrievedFormData(vm) {
    vm.afterGet = true;
    miqService.sparkleOff();
  }

  // list of cloud credentials
  var formCloudCredentials = function(vm, provisionCredentialId, retirementCredentialId) {
    allApiPromises.push(API.options('/api/authentications')
      .then(function(data) {
        var cloudTypes = {};
        angular.forEach(data.data.credential_types.embedded_ansible_credential_types, function(credObject, credType) {
          if (credObject.type === 'cloud') {
            cloudTypes[credType] = credObject.label;
          }
        });
        vm.cloudTypes = getSortedHash(cloudTypes);
        cloudCredentialsList(vm, provisionCredentialId, retirementCredentialId);
      })
      .catch(miqService.handleFailure)
    );
    $q.all(allApiPromises)
      .then(retrievedFormData(vm));
  };

  // get playbooks for selected repository
  var repositoryChanged = function(vm, prefix, id) {
    API.get('/api/configuration_script_sources/' + id + '/configuration_script_payloads?expand=resources&filter[]=region_number=' + vm.currentRegion + sortOptions)
      .then(function(data) {
        vm[prefix + '_playbooks'] = data.resources;
        // if repository has changed
        if (id !== vm[vm.model][prefix + '_repository_id']) {
          vm[vm.model][prefix + '_playbook_id'] = '';
          vm[vm.model][prefix + '_repository_id'] = id;
          if (vm[vm.model].retirement_remove_resources !== undefined) {
            getRemoveResourcesTypes(vm);
          }
        } else {
          findObjectForDropDown(prefix, '_playbook', '_playbooks', vm);
        }
      })
      .catch(miqService.handleFailure);
  };

  var getRemoveResourcesTypes = function(vm) {
    if (vm[vm.model].retirement_repository_id === undefined || vm[vm.model].retirement_repository_id === '') {
      vm[vm.model].retirement_remove_resources = 'yes_without_playbook';
      vm.remove_resources_types = {
        'No': 'no_without_playbook',
        'Yes': 'yes_without_playbook',
      };
    } else {
      vm[vm.model].retirement_remove_resources = 'no_with_playbook';
      vm.remove_resources_types = {
        'No': 'no_with_playbook',
        'Before Playbook runs': 'pre_with_playbook',
        'After Playbook runs': 'post_with_playbook',
      };
    }
  };

  var cloudTypeChanged = function(vm, prefix, value) {
    var valueChanged = (value !== vm[prefix + "_cloud_type"]);
    if (value) {
      vm[prefix + "_cloud_type"] = value;
    } else {
      vm[prefix + "_cloud_type"] = '';
    }
    if (valueChanged) {
      var typ = vm[prefix + "_cloud_type"];
      vm[vm.model][prefix + "_cloud_credential_id"] = '';
      getCloudCredentialsforType(prefix, typ, vm);
    }
  };

  return {
    checkFormPristine: checkFormPristine,
    cloudCredentialsList: cloudCredentialsList,
    cloudTypeChanged: cloudTypeChanged,
    getVerbosityTypes: getVerbosityTypes,
    getLogOutputTypes: getLogOutputTypes,
    getSortedHash: getSortedHash,
    getCloudCredentialsforType: getCloudCredentialsforType,
    getRemoveResourcesTypes: getRemoveResourcesTypes,
    findObjectForDropDown: findObjectForDropDown,
    formOptions: formOptions,
    formCloudCredentials: formCloudCredentials,
    repositoryChanged: repositoryChanged,
    setIfDefined: setIfDefined,
  };
}
<|endoftext|>


<commit_before>/* global Vue, Flash, gl */
/* eslint-disable no-param-reassign */

((gl) => {
  gl.VuePipelineActions = Vue.extend({
    props: ['pipeline', 'svgs'],
    computed: {
      actions() {
        return this.pipeline.details.manual_actions.length > 0;
      },
      artifacts() {
        return this.pipeline.details.artifacts.length > 0;
      },
    },
    methods: {
      download(name) {
        return `Download ${name} artifacts`;
      },
    },
    template: `

              <button
                v-if='actions'
                class="dropdown-toggle btn btn-default has-tooltip js-pipeline-dropdown-manual-actions"
                data-toggle="dropdown"
                title="Manual build"
                data-placement="top"
                data-toggle="dropdown"
                aria-label="Manual build"
              >

              </button>

                    {{action.name}}

              <button
                v-if='artifacts'
                class="dropdown-toggle btn btn-default build-artifacts has-tooltip js-pipeline-dropdown-download"
                data-toggle="dropdown"
                title="Artifacts"
                data-placement="top"
                data-toggle="dropdown"
                aria-label="Artifacts"
              >

              </button>

                    {{download(artifact.name)}}

    `,
  });
})(window.gl || (window.gl = {}));
<commit_msg>remove redundant "data-toggle" attribute so Vue doesn't complain<commit_after>/* global Vue, Flash, gl */
/* eslint-disable no-param-reassign */

((gl) => {
  gl.VuePipelineActions = Vue.extend({
    props: ['pipeline', 'svgs'],
    computed: {
      actions() {
        return this.pipeline.details.manual_actions.length > 0;
      },
      artifacts() {
        return this.pipeline.details.artifacts.length > 0;
      },
    },
    methods: {
      download(name) {
        return `Download ${name} artifacts`;
      },
    },
    template: `

              <button
                v-if='actions'
                class="dropdown-toggle btn btn-default has-tooltip js-pipeline-dropdown-manual-actions"
                data-toggle="dropdown"
                title="Manual build"
                data-placement="top"
                aria-label="Manual build"
              >

              </button>

                    {{action.name}}

              <button
                v-if='artifacts'
                class="dropdown-toggle btn btn-default build-artifacts has-tooltip js-pipeline-dropdown-download"
                data-toggle="dropdown"
                title="Artifacts"
                data-placement="top"
                aria-label="Artifacts"
              >

              </button>

                    {{download(artifact.name)}}

    `,
  });
})(window.gl || (window.gl = {}));
<|endoftext|>


<commit_before>import SubmissionPreview from '@/submissions/components/SubmissionPreview/SubmissionPreview.vue'
import { shallowMount, createLocalVue } from '@vue/test-utils'

const localVue = createLocalVue()

describe('filterBlankRows', () => {
  it('Removes blank rows and objects containing only empty strings for tables', () => {
    const wrapper = shallowMount(SubmissionPreview, {
      localVue,
      propsData: {
        form: {},
        sections: {}
      }
    })
    let rows = [
      { data: 123 },
      { data: '' },
      {}
    ]

    const filteredRows = wrapper.vm.filterBlankRows(rows)

    expect(filteredRows.length).toBe(1)
  })
  it('Does not remove rows with values for tables', () => {
    const wrapper = shallowMount(SubmissionPreview, {
      localVue,
      propsData: {
        form: {},
        sections: {}
      }
    })
    let rows = [
      { data: 123 },
      { data: 456 },
      { data: 789 }
    ]

    const filteredRows = wrapper.vm.filterBlankRows(rows)

    expect(filteredRows.length).toBe(3)
  })
})
<commit_msg>add store into test that calls getters<commit_after>import Vuex from 'vuex'
import SubmissionPreview from '@/submissions/components/SubmissionPreview/SubmissionPreview.vue'
import { shallowMount, createLocalVue } from '@vue/test-utils'

const localVue = createLocalVue()
localVue.use(Vuex)

describe('filterBlankRows', () => {
  let store
  let getters

  beforeEach(() => {
    getters = {
      codes: () => ({})
    }
    store = new Vuex.Store({
      getters
    })
  })
  it('Removes blank rows and objects containing only empty strings for tables', () => {
    const wrapper = shallowMount(SubmissionPreview, {
      localVue,
      store,
      propsData: {
        form: {},
        sections: {}
      }
    })
    let rows = [
      { data: 123 },
      { data: '' },
      {}
    ]

    const filteredRows = wrapper.vm.filterBlankRows(rows)

    expect(filteredRows.length).toBe(1)
  })
  it('Does not remove rows with values for tables', () => {
    const wrapper = shallowMount(SubmissionPreview, {
      localVue,
      store,
      propsData: {
        form: {},
        sections: {}
      }
    })
    let rows = [
      { data: 123 },
      { data: 456 },
      { data: 789 }
    ]

    const filteredRows = wrapper.vm.filterBlankRows(rows)

    expect(filteredRows.length).toBe(3)
  })
})
<|endoftext|>