<commit_before>import React from 'react';
import ReactDOM from 'react-dom';
import PureRenderMixin from 'react-addons-pure-render-mixin';
import Popover from '../Popover/Popover';
import CheckIcon from '../svg-icons/navigation/check';
import ListItem from '../List/ListItem';
import Menu from '../Menu/Menu';

const nestedMenuStyle = {
  position: 'relative',
};

const MenuItem = React.createClass({

  propTypes: {
    /**
     * If true, a left check mark will be rendered.
     */
    checked: React.PropTypes.bool,

    /**
     * Elements passed as children to the underlying `ListItem`.
     */
    children: React.PropTypes.node,

    /**
     * @ignore
     * If true, the menu item will render with compact desktop
     * styles.
     */
    desktop: React.PropTypes.bool,

    /**
     * If true, the menu item will be disabled.
     */
    disabled: React.PropTypes.bool,

    /**
     * The focus state of the menu item. This prop is used to set the focus
     * state of the underlying `ListItem`.
     */
    focusState: React.PropTypes.oneOf([
      'none',
      'focused',
      'keyboard-focused',
    ]),

    /**
     * Override the inline-styles of the inner div.
     */
    innerDivStyle: React.PropTypes.object,

    /**
     * If true, the children will be indented.
     * This is only needed when there is no `leftIcon`.
     */
    insetChildren: React.PropTypes.bool,

    /**
     * The `SvgIcon` or `FontIcon` to be displayed on the left side.
     */
    leftIcon: React.PropTypes.element,

    /**
     * `MenuItem` elements to nest within the menu item.
     */
    menuItems: React.PropTypes.node,

    /**
     * Callback function fired when the menu item is touch-tapped.
     *
     * @param {object} event TouchTap event targeting the menu item.
     */
    onTouchTap: React.PropTypes.func,

    /**
     * Can be used to render primary text within the menu item.
     */
    primaryText: React.PropTypes.node,

    /**
     * The `SvgIcon` or `FontIcon` to be displayed on the right side.
     */
    rightIcon: React.PropTypes.element,

    /**
     * Can be used to render secondary text within the menu item.
     */
    secondaryText: React.PropTypes.node,

    /**
     * Override the inline-styles of the root element.
     */
    style: React.PropTypes.object,

    /**
     * The value of the menu item.
     */
    value: React.PropTypes.any,
  },

  contextTypes: {
    muiTheme: React.PropTypes.object.isRequired,
  },

  mixins: [
    PureRenderMixin,
  ],

  getDefaultProps() {
    return {
      checked: false,
      desktop: false,
      disabled: false,
      focusState: 'none',
      insetChildren: false,
    };
  },

  getInitialState() {
    return {
      open: false,
    };
  },

  componentDidMount() {
    this._applyFocusState();
  },

  componentWillReceiveProps(nextProps) {
    if (this.state.open && nextProps.focusState === 'none') {
      this.handleRequestClose();
    }
  },

  componentDidUpdate() {
    this._applyFocusState();
  },

  componentWillUnmount() {
    if (this.state.open) {
      this.setState({
        open: false,
      });
    }
  },

  _applyFocusState() {
    this.refs.listItem.applyFocusState(this.props.focusState);
  },

  _cloneMenuItem(item) {
    return React.cloneElement(item, {
      onTouchTap: (event) => {
        if (!item.props.menuItems) {
          this.handleRequestClose();
        }

        if (item.props.onTouchTap) {
          item.props.onTouchTap(event);
        }
      },
      onRequestClose: this.handleRequestClose,
    });
  },

  handleTouchTap(event) {
    event.preventDefault();

    this.setState({
      open: true,
      anchorEl: ReactDOM.findDOMNode(this),
    });

    if (this.props.onTouchTap) {
      this.props.onTouchTap(event);
    }
  },

  handleRequestClose() {
    this.setState({
      open: false,
      anchorEl: null,
    });
  },

  render() {
    const {
      checked,
      children,
      desktop,
      disabled,
      focusState, // eslint-disable-line no-unused-vars
      innerDivStyle,
      insetChildren,
      leftIcon,
      menuItems,
      rightIcon,
      secondaryText,
      style,
      value, // eslint-disable-line no-unused-vars
      ...other,
    } = this.props;

    const {
      prepareStyles,
      menuItem,
    } = this.context.muiTheme;

    const disabledColor = this.context.muiTheme.rawTheme.palette.disabledColor;
    const textColor = this.context.muiTheme.rawTheme.palette.textColor;
    const leftIndent = desktop ? 64 : 72;
    const sidePadding = desktop ? 24 : 16;

    const styles = {
      root: {
        color: disabled ? disabledColor : textColor,
        lineHeight: desktop ? '32px' : '48px',
        fontSize: desktop ? 15 : 16,
        whiteSpace: 'nowrap',
      },

      innerDivStyle: {
        paddingLeft: leftIcon || insetChildren || checked ? leftIndent : sidePadding,
        paddingRight: sidePadding,
        paddingBottom: 0,
        paddingTop: 0,
        display: 'flex',
        justifyContent: 'space-between',
        alignContent: 'space-between',
      },

      secondaryText: {
        order: 2,
        overflow: 'hidden',
        textOverflow: 'ellipsis',
        whiteSpace: 'nowrap',
      },

      leftIconDesktop: {
        margin: 0,
        left: 24,
        top: 4,
      },

      rightIconDesktop: {
        margin: 0,
        right: 24,
        top: 4,
        fill: menuItem.rightIconDesktopFill,
      },
    };

    const mergedRootStyles = Object.assign(styles.root, style);
    const mergedInnerDivStyles = Object.assign(styles.innerDivStyle, innerDivStyle);

    //Left Icon
    let leftIconElement = leftIcon ? leftIcon : checked ? <CheckIcon /> : null;
    if (leftIconElement && desktop) {
      const mergedLeftIconStyles = Object.assign(styles.leftIconDesktop, leftIconElement.props.style);
      leftIconElement = React.cloneElement(leftIconElement, {style: mergedLeftIconStyles});
    }

    //Right Icon
    let rightIconElement;
    if (rightIcon) {
      const mergedRightIconStyles = desktop ?
        Object.assign(styles.rightIconDesktop, rightIcon.props.style) : rightIcon.props.style;
      rightIconElement = React.cloneElement(rightIcon, {style: mergedRightIconStyles});
    }

    //Secondary Text
    let secondaryTextElement;
    if (secondaryText) {
      const secondaryTextIsAnElement = React.isValidElement(secondaryText);
      const mergedSecondaryTextStyles = secondaryTextIsAnElement ?
      Object.assign(styles.secondaryText, secondaryText.props.style) : null;

      secondaryTextElement = secondaryTextIsAnElement ?
        React.cloneElement(secondaryText, {style: mergedSecondaryTextStyles}) :

{secondaryText}
;
    }
    let childMenuPopover;
    if (menuItems) {
      childMenuPopover = (
        <Popover
          anchorOrigin={{horizontal: 'right', vertical: 'top'}}
          anchorEl={this.state.anchorEl}
          open={this.state.open}
          useLayerForClickAway={false}
          onRequestClose={this.handleRequestClose}
        >
          <Menu desktop={desktop} disabled={disabled} style={nestedMenuStyle}>
            {React.Children.map(menuItems, this._cloneMenuItem)}
          </Menu>
        </Popover>
      );
      other.onTouchTap = this.handleTouchTap;
    }

    return (
      <ListItem
        {...other}
        disabled={disabled}
        innerDivStyle={mergedInnerDivStyles}
        insetChildren={insetChildren}
        leftIcon={leftIconElement}
        ref="listItem"
        rightIcon={rightIconElement}
        style={mergedRootStyles}
      >
        {children}
        {secondaryTextElement}
        {childMenuPopover}
      </ListItem>
    );
  },

});

export default MenuItem;
<commit_msg>[MenuItem] Add getStyles()<commit_after>import React from 'react';
import ReactDOM from 'react-dom';
import PureRenderMixin from 'react-addons-pure-render-mixin';
import Popover from '../Popover/Popover';
import CheckIcon from '../svg-icons/navigation/check';
import ListItem from '../List/ListItem';
import Menu from '../Menu/Menu';

const nestedMenuStyle = {
  position: 'relative',
};

function getStyles(props, context) {
  const disabledColor = context.muiTheme.baseTheme.palette.disabledColor;
  const textColor = context.muiTheme.baseTheme.palette.textColor;
  const leftIndent = props.desktop ? 64 : 72;
  const sidePadding = props.desktop ? 24 : 16;

  const styles = {
    root: {
      color: props.disabled ? disabledColor : textColor,
      lineHeight: props.desktop ? '32px' : '48px',
      fontSize: props.desktop ? 15 : 16,
      whiteSpace: 'nowrap',
    },

    innerDivStyle: {
      paddingLeft: props.leftIcon || props.insetChildren || props.checked ? leftIndent : sidePadding,
      paddingRight: sidePadding,
      paddingBottom: 0,
      paddingTop: 0,
      display: 'flex',
      justifyContent: 'space-between',
      alignContent: 'space-between',
    },

    secondaryText: {
      order: 2,
      overflow: 'hidden',
      textOverflow: 'ellipsis',
      whiteSpace: 'nowrap',
    },

    leftIconDesktop: {
      margin: 0,
      left: 24,
      top: 4,
    },

    rightIconDesktop: {
      margin: 0,
      right: 24,
      top: 4,
      fill: context.muiTheme.menuItem.rightIconDesktopFill,
    },
  };

  return styles;
}

const MenuItem = React.createClass({

  propTypes: {
    /**
     * If true, a left check mark will be rendered.
     */
    checked: React.PropTypes.bool,

    /**
     * Elements passed as children to the underlying `ListItem`.
     */
    children: React.PropTypes.node,

    /**
     * @ignore
     * If true, the menu item will render with compact desktop
     * styles.
     */
    desktop: React.PropTypes.bool,

    /**
     * If true, the menu item will be disabled.
     */
    disabled: React.PropTypes.bool,

    /**
     * The focus state of the menu item. This prop is used to set the focus
     * state of the underlying `ListItem`.
     */
    focusState: React.PropTypes.oneOf([
      'none',
      'focused',
      'keyboard-focused',
    ]),

    /**
     * Override the inline-styles of the inner div.
     */
    innerDivStyle: React.PropTypes.object,

    /**
     * If true, the children will be indented.
     * This is only needed when there is no `leftIcon`.
     */
    insetChildren: React.PropTypes.bool,

    /**
     * The `SvgIcon` or `FontIcon` to be displayed on the left side.
     */
    leftIcon: React.PropTypes.element,

    /**
     * `MenuItem` elements to nest within the menu item.
     */
    menuItems: React.PropTypes.node,

    /**
     * Callback function fired when the menu item is touch-tapped.
     *
     * @param {object} event TouchTap event targeting the menu item.
     */
    onTouchTap: React.PropTypes.func,

    /**
     * Can be used to render primary text within the menu item.
     */
    primaryText: React.PropTypes.node,

    /**
     * The `SvgIcon` or `FontIcon` to be displayed on the right side.
     */
    rightIcon: React.PropTypes.element,

    /**
     * Can be used to render secondary text within the menu item.
     */
    secondaryText: React.PropTypes.node,

    /**
     * Override the inline-styles of the root element.
     */
    style: React.PropTypes.object,

    /**
     * The value of the menu item.
     */
    value: React.PropTypes.any,
  },

  contextTypes: {
    muiTheme: React.PropTypes.object.isRequired,
  },

  mixins: [
    PureRenderMixin,
  ],

  getDefaultProps() {
    return {
      checked: false,
      desktop: false,
      disabled: false,
      focusState: 'none',
      insetChildren: false,
    };
  },

  getInitialState() {
    return {
      open: false,
    };
  },

  componentDidMount() {
    this._applyFocusState();
  },

  componentWillReceiveProps(nextProps) {
    if (this.state.open && nextProps.focusState === 'none') {
      this.handleRequestClose();
    }
  },

  componentDidUpdate() {
    this._applyFocusState();
  },

  componentWillUnmount() {
    if (this.state.open) {
      this.setState({
        open: false,
      });
    }
  },

  _applyFocusState() {
    this.refs.listItem.applyFocusState(this.props.focusState);
  },

  _cloneMenuItem(item) {
    return React.cloneElement(item, {
      onTouchTap: (event) => {
        if (!item.props.menuItems) {
          this.handleRequestClose();
        }

        if (item.props.onTouchTap) {
          item.props.onTouchTap(event);
        }
      },
      onRequestClose: this.handleRequestClose,
    });
  },

  handleTouchTap(event) {
    event.preventDefault();

    this.setState({
      open: true,
      anchorEl: ReactDOM.findDOMNode(this),
    });

    if (this.props.onTouchTap) {
      this.props.onTouchTap(event);
    }
  },

  handleRequestClose() {
    this.setState({
      open: false,
      anchorEl: null,
    });
  },

  render() {
    const {
      checked,
      children,
      desktop,
      disabled,
      focusState, // eslint-disable-line no-unused-vars
      innerDivStyle,
      insetChildren,
      leftIcon,
      menuItems,
      rightIcon,
      secondaryText,
      style,
      value, // eslint-disable-line no-unused-vars
      ...other,
    } = this.props;

    const {prepareStyles} = this.context.muiTheme;
    const styles = getStyles(this.props, this.context);
    const mergedRootStyles = Object.assign(styles.root, style);
    const mergedInnerDivStyles = Object.assign(styles.innerDivStyle, innerDivStyle);

    //Left Icon
    let leftIconElement = leftIcon ? leftIcon : checked ? <CheckIcon /> : null;
    if (leftIconElement && desktop) {
      const mergedLeftIconStyles = Object.assign(styles.leftIconDesktop, leftIconElement.props.style);
      leftIconElement = React.cloneElement(leftIconElement, {style: mergedLeftIconStyles});
    }

    //Right Icon
    let rightIconElement;
    if (rightIcon) {
      const mergedRightIconStyles = desktop ?
        Object.assign(styles.rightIconDesktop, rightIcon.props.style) : rightIcon.props.style;
      rightIconElement = React.cloneElement(rightIcon, {style: mergedRightIconStyles});
    }

    //Secondary Text
    let secondaryTextElement;
    if (secondaryText) {
      const secondaryTextIsAnElement = React.isValidElement(secondaryText);
      const mergedSecondaryTextStyles = secondaryTextIsAnElement ?
      Object.assign(styles.secondaryText, secondaryText.props.style) : null;

      secondaryTextElement = secondaryTextIsAnElement ?
        React.cloneElement(secondaryText, {style: mergedSecondaryTextStyles}) :

{secondaryText}
;
    }
    let childMenuPopover;
    if (menuItems) {
      childMenuPopover = (
        <Popover
          anchorOrigin={{horizontal: 'right', vertical: 'top'}}
          anchorEl={this.state.anchorEl}
          open={this.state.open}
          useLayerForClickAway={false}
          onRequestClose={this.handleRequestClose}
        >
          <Menu desktop={desktop} disabled={disabled} style={nestedMenuStyle}>
            {React.Children.map(menuItems, this._cloneMenuItem)}
          </Menu>
        </Popover>
      );
      other.onTouchTap = this.handleTouchTap;
    }

    return (
      <ListItem
        {...other}
        disabled={disabled}
        innerDivStyle={mergedInnerDivStyles}
        insetChildren={insetChildren}
        leftIcon={leftIconElement}
        ref="listItem"
        rightIcon={rightIconElement}
        style={mergedRootStyles}
      >
        {children}
        {secondaryTextElement}
        {childMenuPopover}
      </ListItem>
    );
  },

});

export default MenuItem;
<|endoftext|>


<commit_before>app.service('campaignService', function() {
	var campaignService = {
		campaignsById: {
			ks2013: {
				done: function() {
					return Boolean(this.deadline() <= 0);
				},
				deadline: function getDeadline() {
					// 1388534400000: Jan 1 2014 GMT
					return (1391197789000 - new Date().getTime());
				}
			}
		}
	}

	return campaignService;
});<commit_msg>updated kickstarter launch date!<commit_after>app.service('campaignService', function() {
	var campaignService = {
		campaignsById: {
			ks2013: {
				done: function() {
					return Boolean(this.deadline() <= 0);
				},
				deadline: function getDeadline() {
					// 1388534400000: Jan 1 2014 GMT
					return (1396310400000 - new Date().getTime());
				}
			}
		}
	}

	return campaignService;
});<|endoftext|>


<commit_before>export const RECEIVED_USER_PROFILE = 'RECEIVED_USER_PROFILE';
export function receivedUserProfile (json) {
    return {
        type: RECEIVED_USER_PROFILE,
        user: json.user
    }
}

/**
 * Action creator fired just before fetching data for a user profile
 * @type {Function}
 */
export const REQUESTING_USER_PROFILE = 'REQUESTING_USER_PROFILE';
export function requestingUserProfile () {
  return {
    type: REQUESTING_USER_PROFILE,
  }
}

/**
 * Thunk action creator, in order to be able to do async actions
 * Redux-thunk middleware let us use action creator that returns a function instead
 * of a plain object. Therefore, the returned function can delay the action dispatches
 * @return {[fn]} [Promise]
 */
export function fetchUserProfile(username) {
  return function (dispatch) {
    dispatch(requestingUserProfile());
    return fetch(`http://localhost:3000/user`)
              .then(response => response.json())
              .then(json => dispatch(receivedUserProfile(json)))
              .catch(e => console.log('error', e));
  }
}
<commit_msg>improve doc on redux-thunk<commit_after>export const RECEIVED_USER_PROFILE = 'RECEIVED_USER_PROFILE';
export function receivedUserProfile (json) {
    return {
        type: RECEIVED_USER_PROFILE,
        user: json.user
    }
}

/**
 * Action creator fired just before fetching data for a user profile
 * @type {Function}
 */
export const REQUESTING_USER_PROFILE = 'REQUESTING_USER_PROFILE';
export function requestingUserProfile () {
  return {
    type: REQUESTING_USER_PROFILE,
  }
}

/**
 * Thunk action creator, in order to be able to do async actions.
 * The redux-thunk middleware let us have access the `dispatch` fn
 * Redux-thunk middleware let us use action creator that returns a function instead
 * of a plain object. Therefore, the returned function can delay the dispatched action
 * @return {[fn]} [Promise]
 */
export function fetchUserProfile(username) {
  return function (dispatch) {
    dispatch(requestingUserProfile());
    return fetch(`http://localhost:3000/user`)
              .then(response => response.json())
              .then(json => dispatch(receivedUserProfile(json)))
              .catch(e => console.log('error', e));
  }
}
<|endoftext|>


<commit_before>import d3 from "d3";
import inflection from "inflection";

var precisionNumberFormatter = d3.format(".2r");
var fixedNumberFormatter = d3.format(",.f");

var decimalDegreesFormatter = d3.format(".08f");

export function formatNumber(number) {
    if (number > -1 && number < 1) {
        // numbers between 1 and -1 round to 2 significant digits with extra 0s stripped off
        return precisionNumberFormatter(number).replace(/\.?0+$/, "");
    } else {
        // anything else rounds to at most 2 decimal points
        return fixedNumberFormatter(d3.round(number, 2));
    }
}

export function formatScalar(scalar) {
    if (typeof scalar === "number") {
        return formatNumber(scalar);
    } else {
        return String(scalar);
    }
}

export function formatCell(value, column) {
    if (value == undefined) {
        return null
    } else if (typeof value === "string") {
        return value;
    } else if (typeof value === "number") {
        if (column && (column.special_type === "latitude" || column.special_type === "longitude")) {
            return decimalDegreesFormatter(value)
        } else {
            return formatNumber(value);
        }
    } else {
        return String(value);
    }
}

export function singularize(...args) {
    return inflection.singularize(...args);
}

export function capitalize(...args) {
    return inflection.capitalize(...args);
}

// Removes trailing "id" from field names
export function stripId(name) {
    return name && name.replace(/ id$/i, "");
}
<commit_msg>Format nested objects as JSON in table view<commit_after>import d3 from "d3";
import inflection from "inflection";

var precisionNumberFormatter = d3.format(".2r");
var fixedNumberFormatter = d3.format(",.f");

var decimalDegreesFormatter = d3.format(".08f");

export function formatNumber(number) {
    if (number > -1 && number < 1) {
        // numbers between 1 and -1 round to 2 significant digits with extra 0s stripped off
        return precisionNumberFormatter(number).replace(/\.?0+$/, "");
    } else {
        // anything else rounds to at most 2 decimal points
        return fixedNumberFormatter(d3.round(number, 2));
    }
}

export function formatScalar(scalar) {
    if (typeof scalar === "number") {
        return formatNumber(scalar);
    } else {
        return String(scalar);
    }
}

export function formatCell(value, column) {
    if (value == undefined) {
        return null
    } else if (typeof value === "string") {
        return value;
    } else if (typeof value === "number") {
        if (column && (column.special_type === "latitude" || column.special_type === "longitude")) {
            return decimalDegreesFormatter(value)
        } else {
            return formatNumber(value);
        }
    } else if (typeof value === "object") {
        // no extra whitespace for table cells
        return JSON.stringify(value);
    } else {
        return String(value);
    }
}

export function singularize(...args) {
    return inflection.singularize(...args);
}

export function capitalize(...args) {
    return inflection.capitalize(...args);
}

// Removes trailing "id" from field names
export function stripId(name) {
    return name && name.replace(/ id$/i, "");
}
<|endoftext|>


<commit_before>import e from 'gEngine/engine'
import * as metricActions from 'gModules/metrics/actions'
import {selectRegion} from 'gModules/selected_region/actions'
import {deSelect} from 'gModules/selected_cell/actions'
import {runSimulations} from 'gModules/simulations/actions'

import {isLocation, isWithinRegion, translate} from 'lib/locationUtils.js'

export function copy(spaceId){
  return (dispatch, getState) => {
    const state = getState()

    const region = state.selectedRegion
    const metrics = state.metrics.filter(m => m.space === spaceId && isWithinRegion(m.location, region))
    const guesstimates = metrics.map(metric => state.guesstimates.find(g => g.metric === metric.id))

    dispatch({type: "COPY", copied: {metrics, guesstimates, block: region}})
  }
}

function translateReadableIds(input, idMap) {
  if (!input) {return ""}
  const re = RegExp(Object.keys(idMap).join("|"), "g")
  return input.replace(re, (match) => idMap[match])
}

export function paste(spaceId){
  return (dispatch, getState) => {
    const state = getState()
    if (!(state.copied && state.selectedCell && isLocation(state.selectedCell))) { return }

    const {metrics, guesstimates, block} = state.copied
    const location = state.selectedCell

    const translateFn = translate(block[0], location)

    const pasteRegion = [location, translateFn(block[1])]

    const spaceMetrics = state.metrics.filter(m => m.space === spaceId)
    let existingReadableIds = spaceMetrics.map(m => m.readableId)

    let readableIdsMap = {}
    const newMetrics = _.map(metrics, metric => {
      const newMetric = Object.assign(
        {},
        metric,
        e.metric.create(existingReadableIds),
        {space: spaceId},
        {location: translateFn(metric.location)}
      )
      existingReadableIds.push(newMetric.readableId)
      readableIdsMap[metric.readableId] = newMetric.readableId
      return newMetric
    })

    const newGuesstimates = _.map(
      guesstimates,
      (guesstimate, i) => Object.assign(
        {},
        guesstimate,
        {metric: newMetrics[i].id},
        {input: translateReadableIds(guesstimate.input, readableIdsMap)}
      )
    )

    const existingMetrics = spaceMetrics.filter(m => isWithinRegion(m.location, pasteRegion))
    existingMetrics.forEach(existingMetric => {dispatch(metricActions.removeMetric(existingMetric.id))})

    newMetrics.forEach((newMetric, i) => {dispatch({type: 'ADD_METRIC', item: newMetric, newGuesstimate: newGuesstimates[i]})})

    dispatch(runSimulations({spaceId, onlyMetrics: newMetrics}))
    dispatch(selectRegion(pasteRegion[0], pasteRegion[1]))
  }
}
<commit_msg>mild rename in copy actions.<commit_after>import e from 'gEngine/engine'
import * as metricActions from 'gModules/metrics/actions'
import {selectRegion} from 'gModules/selected_region/actions'
import {deSelect} from 'gModules/selected_cell/actions'
import {runSimulations} from 'gModules/simulations/actions'

import {isLocation, isWithinRegion, translate} from 'lib/locationUtils.js'

export function copy(spaceId){
  return (dispatch, getState) => {
    const state = getState()

    const region = state.selectedRegion
    const metrics = state.metrics.filter(m => m.space === spaceId && isWithinRegion(m.location, region))
    const guesstimates = metrics.map(metric => state.guesstimates.find(g => g.metric === metric.id))

    dispatch({type: "COPY", copied: {metrics, guesstimates, region}})
  }
}

function translateReadableIds(input, idMap) {
  if (!input) {return ""}
  const re = RegExp(Object.keys(idMap).join("|"), "g")
  return input.replace(re, (match) => idMap[match])
}

export function paste(spaceId){
  return (dispatch, getState) => {
    const state = getState()
    if (!(state.copied && state.selectedCell && isLocation(state.selectedCell))) { return }

    const {metrics, guesstimates, region} = state.copied
    const location = state.selectedCell

    const translateFn = translate(region[0], location)

    const pasteRegion = [location, translateFn(region[1])]

    const spaceMetrics = state.metrics.filter(m => m.space === spaceId)
    let existingReadableIds = spaceMetrics.map(m => m.readableId)

    let readableIdsMap = {}
    const newMetrics = _.map(metrics, metric => {
      const newMetric = Object.assign(
        {},
        metric,
        e.metric.create(existingReadableIds),
        {space: spaceId},
        {location: translateFn(metric.location)}
      )
      existingReadableIds.push(newMetric.readableId)
      readableIdsMap[metric.readableId] = newMetric.readableId
      return newMetric
    })

    const newGuesstimates = _.map(
      guesstimates,
      (guesstimate, i) => Object.assign(
        {},
        guesstimate,
        {metric: newMetrics[i].id},
        {input: translateReadableIds(guesstimate.input, readableIdsMap)}
      )
    )

    const existingMetrics = spaceMetrics.filter(m => isWithinRegion(m.location, pasteRegion))
    existingMetrics.forEach(existingMetric => {dispatch(metricActions.removeMetric(existingMetric.id))})

    newMetrics.forEach((newMetric, i) => {dispatch({type: 'ADD_METRIC', item: newMetric, newGuesstimate: newGuesstimates[i]})})

    dispatch(runSimulations({spaceId, onlyMetrics: newMetrics}))
    dispatch(selectRegion(pasteRegion[0], pasteRegion[1]))
  }
}
<|endoftext|>


<commit_before>/**
 * Documentation: http://docs.azk.io/Azkfile.js
 */

var lodash = require('lodash');
var join   = require('path').join;
var config = require('azk').config;

var mounts = (function() {
  // Default mounts
  var mounts = {
    "/.tmux.conf"      : join(env.HOME, ".tmux.conf"),
    "/azk/demos"       : "../demos",
    "/azk/build"       : persistent('build-#{system.name}'),
    "/azk/lib"         : persistent('lib-#{system.name}'),
    "/azk/data"        : persistent('data-#{system.name}'),
    "/azk/aptly"       : persistent('aptly-#{system.name}'),
    "/var/lib/docker"  : persistent('docker_files-#{system.name}'),
    "/azk/#{manifest.dir}/app"          : sync("/tmp/a"),
    "/azk/#{manifest.dir}/node_modules" : persistent('node_modules-#{system.name}'),
    "/root/.aptly.conf": path("./src/libexec/aptly.json")
  }

  var glob  = require('glob');
  var itens = glob.sync("./!(lib|data|node_modules|npm-debug.log|.git|.DS_Store|.azk)", { dot: true });

  mounts = lodash.reduce(itens, function(mount, item) {
    var key    = join("/azk", "#{manifest.dir}", item);
    mount[key] = path(item);
    return mount;
  }, mounts);

  return mounts;
})();

var agent_system = function(image, extras) {
  extras = extras || {};

  return lodash.merge({
    image: { docker: image },
    scale: false,
    workdir: "/azk/#{manifest.dir}",
    shell: "/usr/local/bin/wrapdocker",
    mounts: mounts,
    envs: {
      PATH: "/azk/#{manifest.dir}/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
      AZK_DATA_PATH: "/azk/data",
      AZK_LIB_PATH : "/azk/lib",
      AZK_NAMESPACE: "azk.linux",
      AZK_PACKAGE_PATH: "/azk/build",
      AZK_BALANCER_HOST: "linux.azk.io",
      AZK_BALANCER_PORT: 8080,
      LOG: "file",
      EXTRA_SCRIPT: "/azk/#{manifest.dir}/src/libexec/init_azk",
      VERSION: "#{azk.version}",
    },
    docker_extra: {
      start: { Privileged: true },
    }
  }, extras);
}

var test_package_system = function(image){
  return {
    depends: ["package"],
    image: { docker: image },
    workdir: "/azk/#{manifest.dir}",
    shell: "/usr/local/bin/wrapdocker",
    mounts: {
      "/azk/demos"          : "../demos",
      "/azk/#{manifest.dir}": ".",
      "/azk/data"           : persistent('data-#{system.name}'),
      "/var/lib/docker"     : persistent('docker_files-#{system.name}'),
    },
    envs: {
      AZK_DATA_PATH: "/azk/data",
      AZK_BALANCER_HOST: "linux_dev.azk.io",
      LOG: "file", // Log docker to file
    },
    docker_extra: {
      start: { Privileged: true },
    }
  }
}

systems({
  'dind-ubuntu': agent_system('azukiapp/dind:ubuntu14'),
  'dind-fedora': agent_system('azukiapp/dind:fedora20'),

  package: agent_system('azukiapp/fpm', {
    shell: "/bin/bash",
    command: "azk nvm ./src/libexec/package-tools/server",
    scalable: { default: 0 },
    http: {
      domains: ["#{system.name}.azk.#{azk.default_domain}"],
    },
    ports: {
      http: "8080/tcp",
    },
  }),

  'pkg-ubuntu12-test': test_package_system('azukiapp/dind:ubuntu12'),
  'pkg-ubuntu14-test': test_package_system('azukiapp/dind:ubuntu14'),
  'pkg-fedora-test'  : test_package_system('azukiapp/dind:fedora20'),

  grunt: {
    image: { docker: "dockerfile/nodejs" },
    workdir: "/azk/#{manifest.dir}",
    mounts: {
      "/azk/#{manifest.dir}": ".",
    },
    envs: {
      PATH: "/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/azk/#{manifest.dir}/node_modules/.bin"
    }
  },

  docs: {
    image: { docker: "dockerfile/python" },
    //provision: [
      //'export INSTALL_DIR=/azk/<%= manifest.dir %>/vendor/python',
      //'pip install --target=$INSTALL_DIR --install-option="--install-scripts=$INSTALL_DIR/bin" sphinx',
    //],
    workdir: "/azk/#{manifest.dir}",
    mounts: {
      "/azk/#{manifest.dir}": ".",
    },
    //envs: {
      //PYTHONPATH: "/azk/<%= manifest.dir %>/vendor/python",
      //PATH: "/bin:/sbin:/usr/bin:/usr/sbin:/azk/<%= manifest.dir %>/vendor/python/bin"
    //}
  },
});

setDefault('docs');
<commit_msg>[hotfix] Removing Azkfile.js dirty<commit_after>/**
 * Documentation: http://docs.azk.io/Azkfile.js
 */

var lodash = require('lodash');
var join   = require('path').join;
var config = require('azk').config;

var mounts = (function() {
  // Default mounts
  var mounts = {
    "/.tmux.conf"      : join(env.HOME, ".tmux.conf"),
    "/azk/demos"       : "../demos",
    "/azk/build"       : persistent('build-#{system.name}'),
    "/azk/lib"         : persistent('lib-#{system.name}'),
    "/azk/data"        : persistent('data-#{system.name}'),
    "/azk/aptly"       : persistent('aptly-#{system.name}'),
    "/var/lib/docker"  : persistent('docker_files-#{system.name}'),
    "/azk/#{manifest.dir}/node_modules" : persistent('node_modules-#{system.name}'),
    "/root/.aptly.conf": path("./src/libexec/aptly.json")
  }

  var glob  = require('glob');
  var itens = glob.sync("./!(lib|data|node_modules|npm-debug.log|.git|.DS_Store|.azk)", { dot: true });

  mounts = lodash.reduce(itens, function(mount, item) {
    var key    = join("/azk", "#{manifest.dir}", item);
    mount[key] = path(item);
    return mount;
  }, mounts);

  return mounts;
})();

var agent_system = function(image, extras) {
  extras = extras || {};

  return lodash.merge({
    image: { docker: image },
    scale: false,
    workdir: "/azk/#{manifest.dir}",
    shell: "/usr/local/bin/wrapdocker",
    mounts: mounts,
    envs: {
      PATH: "/azk/#{manifest.dir}/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
      AZK_DATA_PATH: "/azk/data",
      AZK_LIB_PATH : "/azk/lib",
      AZK_NAMESPACE: "azk.linux",
      AZK_PACKAGE_PATH: "/azk/build",
      AZK_BALANCER_HOST: "linux.azk.io",
      AZK_BALANCER_PORT: 8080,
      LOG: "file",
      EXTRA_SCRIPT: "/azk/#{manifest.dir}/src/libexec/init_azk",
      VERSION: "#{azk.version}",
    },
    docker_extra: {
      start: { Privileged: true },
    }
  }, extras);
}

var test_package_system = function(image){
  return {
    depends: ["package"],
    image: { docker: image },
    workdir: "/azk/#{manifest.dir}",
    shell: "/usr/local/bin/wrapdocker",
    mounts: {
      "/azk/demos"          : "../demos",
      "/azk/#{manifest.dir}": ".",
      "/azk/data"           : persistent('data-#{system.name}'),
      "/var/lib/docker"     : persistent('docker_files-#{system.name}'),
    },
    envs: {
      AZK_DATA_PATH: "/azk/data",
      AZK_BALANCER_HOST: "linux_dev.azk.io",
      LOG: "file", // Log docker to file
    },
    docker_extra: {
      start: { Privileged: true },
    }
  }
}

systems({
  'dind-ubuntu': agent_system('azukiapp/dind:ubuntu14'),
  'dind-fedora': agent_system('azukiapp/dind:fedora20'),

  package: agent_system('azukiapp/fpm', {
    shell: "/bin/bash",
    command: "azk nvm ./src/libexec/package-tools/server",
    scalable: { default: 0 },
    http: {
      domains: ["#{system.name}.azk.#{azk.default_domain}"],
    },
    ports: {
      http: "8080/tcp",
    },
  }),

  'pkg-ubuntu12-test': test_package_system('azukiapp/dind:ubuntu12'),
  'pkg-ubuntu14-test': test_package_system('azukiapp/dind:ubuntu14'),
  'pkg-fedora-test'  : test_package_system('azukiapp/dind:fedora20'),

  grunt: {
    image: { docker: "dockerfile/nodejs" },
    workdir: "/azk/#{manifest.dir}",
    mounts: {
      "/azk/#{manifest.dir}": ".",
    },
    envs: {
      PATH: "/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/azk/#{manifest.dir}/node_modules/.bin"
    }
  },

  docs: {
    image: { docker: "dockerfile/python" },
    //provision: [
      //'export INSTALL_DIR=/azk/<%= manifest.dir %>/vendor/python',
      //'pip install --target=$INSTALL_DIR --install-option="--install-scripts=$INSTALL_DIR/bin" sphinx',
    //],
    workdir: "/azk/#{manifest.dir}",
    mounts: {
      "/azk/#{manifest.dir}": ".",
    },
    //envs: {
      //PYTHONPATH: "/azk/<%= manifest.dir %>/vendor/python",
      //PATH: "/bin:/sbin:/usr/bin:/usr/sbin:/azk/<%= manifest.dir %>/vendor/python/bin"
    //}
  },
});

setDefault('docs');
<|endoftext|>


<commit_before>define(
    [
        'backbone', 'backform', 'bootstrap',
        'bootstrap-datepicker',
        'app/views/rooms_form',
	      'app/views/model_select_control',
        'app/models/logistics_dates',
        'app/models/sitecaptains_for_site',
        'app/models/order_existing',
        'app/models/order_form_overview',
        'app/models/order_items',
        'app/models/order',  // TODO: remove?
        'app/models/site',
        'app/models/order_form_detail',
        'app/models/order_full',
        'text!app/templates/order_choose_form.html',
        'text!app/templates/order_form_button.html',
        'text!app/templates/order_select_items.html',
        'text!app/templates/order_logistics.html',
    ],
    function(Backbone, Backform, bootstrap,
             bsdp, RoomFormView, ModelSelectControl,
             LogisticsDates, SiteCaptains, OrderExisting,
             OrderFormOverview, OrderItems, Order, Site, OrderFormDetail, OrderFull,
             choose_form_template, button_template, select_items_template,
             logistics_template) {
        var basic_logistics_fields = [
            {
                name: "contact",
                label: "Contact person (who will be present)",
                control: "input",
            },
            {
                name: "contact_phone",
                label: "Contact phone",
                control: "input",
            },
            {
                name: "notes",
                label: "Instructions for delivery person",
                control: "textarea"
            },
            {
                name: "submit",
                control: "button",
                extraClasses: ['btn-primary'],
                label: "Choose these options and complete order"
            },
        ];

        var delivery_fields = [
            {
                name: 'delivery_date',
                label: 'Delivery date (Mon-Fri only)',
                control: "datepicker",
                options: {format: "yyyy-mm-dd", startDate: "0d", daysOfWeekDisabled: "06"},
                required: true
            },
        ].concat(basic_logistics_fields);

        var pickup_fields = [
            {
                name: 'pickup_date',
                label: 'Pickup date (Mon-Fri only)',
                control: "datepicker",
                options: {format: "yyyy-mm-dd", startDate: "+2d",  daysOfWeekDisabled: "06"},
                required: true
            },
        ].concat(basic_logistics_fields);

        var borrow_fields = [
            {
                name: 'borrow_date',
                label: 'Borrow date (Mon-Fri only)',
                control: "datepicker",
                options: {format: "yyyy-mm-dd", startDate: "+2d",  daysOfWeekDisabled: "06"},
                required: true
            },
            {
                name: 'return_date',
                label: 'Return date for durable equipment',
                control: "datepicker",
                options: {format: "yyyy-mm-dd", startDate: "+2d"},
                required: true
            },
        ].concat(basic_logistics_fields);

        var retrieval_fields = [
            {
                name: 'dropoff_date',
                label: 'Delivery date (Mon-Fri only)',
                control: "datepicker",
                options: {format: "yyyy-mm-dd", startDate: "0d",  daysOfWeekDisabled: "06"},
                required: true
            },
            {
                name: 'retrieval_date',
                label: 'Retrieval Date (Mon-Fri only)',
                control: "datepicker",
                options: {format: "yyyy-mm-dd", startDate: "0d",  daysOfWeekDisabled: "06"},
                required: true
            },
        ].concat(basic_logistics_fields);

        var OrderFlowView = Backbone.View.extend({
            initialize: function(site_id, order_id) {
                var self = this;
                this.order_id = order_id;

                var site = new Site({id: site_id});
                site.fetch().then(function() {self.site = site; self.render()});

                this.logistics_dates = new LogisticsDates(site_id);
                this.logistics_dates.fetch();

                this.sitecaptains = new SiteCaptains(site_id);
                this.sitecaptains.fetch();

                if (order_id) {  // edit mode
                    var order_full = new OrderFull({id: order_id});
                    order_full.fetch().then(function() {
                        self.order_full = order_full;
                        self.order = new Order(order_full.get('order'));
                        self.order_items = new OrderItems(order_full.get('order_items'));
                        self.listenTo(self.order_items, 'change add', self.renderStep2);
                        self.listenTo(self.order, 'change', self.renderStep2);

                        if (order_full.has('delivery')) {
                            self.delivery = new Backbone.Model(order_full.get('delivery'));
                        }
                        if (order_full.has('pickup')) {
                            self.pickup = new Backbone.Model(order_full.get('pickup'));
                        }
                        if (order_full.has('borrow')) {
                            self.borrow = new Backbone.Model(order_full.get('borrow'));
                        }
                        if (order_full.has('retrieval')) {
                            self.retrieval = new Backbone.Model(order_full.get('retrieval'));
                        }

                        var ofd = new OrderFormDetail({id: self.order.get('order_sheet')});
                        ofd.fetch().then(function() {
                            self.order_form_detail = ofd;
                            self.renderStep2();
                        });
                    });
                } else {
                    this.order_full = new OrderFull();
                    this.order = new Order({site: site_id});
                    var order_forms = new OrderFormOverview();
                    order_forms.fetch().then(function() {self.order_forms = order_forms; self.render()});
                    this.order_items = new OrderItems();
                    this.logistics = new Backbone.Model();
                    this.listenTo(this.order_items, 'change add', this.renderStep2);
                    this.listenTo(this.order, 'change', this.renderStep2);
                }

                this.choose_form_template = _.template(choose_form_template);
                this.button_template = _.template(button_template);
                this.select_items_template = _.template(select_items_template);
                this.logistics_template = _.template(logistics_template);
            },
            events: {
                'change #id_notes': 'savenotes',
                'change .item-quantity': 'changeQuantity',
                'change .item-notes': 'changeNotes',
                'click #order-proceed-delivery': 'renderDelivery',
                'click #order-proceed-pickup': 'renderPickup',
                'click #order-proceed-borrow': 'renderBorrow',
                'click #order-proceed-retrieval': 'renderRetrieval',
                'click #order-proceed-no-logistics': 'save',
            },
            savenotes: function(e) {
                this.order.set('notes', e.target.value);
            },
            changeQuantity: function(e) {
                var new_quantity_float = 0;
                if (e.target.value != "") {
                    new_quantity_float = parseInt(e.target.value);
                }
                var oi = this.order_items.get(parseInt(e.target.name));
                if (!oi) {
                    this.order_items.add({
                        item: parseInt(e.target.name),
                        quantity: new_quantity_float
                    });
                } else {
                    oi.set('quantity', new_quantity_float);
                }
                return false;
            },
            changeNotes: function(e) {
                var oi = this.order_items.get(parseInt(e.target.name));
                if (!oi) {
                    this.order_items.add({
                        item: parseInt(e.target.name),
                        name: e.target.value
                    });
                } else {
                    oi.set('name', e.target.value);
                }
                return false;
            },
            orderTotal: function() {
                return _.reduce(this.order_items, function(memo, value) {
                    return memo + totalForItem(value)
                });
            },
            el: '#order-flow-view',
            totalForItem: function(item, order_items) {
                var oi = order_items.get(item.id)
                if (!oi) {
                    return "";
                }
                if (!oi.get('quantity')) {
                    return "";
                }
                var q = parseFloat(oi.get('quantity'));
                if (!q) {
                    return "";
                }
                if (item.unit_cost) {
                    var num = item.unit_cost * q;
                    return Math.round( num * 100 + Number.EPSILON ) / 100;

                } else {
                    return "";
                }
            },
            save: function() {
                $('button[name="submit"]').attr('disabled', true);
                $('span.status')
                    .css('color', '#909b27')
                    .text('Saving...')
                    .show();
                this.order_full.set({
                    order: this.order.attributes,
                    order_items: this.order_items.map(function(modl) {
                        return modl.attributes;
                    })
                });
                this.order_full.save(
                    null, {
                        'success': function(model, attrs, response) {
                            response.xhr.statusText = 'SAVED';
                            $('span.status')
                                .css('color', '#409b27')
                                .text('Saved. Now loading site ...')
                                .show();
                            window.location = $('#rooms-form-after-save').attr('href');
                        },
                        'error': function(model, response, error) {
                            $('span.status')
                                .css('color', 'red')
                                .text('Error: ' + response.responseText)
                                .show();
                            $('button[name="submit"]').attr('disabled', false);
                        },
                    });
            },
            renderDelivery: function() {
                if (!this.delivery) {
                    this.delivery = new Backbone.Model();
                }
                this.order_full.set('delivery', this.delivery.attributes);
                this.renderLogistics(delivery_fields, "Delivery", this.delivery);
            },
            renderPickup: function() {
                if (!this.pickup) {
                    this.pickup = new Backbone.Model();
                }
                this.order_full.set('pickup', this.pickup.attributes);
                pickup_fields[3].label = "Instructions for warehouse staff";
                this.renderLogistics(pickup_fields, "Pickup", this.pickup);
            },
            renderBorrow: function() {
                if (!this.borrow) {
                    this.borrow = new Backbone.Model();
                }
                this.order_full.set('borrow', this.borrow.attributes);
                borrow_fields[4].label = "Instructions for warehouse staff";
                this.renderLogistics(borrow_fields, "Borrow and Return", this.borrow);
            },
            renderRetrieval: function() {
                if (!this.retrieval) {
                    this.retrieval = new Backbone.Model();
                }
                this.order_full.set('retrieval', this.retrieval.attributes);
                this.renderLogistics(retrieval_fields, "Drop-off and Retrieval", this.retrieval);
            },
            renderLogistics: function(fields, logistics_words, model) {
                console.log('step 3');
                var t = this.logistics_template({
                    order: this.order,
                    site: this.site,
                    sitecaptains: this.sitecaptains,
                    logistics_dates: this.logistics_dates,
                    logistics_words: logistics_words,
                    order_form: this.order_form_detail.get('order_sheet'),
                });
                this.$el.html(t);
                this.logistics_form = new Backform.Form({
                    model: model,
                    fields: fields,
                    showRequiredAsAsterisk: true,
                    events: {
                        'submit': function(e) {
                            e.preventDefault();
                            this.trigger('submit');
                        },
                    }
                });
                this.listenTo(this.logistics_form, 'submit', this.save);
                this.logistics_form.setElement(this.$el.find('#order-logistics-form'));
                this.logistics_form.render().$el.find('label.control-label:contains("*")').addClass('required');
                return this;
            },
            render: function() {
                this.renderStep1();
                return this
            },
            renderStep2: function() {
                console.log('step 2');
		            trackJs.track('order_flow step2 on ' + this.site.get('number'));
                var self = this;
                if (!this.site) {
                    return this;  // loading
                }
                if (!this.order_form_detail) {
                    return this;  // loading
                }
                var t = this.select_items_template({
                    order: this.order,
                    site: this.site,
                    order_existing: this.order_existing,
                    order_form: this.order_form_detail.get('order_sheet'),
                    items: this.order_form_detail.get('sorted_items'),  // TODO
                    order_items: this.order_items,
                    totalForItem: this.totalForItem,
                    quantityForItem: function(item, order_items) {
                        var oi = order_items.get(item.id);
                        if (oi) {
                            return oi.get('quantity');
                        } else {
                            return '';
                        }
                    },
                    notesForItem: function(item, order_items) {
                        var oi = order_items.get(item.id);
                        if (oi) {
                            return oi.get('name');
                        } else {
                            return '';
                        }
                    },
                    subtotal: function() {
                        var num = _.reduce(
                            self.order_form_detail.get('sorted_items'),
                            function(memo, val) {
                                var t = self.totalForItem(val, self.order_items);
                                if (t) {
                                    return t + memo;
                                } else {
                                    return memo;
                                }
                            }, 0);
                        return Math.round( num * 100 + Number.EPSILON ) / 100;
                    }
                });
                this.$el.html(t);
                return this;
            },
            renderStep1: function() {  // step 1, render the choose order form screen.
                var self = this;
                if (!this.site) {
                    console.log('waiting for site');
                    return this;  // loading
                }
                if (!this.order_full) {
                    console.log('waiting for order_full');
                    return this;  // loading
                }
                if (!this.order_forms) {
                    console.log('waiting for order_forms');
                    return this;  // loading
                }

                var t = this.choose_form_template({
                    s: this.order.attributes,
                    site: this.site,
                });
                this.$el.html(t);
                var order_forms = this.order_forms.groupBy('visibility');
                var button_template = this.button_template;

                // buttons for forms that everyone can see
                var buttons = this.$('#order-form-buttons-everyone');
                var addButtons = function(visibility) {
                    _.chain(order_forms[visibility])
                        .sortBy(function(f) {return f.get('code')})
                        .each(function(f) {
                            buttons.append(button_template(f.attributes));
                        });
                }
                addButtons('Everyone')

                // staff-only buttons
                buttons = this.$('#order-form-buttons-staff');
                buttons.hide();
                if (order_forms['Staff Only']) {
                    addButtons('Staff Only')
                    buttons.show();
                }

                // when button is clicked, move to step 2
                $(".order-form-buttons button").click(function() {
                    var id = parseInt(this.id);  // ordersheet ID
                    self.order.set('order_sheet', id);
                    self.order_existing = new OrderExisting(self.site.id, id);
                    self.listenTo(self.order_existing, 'update', self.renderStep2);
                    self.order_existing.fetch();
                    var ofd = new OrderFormDetail({id: id});
                    ofd.fetch().then(function() {
                        self.order_form_detail = ofd;
                        self.renderStep2()});
                });
                return this;
            }
        });
        return OrderFlowView;
    }
)
<commit_msg>removed trackjs form order_flow<commit_after>define(
    [
        'backbone', 'backform', 'bootstrap',
        'bootstrap-datepicker',
        'app/views/rooms_form',
	      'app/views/model_select_control',
        'app/models/logistics_dates',
        'app/models/sitecaptains_for_site',
        'app/models/order_existing',
        'app/models/order_form_overview',
        'app/models/order_items',
        'app/models/order',  // TODO: remove?
        'app/models/site',
        'app/models/order_form_detail',
        'app/models/order_full',
        'text!app/templates/order_choose_form.html',
        'text!app/templates/order_form_button.html',
        'text!app/templates/order_select_items.html',
        'text!app/templates/order_logistics.html',
    ],
    function(Backbone, Backform, bootstrap,
             bsdp, RoomFormView, ModelSelectControl,
             LogisticsDates, SiteCaptains, OrderExisting,
             OrderFormOverview, OrderItems, Order, Site, OrderFormDetail, OrderFull,
             choose_form_template, button_template, select_items_template,
             logistics_template) {
        var basic_logistics_fields = [
            {
                name: "contact",
                label: "Contact person (who will be present)",
                control: "input",
            },
            {
                name: "contact_phone",
                label: "Contact phone",
                control: "input",
            },
            {
                name: "notes",
                label: "Instructions for delivery person",
                control: "textarea"
            },
            {
                name: "submit",
                control: "button",
                extraClasses: ['btn-primary'],
                label: "Choose these options and complete order"
            },
        ];

        var delivery_fields = [
            {
                name: 'delivery_date',
                label: 'Delivery date (Mon-Fri only)',
                control: "datepicker",
                options: {format: "yyyy-mm-dd", startDate: "0d", daysOfWeekDisabled: "06"},
                required: true
            },
        ].concat(basic_logistics_fields);

        var pickup_fields = [
            {
                name: 'pickup_date',
                label: 'Pickup date (Mon-Fri only)',
                control: "datepicker",
                options: {format: "yyyy-mm-dd", startDate: "+2d",  daysOfWeekDisabled: "06"},
                required: true
            },
        ].concat(basic_logistics_fields);

        var borrow_fields = [
            {
                name: 'borrow_date',
                label: 'Borrow date (Mon-Fri only)',
                control: "datepicker",
                options: {format: "yyyy-mm-dd", startDate: "+2d",  daysOfWeekDisabled: "06"},
                required: true
            },
            {
                name: 'return_date',
                label: 'Return date for durable equipment',
                control: "datepicker",
                options: {format: "yyyy-mm-dd", startDate: "+2d"},
                required: true
            },
        ].concat(basic_logistics_fields);

        var retrieval_fields = [
            {
                name: 'dropoff_date',
                label: 'Delivery date (Mon-Fri only)',
                control: "datepicker",
                options: {format: "yyyy-mm-dd", startDate: "0d",  daysOfWeekDisabled: "06"},
                required: true
            },
            {
                name: 'retrieval_date',
                label: 'Retrieval Date (Mon-Fri only)',
                control: "datepicker",
                options: {format: "yyyy-mm-dd", startDate: "0d",  daysOfWeekDisabled: "06"},
                required: true
            },
        ].concat(basic_logistics_fields);

        var OrderFlowView = Backbone.View.extend({
            initialize: function(site_id, order_id) {
                var self = this;
                this.order_id = order_id;

                var site = new Site({id: site_id});
                site.fetch().then(function() {self.site = site; self.render()});

                this.logistics_dates = new LogisticsDates(site_id);
                this.logistics_dates.fetch();

                this.sitecaptains = new SiteCaptains(site_id);
                this.sitecaptains.fetch();

                if (order_id) {  // edit mode
                    var order_full = new OrderFull({id: order_id});
                    order_full.fetch().then(function() {
                        self.order_full = order_full;
                        self.order = new Order(order_full.get('order'));
                        self.order_items = new OrderItems(order_full.get('order_items'));
                        self.listenTo(self.order_items, 'change add', self.renderStep2);
                        self.listenTo(self.order, 'change', self.renderStep2);

                        if (order_full.has('delivery')) {
                            self.delivery = new Backbone.Model(order_full.get('delivery'));
                        }
                        if (order_full.has('pickup')) {
                            self.pickup = new Backbone.Model(order_full.get('pickup'));
                        }
                        if (order_full.has('borrow')) {
                            self.borrow = new Backbone.Model(order_full.get('borrow'));
                        }
                        if (order_full.has('retrieval')) {
                            self.retrieval = new Backbone.Model(order_full.get('retrieval'));
                        }

                        var ofd = new OrderFormDetail({id: self.order.get('order_sheet')});
                        ofd.fetch().then(function() {
                            self.order_form_detail = ofd;
                            self.renderStep2();
                        });
                    });
                } else {
                    this.order_full = new OrderFull();
                    this.order = new Order({site: site_id});
                    var order_forms = new OrderFormOverview();
                    order_forms.fetch().then(function() {self.order_forms = order_forms; self.render()});
                    this.order_items = new OrderItems();
                    this.logistics = new Backbone.Model();
                    this.listenTo(this.order_items, 'change add', this.renderStep2);
                    this.listenTo(this.order, 'change', this.renderStep2);
                }

                this.choose_form_template = _.template(choose_form_template);
                this.button_template = _.template(button_template);
                this.select_items_template = _.template(select_items_template);
                this.logistics_template = _.template(logistics_template);
            },
            events: {
                'change #id_notes': 'savenotes',
                'change .item-quantity': 'changeQuantity',
                'change .item-notes': 'changeNotes',
                'click #order-proceed-delivery': 'renderDelivery',
                'click #order-proceed-pickup': 'renderPickup',
                'click #order-proceed-borrow': 'renderBorrow',
                'click #order-proceed-retrieval': 'renderRetrieval',
                'click #order-proceed-no-logistics': 'save',
            },
            savenotes: function(e) {
                this.order.set('notes', e.target.value);
            },
            changeQuantity: function(e) {
                var new_quantity_float = 0;
                if (e.target.value != "") {
                    new_quantity_float = parseInt(e.target.value);
                }
                var oi = this.order_items.get(parseInt(e.target.name));
                if (!oi) {
                    this.order_items.add({
                        item: parseInt(e.target.name),
                        quantity: new_quantity_float
                    });
                } else {
                    oi.set('quantity', new_quantity_float);
                }
                return false;
            },
            changeNotes: function(e) {
                var oi = this.order_items.get(parseInt(e.target.name));
                if (!oi) {
                    this.order_items.add({
                        item: parseInt(e.target.name),
                        name: e.target.value
                    });
                } else {
                    oi.set('name', e.target.value);
                }
                return false;
            },
            orderTotal: function() {
                return _.reduce(this.order_items, function(memo, value) {
                    return memo + totalForItem(value)
                });
            },
            el: '#order-flow-view',
            totalForItem: function(item, order_items) {
                var oi = order_items.get(item.id)
                if (!oi) {
                    return "";
                }
                if (!oi.get('quantity')) {
                    return "";
                }
                var q = parseFloat(oi.get('quantity'));
                if (!q) {
                    return "";
                }
                if (item.unit_cost) {
                    var num = item.unit_cost * q;
                    return Math.round( num * 100 + Number.EPSILON ) / 100;

                } else {
                    return "";
                }
            },
            save: function() {
                $('button[name="submit"]').attr('disabled', true);
                $('span.status')
                    .css('color', '#909b27')
                    .text('Saving...')
                    .show();
                this.order_full.set({
                    order: this.order.attributes,
                    order_items: this.order_items.map(function(modl) {
                        return modl.attributes;
                    })
                });
                this.order_full.save(
                    null, {
                        'success': function(model, attrs, response) {
                            response.xhr.statusText = 'SAVED';
                            $('span.status')
                                .css('color', '#409b27')
                                .text('Saved. Now loading site ...')
                                .show();
                            window.location = $('#rooms-form-after-save').attr('href');
                        },
                        'error': function(model, response, error) {
                            $('span.status')
                                .css('color', 'red')
                                .text('Error: ' + response.responseText)
                                .show();
                            $('button[name="submit"]').attr('disabled', false);
                        },
                    });
            },
            renderDelivery: function() {
                if (!this.delivery) {
                    this.delivery = new Backbone.Model();
                }
                this.order_full.set('delivery', this.delivery.attributes);
                this.renderLogistics(delivery_fields, "Delivery", this.delivery);
            },
            renderPickup: function() {
                if (!this.pickup) {
                    this.pickup = new Backbone.Model();
                }
                this.order_full.set('pickup', this.pickup.attributes);
                pickup_fields[3].label = "Instructions for warehouse staff";
                this.renderLogistics(pickup_fields, "Pickup", this.pickup);
            },
            renderBorrow: function() {
                if (!this.borrow) {
                    this.borrow = new Backbone.Model();
                }
                this.order_full.set('borrow', this.borrow.attributes);
                borrow_fields[4].label = "Instructions for warehouse staff";
                this.renderLogistics(borrow_fields, "Borrow and Return", this.borrow);
            },
            renderRetrieval: function() {
                if (!this.retrieval) {
                    this.retrieval = new Backbone.Model();
                }
                this.order_full.set('retrieval', this.retrieval.attributes);
                this.renderLogistics(retrieval_fields, "Drop-off and Retrieval", this.retrieval);
            },
            renderLogistics: function(fields, logistics_words, model) {
                console.log('step 3');
                var t = this.logistics_template({
                    order: this.order,
                    site: this.site,
                    sitecaptains: this.sitecaptains,
                    logistics_dates: this.logistics_dates,
                    logistics_words: logistics_words,
                    order_form: this.order_form_detail.get('order_sheet'),
                });
                this.$el.html(t);
                this.logistics_form = new Backform.Form({
                    model: model,
                    fields: fields,
                    showRequiredAsAsterisk: true,
                    events: {
                        'submit': function(e) {
                            e.preventDefault();
                            this.trigger('submit');
                        },
                    }
                });
                this.listenTo(this.logistics_form, 'submit', this.save);
                this.logistics_form.setElement(this.$el.find('#order-logistics-form'));
                this.logistics_form.render().$el.find('label.control-label:contains("*")').addClass('required');
                return this;
            },
            render: function() {
                this.renderStep1();
                return this
            },
            renderStep2: function() {
                console.log('step 2');
                var self = this;
                if (!this.site) {
                    return this;  // loading
                }
                if (!this.order_form_detail) {
                    return this;  // loading
                }
                var t = this.select_items_template({
                    order: this.order,
                    site: this.site,
                    order_existing: this.order_existing,
                    order_form: this.order_form_detail.get('order_sheet'),
                    items: this.order_form_detail.get('sorted_items'),  // TODO
                    order_items: this.order_items,
                    totalForItem: this.totalForItem,
                    quantityForItem: function(item, order_items) {
                        var oi = order_items.get(item.id);
                        if (oi) {
                            return oi.get('quantity');
                        } else {
                            return '';
                        }
                    },
                    notesForItem: function(item, order_items) {
                        var oi = order_items.get(item.id);
                        if (oi) {
                            return oi.get('name');
                        } else {
                            return '';
                        }
                    },
                    subtotal: function() {
                        var num = _.reduce(
                            self.order_form_detail.get('sorted_items'),
                            function(memo, val) {
                                var t = self.totalForItem(val, self.order_items);
                                if (t) {
                                    return t + memo;
                                } else {
                                    return memo;
                                }
                            }, 0);
                        return Math.round( num * 100 + Number.EPSILON ) / 100;
                    }
                });
                this.$el.html(t);
                return this;
            },
            renderStep1: function() {  // step 1, render the choose order form screen.
                var self = this;
                if (!this.site) {
                    console.log('waiting for site');
                    return this;  // loading
                }
                if (!this.order_full) {
                    console.log('waiting for order_full');
                    return this;  // loading
                }
                if (!this.order_forms) {
                    console.log('waiting for order_forms');
                    return this;  // loading
                }

                var t = this.choose_form_template({
                    s: this.order.attributes,
                    site: this.site,
                });
                this.$el.html(t);
                var order_forms = this.order_forms.groupBy('visibility');
                var button_template = this.button_template;

                // buttons for forms that everyone can see
                var buttons = this.$('#order-form-buttons-everyone');
                var addButtons = function(visibility) {
                    _.chain(order_forms[visibility])
                        .sortBy(function(f) {return f.get('code')})
                        .each(function(f) {
                            buttons.append(button_template(f.attributes));
                        });
                }
                addButtons('Everyone')

                // staff-only buttons
                buttons = this.$('#order-form-buttons-staff');
                buttons.hide();
                if (order_forms['Staff Only']) {
                    addButtons('Staff Only')
                    buttons.show();
                }

                // when button is clicked, move to step 2
                $(".order-form-buttons button").click(function() {
                    var id = parseInt(this.id);  // ordersheet ID
                    self.order.set('order_sheet', id);
                    self.order_existing = new OrderExisting(self.site.id, id);
                    self.listenTo(self.order_existing, 'update', self.renderStep2);
                    self.order_existing.fetch();
                    var ofd = new OrderFormDetail({id: id});
                    ofd.fetch().then(function() {
                        self.order_form_detail = ofd;
                        self.renderStep2()});
                });
                return this;
            }
        });
        return OrderFlowView;
    }
)
<|endoftext|>


<commit_before>'use strict';

describe('Controller Tests', function() {

    beforeEach(mockApiAccountCall);
    beforeEach(mockI18nCalls);

    describe('SessionsController', function() {

        var $scope, $q; // actual implementations
        var MockSessions, MockPrincipal; // mocks
        var createController; // local utility function

        var sessions = [{
            tokenDate: "2015-10-15",
            ipAddress: "0:0:0:0:0:0:0:1",
            series: "xxxxxx==",
            userAgent: "Mozilla/5.0"
        }];

        beforeEach(inject(function($injector) {
            $q = $injector.get('$q');
            $scope = $injector.get('$rootScope').$new();
            MockSessions = jasmine.createSpyObj('MockSessions', ['getAll', 'delete']);
            MockPrincipal = jasmine.createSpyObj('MockPrincipal', ['identity']);

            var locals = {
                '$scope': $scope,
                'Sessions': MockSessions,
                'Principal': MockPrincipal
            };
            createController = function() {
                return $injector.get('$controller')('SessionsController as vm', locals);
            };
        }));

        it('should define its initial state', function() {
            MockPrincipal.identity.and.returnValue($q.resolve({
                id: 'fuzzer'
            }));
            MockSessions.getAll.and.returnValue(sessions);
            // given
            $scope.$apply(createController);
            // then
            expect(MockPrincipal.identity).toHaveBeenCalled();
            expect(MockSessions.getAll).toHaveBeenCalled();
            expect($scope.vm.success).toBeNull();
            expect($scope.vm.error).toBeNull();
            expect($scope.vm.account).toEqual({
                id: 'fuzzer'
            });
            expect($scope.vm.sessions).toEqual(sessions);
        });

        it('should call delete on Sessions to invalidate a session', function() {
            MockPrincipal.identity.and.returnValue($q.resolve({
                id: 'fuzzer'
            }));
            MockSessions.getAll.and.returnValue(sessions);
            // given
            createController();
            $scope.vm.invalidate('xyz');
            $scope.$apply();
            // then
            expect(MockSessions.delete).toHaveBeenCalledWith({
                series: 'xyz'
            }, jasmine.any(Function), jasmine.any(Function));
        });

        it('should call delete on Sessions and notify of error', function() {
            MockPrincipal.identity.and.returnValue($q.resolve({
                id: 'fuzzer'
            }));
            MockSessions.getAll.and.returnValue(sessions);
            MockSessions.delete.and.callFake(function resourceDelete(arg, success, failure) {
                failure();
            });
            // given
            createController();
            $scope.vm.invalidate('xyz');
            $scope.$apply();
            // then
            expect($scope.vm.success).toBeNull();
            expect($scope.vm.error).toBe('ERROR');
        });

        it('should call notify of success upon session invalidation', function() {
            MockPrincipal.identity.and.returnValue($q.resolve({
                id: 'fuzzer'
            }));
            MockSessions.getAll.and.returnValue(sessions);
            MockSessions.delete.and.callFake(function resourceDelete(arg, success, failure) {
                success();
            });
            // given
            createController();
            $scope.vm.invalidate('xyz');
            $scope.$apply();
            // then
            expect($scope.vm.error).toBeNull();
            expect($scope.vm.success).toBe('OK');
        });
    });
});
<commit_msg>polish<commit_after>'use strict';

describe('Controller Tests', function() {

    beforeEach(mockApiAccountCall);
    beforeEach(mockI18nCalls);

    describe('SessionsController', function() {

        var $scope, $q; // actual implementations
        var MockSessions, MockPrincipal; // mocks
        var createController; // local utility function

        var sessions = [{
            tokenDate: '2015-10-15',
            ipAddress: '0:0:0:0:0:0:0:1',
            series: 'xxxxxx==',
            userAgent: 'Mozilla/5.0'
        }];

        beforeEach(inject(function($injector) {
            $q = $injector.get('$q');
            $scope = $injector.get('$rootScope').$new();
            MockSessions = jasmine.createSpyObj('MockSessions', ['getAll', 'delete']);
            MockPrincipal = jasmine.createSpyObj('MockPrincipal', ['identity']);

            var locals = {
                '$scope': $scope,
                'Sessions': MockSessions,
                'Principal': MockPrincipal
            };
            createController = function() {
                return $injector.get('$controller')('SessionsController as vm', locals);
            };
        }));

        it('should define its initial state', function() {
            MockPrincipal.identity.and.returnValue($q.resolve({
                id: 'fuzzer'
            }));
            MockSessions.getAll.and.returnValue(sessions);
            // given
            $scope.$apply(createController);
            // then
            expect(MockPrincipal.identity).toHaveBeenCalled();
            expect(MockSessions.getAll).toHaveBeenCalled();
            expect($scope.vm.success).toBeNull();
            expect($scope.vm.error).toBeNull();
            expect($scope.vm.account).toEqual({
                id: 'fuzzer'
            });
            expect($scope.vm.sessions).toEqual(sessions);
        });

        it('should call delete on Sessions to invalidate a session', function() {
            MockPrincipal.identity.and.returnValue($q.resolve({
                id: 'fuzzer'
            }));
            MockSessions.getAll.and.returnValue(sessions);
            // given
            createController();
            $scope.vm.invalidate('xyz');
            $scope.$apply();
            // then
            expect(MockSessions.delete).toHaveBeenCalledWith({
                series: 'xyz'
            }, jasmine.any(Function), jasmine.any(Function));
        });

        it('should call delete on Sessions and notify of error', function() {
            MockPrincipal.identity.and.returnValue($q.resolve({
                id: 'fuzzer'
            }));
            MockSessions.getAll.and.returnValue(sessions);
            MockSessions.delete.and.callFake(function resourceDelete(arg, success, failure) {
                failure();
            });
            // given
            createController();
            $scope.vm.invalidate('xyz');
            $scope.$apply();
            // then
            expect($scope.vm.success).toBeNull();
            expect($scope.vm.error).toBe('ERROR');
        });

        it('should call notify of success upon session invalidation', function() {
            MockPrincipal.identity.and.returnValue($q.resolve({
                id: 'fuzzer'
            }));
            MockSessions.getAll.and.returnValue(sessions);
            MockSessions.delete.and.callFake(function resourceDelete(arg, success, failure) {
                success();
            });
            // given
            createController();
            $scope.vm.invalidate('xyz');
            $scope.$apply();
            // then
            expect($scope.vm.error).toBeNull();
            expect($scope.vm.success).toBe('OK');
        });
    });
});
<|endoftext|>


<commit_before>import * as system from './system';
import * as task from './task';

var dotList;
var dotCount;
var lastActivityRE =
  /Last Activity:<\/td>(\d+)d (\d+)h (\d+)m (\d+)s<\/td>/;
var redDot = '';
var greenDiamond =
  '';
var yellowDiamond =
  '';
var orangeDiamond =
  '';
var offlineDot =
  '';
var sevenDayDot =
  '';

export var pCC = document.getElementById('pCC');

export function buffAllHref(shortList) { // Bad Pattern
  var _shortList = shortList.join(',').replace(/\s/g, '');
  var j = 'java';
  return j + 'script:openWindow(\'index.php?cmd=quickbuff&t=' + _shortList +
    '\', \'fsQuickBuff\', 618, 1000, \',scrollbars\')';
}

export function quickBuffHref(aPlayerId, buffList) { // Bad Pattern
  return 'href=\'javascript:window.openWindow("index.php?cmd=' +
    'quickbuff&tid=' + aPlayerId + (buffList ? '&blist=' + buffList : '') +
    '", "fsQuickBuff", 618, 1000, ",scrollbars")\'';
}

export function openQuickBuffById(aPlayerId) {
  window.openWindow('index.php?cmd=quickbuff&tid=' + aPlayerId,
    'fsQuickBuff', 618, 1000, ',scrollbars');
}

export function openQuickBuffByName(aPlayerName) {
  window.openWindow('index.php?cmd=quickbuff&t=' + aPlayerName,
    'fsQuickBuff', 618, 1000, ',scrollbars');
}

export function infoBox(documentText) { // Native
  var doc = system.createDocument(documentText);
  var result;
  var infoMsg = doc.getElementById('info-msg');
  if (infoMsg) {
    var infoMatch = infoMsg.innerHTML;
    result = '';
    if (infoMatch) {
      infoMatch = infoMatch.replace(/<br.*/, '');
      result = infoMatch;
    }
  }
  return result;
}

export function guildId() { // Native
  var _guildId;
  var nodeList = document.body.getElementsByTagName('script');
  Array.prototype.forEach.call(nodeList, function getGuildId(el) {
    var match = el.textContent.match(/\s+guildId: ([0-9]+),/);
    if (match) {_guildId = parseInt(match[1], 10);}
  });
  system.setValue('guildId', _guildId);
  return _guildId;
}

export function playerId() { // Native
  var thePlayerId = parseInt(document.getElementById('holdtext')
    .textContent.match(/fallensword.com\/\?ref=(\d+)/)[1], 10);
  system.setValue('playerID', thePlayerId);
  return thePlayerId;
}

export function playerName() {
  return document.getElementById('statbar-character').textContent;
}

export function makePageHeader(title, comment, spanId, button) { // Native
  return '

' +
    ' ' + title + '' +
    (comment === '' ? '' : ' (' + comment + ')') +
    '' +
    (spanId ? '[' + button + ']' : '') +
    '

';
}

export function makePageTemplate(title, comment, spanId, button, divId) { // Native
  return makePageHeader(title, comment, spanId, button) +
    '

';
}

function getMins(obj) {
  var min = 0;
  if (obj.day) {min += parseInt(obj.day, 10) * 1440;}
  if (obj.hour) {min += parseInt(obj.hour, 10) * 60;}
  if (obj.min) {min += parseInt(obj.min, 10);}
  if (obj.last_login) {
    min = Math.floor(Date.now() / 60000) - Math.floor(obj.last_login / 60);
  }
  // last_login is 'false' over 30 days
  if ('last_login' in obj && !obj.last_login) {min = 99999;}
  return min;
}

export function onlineDot(obj) { // Native
  var img;
  var min = getMins(obj);
  if (min < 2) {
    img = greenDiamond;
  } else if (min < 5) {
    img = yellowDiamond;
  } else if (min < 30) {
    img = orangeDiamond;
  } else if (min < 10080) {
    img = offlineDot;
  } else if (min < 44640) {
    img = sevenDayDot;
  } else {img = redDot;}
  return img;
}

function changeOnlineDot(contactLink) { // Native
  var lastActivity = lastActivityRE
    .exec(contactLink.getAttribute('data-tipped'));
  contactLink.parentNode.previousSibling.innerHTML =
    onlineDot({
      min: lastActivity[3],
      hour: lastActivity[2],
      day: lastActivity[1]
    });
}

function batchDots() { // Native
  var limit = performance.now() + 5;
  while (performance.now() < limit &&
      dotCount < dotList.length) {
    changeOnlineDot(dotList[dotCount]);
    dotCount += 1;
  }
  if (dotCount < dotList.length) {
    task.add(3, batchDots);
  }
}

export function colouredDots() { // Native
  if (!system.getValue('enhanceOnlineDots')) {return;}
  dotList = document.querySelectorAll(
    '#pCC a[data-tipped*="Last Activity"]');
  dotCount = 0;
  task.add(3, batchDots);
}

export function confirm(title, msgText, fn) { // jQuery
  var fshMsg = document.getElementById('fshmsg');
  if (!fshMsg) {
    fshMsg = document.createElement('div');
    fshMsg.id = 'fshmsg';
    document.body.appendChild(fshMsg);
    $(fshMsg).dialog({
      autoOpen: false,
      dialogClass: 'no-close',
      draggable: false,
      modal: true,
      resizable: false,
    });
  }
  fshMsg.textContent = msgText;
  $(fshMsg).dialog('option', {
    buttons: {
      Yes: function() {
        fn();
        $(this).dialog('close');
      },
      No: function() {$(this).dialog('close');}
    },
    title: title
  })
  .dialog('open');
}
<commit_msg>reduce complexity of onlineDot<commit_after>import * as system from './system';
import * as task from './task';

var dotList;
var dotCount;
var lastActivityRE =
  /Last Activity:<\/td>(\d+)d (\d+)h (\d+)m (\d+)s<\/td>/;
var redDot = '';
var greenDiamond =
  '';
var yellowDiamond =
  '';
var orangeDiamond =
  '';
var offlineDot =
  '';
var sevenDayDot =
  '';

export var pCC = document.getElementById('pCC');

export function buffAllHref(shortList) { // Bad Pattern
  var _shortList = shortList.join(',').replace(/\s/g, '');
  var j = 'java';
  return j + 'script:openWindow(\'index.php?cmd=quickbuff&t=' + _shortList +
    '\', \'fsQuickBuff\', 618, 1000, \',scrollbars\')';
}

export function quickBuffHref(aPlayerId, buffList) { // Bad Pattern
  return 'href=\'javascript:window.openWindow("index.php?cmd=' +
    'quickbuff&tid=' + aPlayerId + (buffList ? '&blist=' + buffList : '') +
    '", "fsQuickBuff", 618, 1000, ",scrollbars")\'';
}

export function openQuickBuffById(aPlayerId) {
  window.openWindow('index.php?cmd=quickbuff&tid=' + aPlayerId,
    'fsQuickBuff', 618, 1000, ',scrollbars');
}

export function openQuickBuffByName(aPlayerName) {
  window.openWindow('index.php?cmd=quickbuff&t=' + aPlayerName,
    'fsQuickBuff', 618, 1000, ',scrollbars');
}

export function infoBox(documentText) { // Native
  var doc = system.createDocument(documentText);
  var result;
  var infoMsg = doc.getElementById('info-msg');
  if (infoMsg) {
    var infoMatch = infoMsg.innerHTML;
    result = '';
    if (infoMatch) {
      infoMatch = infoMatch.replace(/<br.*/, '');
      result = infoMatch;
    }
  }
  return result;
}

export function guildId() { // Native
  var _guildId;
  var nodeList = document.body.getElementsByTagName('script');
  Array.prototype.forEach.call(nodeList, function getGuildId(el) {
    var match = el.textContent.match(/\s+guildId: ([0-9]+),/);
    if (match) {_guildId = parseInt(match[1], 10);}
  });
  system.setValue('guildId', _guildId);
  return _guildId;
}

export function playerId() { // Native
  var thePlayerId = parseInt(document.getElementById('holdtext')
    .textContent.match(/fallensword.com\/\?ref=(\d+)/)[1], 10);
  system.setValue('playerID', thePlayerId);
  return thePlayerId;
}

export function playerName() {
  return document.getElementById('statbar-character').textContent;
}

export function makePageHeader(title, comment, spanId, button) { // Native
  return '

' +
    ' ' + title + '' +
    (comment === '' ? '' : ' (' + comment + ')') +
    '' +
    (spanId ? '[' + button + ']' : '') +
    '

';
}

export function makePageTemplate(title, comment, spanId, button, divId) { // Native
  return makePageHeader(title, comment, spanId, button) +
    '

';
}

var getMins = [
  function(obj, min) {
    if (obj.day) {return min + parseInt(obj.day, 10) * 1440;}
    return min;
  },
  function(obj, min) {
    if (obj.hour) {return min + parseInt(obj.hour, 10) * 60;}
    return min;
  },
  function(obj, min) {
    if (obj.min) {return min + parseInt(obj.min, 10);}
    return min;
  },
  function(obj, min) {
    if (obj.last_login) {
      return Math.floor(Date.now() / 60000) - Math.floor(obj.last_login / 60);
    }
    return min;
  },
  function(obj, min) {
    // last_login is 'false' over 30 days
    if ('last_login' in obj && !obj.last_login) {return 99999;}
    return min;
  }
];

var getDot = [
  {condition: 2, result: greenDiamond},
  {condition: 5, result: yellowDiamond},
  {condition: 30, result: orangeDiamond},
  {condition: 10080, result: offlineDot},
  {condition: 44640, result: sevenDayDot}
];

export function onlineDot(obj) { // Native
  var img = redDot;
  var min = getMins.reduce(function(prev, curr) {
    return curr(obj, prev);
  }, 0);
  getDot.some(function(el) {
    if (min < el.condition) {
      img = el.result;
      return true;
    }
    return false;
  });
  return img;
}

function changeOnlineDot(contactLink) { // Native
  var lastActivity = lastActivityRE
    .exec(contactLink.getAttribute('data-tipped'));
  contactLink.parentNode.previousSibling.innerHTML =
    onlineDot({
      min: lastActivity[3],
      hour: lastActivity[2],
      day: lastActivity[1]
    });
}

function batchDots() { // Native
  var limit = performance.now() + 5;
  while (performance.now() < limit &&
      dotCount < dotList.length) {
    changeOnlineDot(dotList[dotCount]);
    dotCount += 1;
  }
  if (dotCount < dotList.length) {
    task.add(3, batchDots);
  }
}

export function colouredDots() { // Native
  if (!system.getValue('enhanceOnlineDots')) {return;}
  dotList = document.querySelectorAll(
    '#pCC a[data-tipped*="Last Activity"]');
  dotCount = 0;
  task.add(3, batchDots);
}

export function confirm(title, msgText, fn) { // jQuery
  var fshMsg = document.getElementById('fshmsg');
  if (!fshMsg) {
    fshMsg = document.createElement('div');
    fshMsg.id = 'fshmsg';
    document.body.appendChild(fshMsg);
    $(fshMsg).dialog({
      autoOpen: false,
      dialogClass: 'no-close',
      draggable: false,
      modal: true,
      resizable: false,
    });
  }
  fshMsg.textContent = msgText;
  $(fshMsg).dialog('option', {
    buttons: {
      Yes: function() {
        fn();
        $(this).dialog('close');
      },
      No: function() {$(this).dialog('close');}
    },
    title: title
  })
  .dialog('open');
}
<|endoftext|>