<commit_before>/* eslint-disable react/forbid-prop-types */
import React, { useReducer, useEffect, useCallback, useMemo } from 'react';
import PropTypes from 'prop-types';
import { StyleSheet, View } from 'react-native';

import { ToggleBar, PageButton, Step } from '..';
import { AutocompleteSelector, TextEditor } from '../modalChildren';
import { ModalContainer } from './ModalContainer';

import globalStyles, { DARK_GREY, WARM_GREY, SUSSOL_ORANGE } from '../../globalStyles';

import { SETTINGS_KEYS } from '../../settings';
import {
  getAllPrograms,
  getAllPeriodsForProgram,
  getAllProgramsForCustomer,
} from '../../utilities';
import { programStrings, navStrings } from '../../localization';
import { UIDatabase } from '../../database';
import {
  selectProgram,
  selectSupplier,
  selectOrderType,
  selectCustomer,
  selectPeriod,
  setStoreTags,
  setSteps,
  setModalClosed,
  setModalOpen,
  setToggle,
  setName,
  byProgramReducer,
  initialState,
} from '../../reducers/ByProgramReducer';
import { FlexColumn } from '../FlexColumn';

const { THIS_STORE_TAGS, THIS_STORE_NAME_ID } = SETTINGS_KEYS;

const modalProps = ({ dispatch, program, orderType, customer }) => ({
  program: {
    queryStringSecondary: 'name CONTAINS[c] $0',
    onSelect: value => dispatch(selectProgram(value)),
  },
  customer: {
    secondaryFilterProperty: 'code',
    onSelect: value => dispatch(selectCustomer(value)),
  },
  supplier: {
    secondaryFilterProperty: 'code',
    onSelect: value => dispatch(selectSupplier(value)),
  },
  orderType: {
    secondaryFilterProperty: 'code',
    onSelect: value => dispatch(selectOrderType(value)),
    renderRightText: item => {
      const { maxMOS, maxOPP, threshMOS } = getLocalizedStrings();
      const {
        maxOrdersPerPeriod: maxOrders,
        maxMOS: itemMOS,
        thresholdMOS: itemThreshMOS,
        isEmergency,
      } = item;

      const thisStoresTags = UIDatabase.getSetting(SETTINGS_KEYS.THIS_STORE_TAGS);
      const maxLinesForOrder = program.getMaxLines?.(item.name, thisStoresTags);

      const mosText = `${maxMOS}: ${itemMOS}`;
      const thresholdText = `${threshMOS}: ${itemThreshMOS}`;
      const maxItemsText =
        maxLinesForOrder && maxLinesForOrder !== Infinity
          ? `${programStrings.max_items}: ${maxLinesForOrder}`
          : '';
      const emergencyText = `[${programStrings.emergency_order}]  ${maxItemsText}`;
      const maxOrdersText = isEmergency ? emergencyText : `${maxOPP}: ${maxOrders}`;

      return `${maxOrdersText} - ${mosText} - ${thresholdText}`;
    },
  },
  period: {
    onSelect: value => dispatch(selectPeriod(value)),
    renderRightText: item => {
      const { requisitions } = getLocalizedStrings();
      const { maxOrdersPerPeriod, isEmergency } = orderType;

      const requisitionsInPeriod = !customer
        ? item.numberOfSupplierRequisitionsForOrderType(program, orderType)
        : item.numberOfCustomerRequisitionsForOrderType(program, orderType, customer);
      const requisitionsCount = `${requisitionsInPeriod}/${maxOrdersPerPeriod} ${requisitions}`;

      const periodText = isEmergency
        ? `${requisitionsInPeriod} ${programStrings.emergency_order}`
        : requisitionsCount;

      return `${item} - ${periodText}`;
    },
  },
});

export const ByProgramModal = ({ settings, database, transactionType, onConfirm }) => {
  const [state, dispatch] = useReducer(byProgramReducer, {}, () =>
    initialState({ transactionType })
  );

  const { steps, modalData, program, orderType, period, supplier, name, customer } = state;
  const strings = useMemo(() => getLocalizedStrings({ transactionType }), [transactionType]);

  const mounting = () => {
    dispatch(setStoreTags(settings.get(THIS_STORE_TAGS)));
    dispatch(setSteps(transactionType));
  };
  useEffect(mounting, []);

  /** Helpers */
  // Calculates the current state of a step, according to what data is currently
  // stored in state, where the current step the user should perform is CURRENT,
  // data which is stored is COMPLETE and steps which can't be done yet are INCOMPLETE.
  const getStatusCallback = key =>
    steps.reduceRight((status, value, i) => {
      if (state[key]) return 'COMPLETE';
      const previousStepIsCurrent = state[value] && steps[i + 1] === key;
      const firstStepIsCurrent = status === 'INCOMPLETE' && steps[0] === key;
      if (previousStepIsCurrent || firstStepIsCurrent) return 'CURRENT';
      return status;
    }, 'INCOMPLETE');
  const getStatus = useCallback(getStatusCallback, [
    program,
    orderType,
    supplier,
    name,
    period,
    steps,
    customer,
    state.isProgramBased,
  ]);

  const tags = database
    .objects('NameTag')
    .filtered(
      'subquery(nameTagJoins, $joins, $joins.name.id == $0).@count > 0',
      settings.get(THIS_STORE_NAME_ID)
    )
    .map(({ description }) => description.toLowerCase());

  // Helper methods for fetching modal data for user selection
  const getPrograms = () => {
    if (transactionType === 'customerRequisition') {
      return getAllProgramsForCustomer(customer, database);
    }
    return getAllPrograms(settings, database);
  };
  const getSuppliers = () => database.objects('InternalSupplier');
  const getCustomers = () => database.objects('Customer');
  const getOrderTypes = () => program && program.getStoreTagObject(tags).orderTypes;
  const getPeriods = () => {
    if (!(program && orderType)) return null;
    const { periodScheduleName } = program.getStoreTagObject(tags);
    return getAllPeriodsForProgram(database, program, periodScheduleName, orderType, customer);
  };

  /** Call backs */

  // Dispatcher on clicking a step. Opens the modal and sets the passed data as modalData
  const onOpenModal = ({ key, selection }) => dispatch(setModalOpen({ key, selection }));
  // Closed the currently open modal
  const onCloseModal = () => dispatch(setModalClosed());
  // Switches from program <-> general, resetting the state.
  const onToggle = () => dispatch(setToggle());
  const onCreate = () =>
    onConfirm({
      otherStoreName: supplier || customer,
      program,
      period,
      orderType,
      stocktakeName: name,
    });
  /** Inner components */

  // Togglebar for switching between general <-> program. Resets state on toggle.
  const ProgramToggleBar = () => {
    const { toggleContainer } = localStyles;
    const { isProgramBased } = state;
    const { programToggleText, generalToggleText } = strings;
    return (
      <View style={toggleContainer}>
        <ToggleBar
          toggles={[
            { text: programToggleText, onPress: onToggle, isOn: isProgramBased },
            { text: generalToggleText, onPress: onToggle, isOn: !isProgramBased },
          ]}
        />
      </View>
    );
  };

  // Modal component, for user selection
  const ByProgramSelector = () => {
    const { isModalOpen, currentKey } = state;
    const Selector = () => (
      <AutocompleteSelector
        queryString="code CONTAINS[c] $0"
        sortKeyString="name"
        primaryFilterProperty="name"
        renderLeftText={item => `${item.name}`}
        options={modalData}
        {...modalProps({ dispatch, program, orderType, customer })[currentKey]}
      />
    );
    const Editor = () => (
      <TextEditor text={name} onEndEditing={value => dispatch(setName(value))} />
    );
    return (
      <ModalContainer isVisible={isModalOpen} onClose={onCloseModal} fullScreen>
        {currentKey !== 'name' ? <Selector /> : <Editor />}
      </ModalContainer>
    );
  };

  const stepProps = {
    program: useMemo(
      () => ({
        data: program,
        getModalData: getPrograms,
        onPress: onOpenModal,
        status: getStatus('program'),
        stepKey: 'program',
        type: 'select',
        field: 'name',
      }),
      [program, state.isProgramBased, supplier, customer]
    ),
    customer: useMemo(
      () => ({
        data: customer,
        getModalData: getCustomers,
        onPress: onOpenModal,
        status: getStatus('customer'),
        stepKey: 'customer',
        type: 'select',
        field: 'name',
      }),
      [program, supplier, state.isProgramBased, customer]
    ),
    supplier: useMemo(
      () => ({
        data: supplier,
        getModalData: getSuppliers,
        onPress: onOpenModal,
        status: getStatus('supplier'),
        stepKey: 'supplier',
        type: 'select',
        field: 'name',
      }),
      [program, supplier, state.isProgramBased]
    ),
    orderType: useMemo(
      () => ({
        data: orderType,
        getModalData: getOrderTypes,
        onPress: onOpenModal,
        status: getStatus('orderType'),
        stepKey: 'orderType',
        type: 'select',
        field: 'name',
      }),
      [supplier, orderType, program]
    ),
    period: useMemo(
      () => ({
        data: period,
        getModalData: getPeriods,
        onPress: onOpenModal,
        status: getStatus('period'),
        stepKey: 'period',
        type: 'select',
        field: 'name',
      }),
      [orderType, period, supplier, program]
    ),
    name: useMemo(
      () => ({
        data: name,
        status: getStatus('name'),
        onPress: onOpenModal,
        stepKey: 'name',
        type: 'input',
      }),
      [program, state.isProgramBased, name]
    ),
  };

  /** Render */
  const { okButton, pageButtonTextStyle } = localStyles;
  const { isModalOpen } = state;
  const { currentKey } = state;
  const isDisabled = !(steps[steps.length - 1] === currentKey);

  return (
    <FlexColumn flex={1} alignItems="center">
      {transactionType !== 'customerRequisition' ? <ProgramToggleBar /> : null}
      {steps.map(stepKey => (
        <Step key={stepKey} {...stepProps[stepKey]} />
      ))}
      <PageButton
        text="OK"
        onPress={onCreate}
        isDisabled={isDisabled}
        disabledColor={WARM_GREY}
        style={okButton}
        textStyle={pageButtonTextStyle}
      />
      {isModalOpen && <ByProgramSelector />}
    </FlexColumn>
  );
};

const localStyles = StyleSheet.create({
  modalStyle: {
    ...globalStyles.modal,
    backgroundColor: DARK_GREY,
  },
  toggleContainer: {
    width: 292,
    alignSelf: 'center',
    marginVertical: 20,
  },
  okButton: {
    ...globalStyles.button,
    backgroundColor: SUSSOL_ORANGE,
    marginTop: 'auto',
  },
  pageButtonTextStyle: {
    ...globalStyles.buttonText,
    color: 'white',
    fontSize: 14,
  },
});

const getTitleString = transactionType =>
  `${transactionType === 'stocktake' ? programStrings.stock_take : navStrings.requisition} ${
    programStrings.details
  }`;

const getProgramToggle = transactionType =>
  transactionType === 'stocktake' ? programStrings.program_stocktake : programStrings.program_order;

const getGeneralToggle = transactionType =>
  transactionType === 'stocktake' ? programStrings.general_stocktake : programStrings.general_order;

const getLocalizedStrings = ({ transactionType } = {}) => ({
  title: getTitleString(transactionType),
  programToggleText: getProgramToggle(transactionType),
  generalToggleText: getGeneralToggle(transactionType),
  maxMOS: programStrings.max_mos,
  threshMOS: programStrings.threshold_mos,
  maxOPP: programStrings.max_orders_per_period,
  requisitions: programStrings.requisitions,
});

ByProgramModal.defaultProps = {
  transactionType: 'requisition',
};

ByProgramModal.propTypes = {
  onConfirm: PropTypes.func.isRequired,
  settings: PropTypes.object.isRequired,
  database: PropTypes.object.isRequired,
  transactionType: PropTypes.string,
};

export default ByProgramModal;
<commit_msg>Add use of customers ID to lookup their tags<commit_after>/* eslint-disable react/forbid-prop-types */
import React, { useReducer, useEffect, useCallback, useMemo } from 'react';
import PropTypes from 'prop-types';
import { StyleSheet, View } from 'react-native';

import { ToggleBar, PageButton, Step } from '..';
import { AutocompleteSelector, TextEditor } from '../modalChildren';
import { ModalContainer } from './ModalContainer';

import globalStyles, { DARK_GREY, WARM_GREY, SUSSOL_ORANGE } from '../../globalStyles';

import { SETTINGS_KEYS } from '../../settings';
import {
  getAllPrograms,
  getAllPeriodsForProgram,
  getAllProgramsForCustomer,
} from '../../utilities';
import { programStrings, navStrings } from '../../localization';
import { UIDatabase } from '../../database';
import {
  selectProgram,
  selectSupplier,
  selectOrderType,
  selectCustomer,
  selectPeriod,
  setStoreTags,
  setSteps,
  setModalClosed,
  setModalOpen,
  setToggle,
  setName,
  byProgramReducer,
  initialState,
} from '../../reducers/ByProgramReducer';
import { FlexColumn } from '../FlexColumn';

const { THIS_STORE_TAGS, THIS_STORE_NAME_ID } = SETTINGS_KEYS;

const modalProps = ({ dispatch, program, orderType, customer }) => ({
  program: {
    queryStringSecondary: 'name CONTAINS[c] $0',
    onSelect: value => dispatch(selectProgram(value)),
  },
  customer: {
    secondaryFilterProperty: 'code',
    onSelect: value => dispatch(selectCustomer(value)),
  },
  supplier: {
    secondaryFilterProperty: 'code',
    onSelect: value => dispatch(selectSupplier(value)),
  },
  orderType: {
    secondaryFilterProperty: 'code',
    onSelect: value => dispatch(selectOrderType(value)),
    renderRightText: item => {
      const { maxMOS, maxOPP, threshMOS } = getLocalizedStrings();
      const {
        maxOrdersPerPeriod: maxOrders,
        maxMOS: itemMOS,
        thresholdMOS: itemThreshMOS,
        isEmergency,
      } = item;

      const thisStoresTags = UIDatabase.getSetting(SETTINGS_KEYS.THIS_STORE_TAGS);
      const maxLinesForOrder = program.getMaxLines?.(item.name, thisStoresTags);

      const mosText = `${maxMOS}: ${itemMOS}`;
      const thresholdText = `${threshMOS}: ${itemThreshMOS}`;
      const maxItemsText =
        maxLinesForOrder && maxLinesForOrder !== Infinity
          ? `${programStrings.max_items}: ${maxLinesForOrder}`
          : '';
      const emergencyText = `[${programStrings.emergency_order}]  ${maxItemsText}`;
      const maxOrdersText = isEmergency ? emergencyText : `${maxOPP}: ${maxOrders}`;

      return `${maxOrdersText} - ${mosText} - ${thresholdText}`;
    },
  },
  period: {
    onSelect: value => dispatch(selectPeriod(value)),
    renderRightText: item => {
      const { requisitions } = getLocalizedStrings();
      const { maxOrdersPerPeriod, isEmergency } = orderType;

      const requisitionsInPeriod = !customer
        ? item.numberOfSupplierRequisitionsForOrderType(program, orderType)
        : item.numberOfCustomerRequisitionsForOrderType(program, orderType, customer);
      const requisitionsCount = `${requisitionsInPeriod}/${maxOrdersPerPeriod} ${requisitions}`;

      const periodText = isEmergency
        ? `${requisitionsInPeriod} ${programStrings.emergency_order}`
        : requisitionsCount;

      return `${item} - ${periodText}`;
    },
  },
});

export const ByProgramModal = ({ settings, database, transactionType, onConfirm }) => {
  const [state, dispatch] = useReducer(byProgramReducer, {}, () =>
    initialState({ transactionType })
  );

  const { steps, modalData, program, orderType, period, supplier, name, customer } = state;
  const strings = useMemo(() => getLocalizedStrings({ transactionType }), [transactionType]);

  const mounting = () => {
    dispatch(setStoreTags(settings.get(THIS_STORE_TAGS)));
    dispatch(setSteps(transactionType));
  };
  useEffect(mounting, []);

  /** Helpers */
  // Calculates the current state of a step, according to what data is currently
  // stored in state, where the current step the user should perform is CURRENT,
  // data which is stored is COMPLETE and steps which can't be done yet are INCOMPLETE.
  const getStatusCallback = key =>
    steps.reduceRight((status, value, i) => {
      if (state[key]) return 'COMPLETE';
      const previousStepIsCurrent = state[value] && steps[i + 1] === key;
      const firstStepIsCurrent = status === 'INCOMPLETE' && steps[0] === key;
      if (previousStepIsCurrent || firstStepIsCurrent) return 'CURRENT';
      return status;
    }, 'INCOMPLETE');
  const getStatus = useCallback(getStatusCallback, [
    program,
    orderType,
    supplier,
    name,
    period,
    steps,
    customer,
    state.isProgramBased,
  ]);

  const tags = database
    .objects('NameTag')
    .filtered(
      'subquery(nameTagJoins, $joins, $joins.name.id == $0).@count > 0',
      customer?.id ?? settings.get(THIS_STORE_NAME_ID)
    )
    .map(({ description }) => description.toLowerCase());

  // Helper methods for fetching modal data for user selection
  const getPrograms = () => {
    if (transactionType === 'customerRequisition') {
      return getAllProgramsForCustomer(customer, database);
    }
    return getAllPrograms(settings, database);
  };
  const getSuppliers = () => database.objects('InternalSupplier');
  const getCustomers = () => database.objects('Customer');
  const getOrderTypes = () => program && program.getStoreTagObject(tags).orderTypes;
  const getPeriods = () => {
    if (!(program && orderType)) return null;
    const { periodScheduleName } = program.getStoreTagObject(tags);
    return getAllPeriodsForProgram(database, program, periodScheduleName, orderType, customer);
  };

  /** Call backs */

  // Dispatcher on clicking a step. Opens the modal and sets the passed data as modalData
  const onOpenModal = ({ key, selection }) => dispatch(setModalOpen({ key, selection }));
  // Closed the currently open modal
  const onCloseModal = () => dispatch(setModalClosed());
  // Switches from program <-> general, resetting the state.
  const onToggle = () => dispatch(setToggle());
  const onCreate = () =>
    onConfirm({
      otherStoreName: supplier || customer,
      program,
      period,
      orderType,
      stocktakeName: name,
    });
  /** Inner components */

  // Togglebar for switching between general <-> program. Resets state on toggle.
  const ProgramToggleBar = () => {
    const { toggleContainer } = localStyles;
    const { isProgramBased } = state;
    const { programToggleText, generalToggleText } = strings;
    return (
      <View style={toggleContainer}>
        <ToggleBar
          toggles={[
            { text: programToggleText, onPress: onToggle, isOn: isProgramBased },
            { text: generalToggleText, onPress: onToggle, isOn: !isProgramBased },
          ]}
        />
      </View>
    );
  };

  // Modal component, for user selection
  const ByProgramSelector = () => {
    const { isModalOpen, currentKey } = state;
    const Selector = () => (
      <AutocompleteSelector
        queryString="code CONTAINS[c] $0"
        sortKeyString="name"
        primaryFilterProperty="name"
        renderLeftText={item => `${item.name}`}
        options={modalData}
        {...modalProps({ dispatch, program, orderType, customer })[currentKey]}
      />
    );
    const Editor = () => (
      <TextEditor text={name} onEndEditing={value => dispatch(setName(value))} />
    );
    return (
      <ModalContainer isVisible={isModalOpen} onClose={onCloseModal} fullScreen>
        {currentKey !== 'name' ? <Selector /> : <Editor />}
      </ModalContainer>
    );
  };

  const stepProps = {
    program: useMemo(
      () => ({
        data: program,
        getModalData: getPrograms,
        onPress: onOpenModal,
        status: getStatus('program'),
        stepKey: 'program',
        type: 'select',
        field: 'name',
      }),
      [program, state.isProgramBased, supplier, customer]
    ),
    customer: useMemo(
      () => ({
        data: customer,
        getModalData: getCustomers,
        onPress: onOpenModal,
        status: getStatus('customer'),
        stepKey: 'customer',
        type: 'select',
        field: 'name',
      }),
      [program, supplier, state.isProgramBased, customer]
    ),
    supplier: useMemo(
      () => ({
        data: supplier,
        getModalData: getSuppliers,
        onPress: onOpenModal,
        status: getStatus('supplier'),
        stepKey: 'supplier',
        type: 'select',
        field: 'name',
      }),
      [program, supplier, state.isProgramBased]
    ),
    orderType: useMemo(
      () => ({
        data: orderType,
        getModalData: getOrderTypes,
        onPress: onOpenModal,
        status: getStatus('orderType'),
        stepKey: 'orderType',
        type: 'select',
        field: 'name',
      }),
      [supplier, orderType, program]
    ),
    period: useMemo(
      () => ({
        data: period,
        getModalData: getPeriods,
        onPress: onOpenModal,
        status: getStatus('period'),
        stepKey: 'period',
        type: 'select',
        field: 'name',
      }),
      [orderType, period, supplier, program]
    ),
    name: useMemo(
      () => ({
        data: name,
        status: getStatus('name'),
        onPress: onOpenModal,
        stepKey: 'name',
        type: 'input',
      }),
      [program, state.isProgramBased, name]
    ),
  };

  /** Render */
  const { okButton, pageButtonTextStyle } = localStyles;
  const { isModalOpen } = state;
  const { currentKey } = state;
  const isDisabled = !(steps[steps.length - 1] === currentKey);

  return (
    <FlexColumn flex={1} alignItems="center">
      {transactionType !== 'customerRequisition' ? <ProgramToggleBar /> : null}
      {steps.map(stepKey => (
        <Step key={stepKey} {...stepProps[stepKey]} />
      ))}
      <PageButton
        text="OK"
        onPress={onCreate}
        isDisabled={isDisabled}
        disabledColor={WARM_GREY}
        style={okButton}
        textStyle={pageButtonTextStyle}
      />
      {isModalOpen && <ByProgramSelector />}
    </FlexColumn>
  );
};

const localStyles = StyleSheet.create({
  modalStyle: {
    ...globalStyles.modal,
    backgroundColor: DARK_GREY,
  },
  toggleContainer: {
    width: 292,
    alignSelf: 'center',
    marginVertical: 20,
  },
  okButton: {
    ...globalStyles.button,
    backgroundColor: SUSSOL_ORANGE,
    marginTop: 'auto',
  },
  pageButtonTextStyle: {
    ...globalStyles.buttonText,
    color: 'white',
    fontSize: 14,
  },
});

const getTitleString = transactionType =>
  `${transactionType === 'stocktake' ? programStrings.stock_take : navStrings.requisition} ${
    programStrings.details
  }`;

const getProgramToggle = transactionType =>
  transactionType === 'stocktake' ? programStrings.program_stocktake : programStrings.program_order;

const getGeneralToggle = transactionType =>
  transactionType === 'stocktake' ? programStrings.general_stocktake : programStrings.general_order;

const getLocalizedStrings = ({ transactionType } = {}) => ({
  title: getTitleString(transactionType),
  programToggleText: getProgramToggle(transactionType),
  generalToggleText: getGeneralToggle(transactionType),
  maxMOS: programStrings.max_mos,
  threshMOS: programStrings.threshold_mos,
  maxOPP: programStrings.max_orders_per_period,
  requisitions: programStrings.requisitions,
});

ByProgramModal.defaultProps = {
  transactionType: 'requisition',
};

ByProgramModal.propTypes = {
  onConfirm: PropTypes.func.isRequired,
  settings: PropTypes.object.isRequired,
  database: PropTypes.object.isRequired,
  transactionType: PropTypes.string,
};

export default ByProgramModal;
<|endoftext|>


<commit_before>'use strict';

angular.module('myApp.register', ['ngRoute', 'firebase'])

.config(['$routeProvider', function($routeProvider) {
    $routeProvider.when('/register', {
      templateUrl: 'register/register.html',
      controller: 'RegisterCtrl'
    });
}])

.controller('RegisterCtrl', ['$scope', '$firebaseAuth', function($scope, $firebaseAuth) {
 	$scope.signUp = function() {
 		if (!$scope.regForm.$invalid) {
 			console.log('Valid form submission');
 		}
 	};
}]);<commit_msg>firebase object created for authentication upon creating a new user<commit_after>'use strict';

angular.module('myApp.register', ['ngRoute', 'firebase'])

.config(['$routeProvider', function($routeProvider) {
    $routeProvider.when('/register', {
      templateUrl: 'register/register.html',
      controller: 'RegisterCtrl'
    });
}])

.controller('RegisterCtrl', ['$scope', '$firebaseAuth', function($scope, $firebaseAuth) {
	var firebaseObj = new Firebase("https://blogz-on-fire.firebaseio.com/");
	var auth = $firebaseAuth(firebaseObj);

 	$scope.signUp = function() {
 		if (!$scope.regForm.$invalid) {
 			console.log('Valid form submission');
 		}
 	};
}]);<|endoftext|>


<commit_before>define(['rz_core', 'Bacon'],
function(rz_core,   Bacon) {

function get_rz_core()
{
    // circular dependency on rz_core, so require.js cannot solve it.
    if (rz_core === undefined) {
        rz_core = require('rz_core');
        listen_on_diff_bus(rz_core.main_graph.diffBus);
    }
    return rz_core;
}

var selected_nodes = [],
    selectionChangedBus = new Bacon.Bus();

function listen_on_diff_bus(diffBus)
{
    diffBus
        .filter(".node_set_rm")
        .onValue(function (diff) {
            var node_node_cmp = (function (a, b) { return a.id > b.id; }),
                node_id_cmp = (function (a, b) { return a.id === b ? 0 : (a.id > b ? 1 : -1); });

            updateSelectedNodesBus(sortedArrayDiff(selected_nodes.sort(node_node_cmp),
                                             diff.node_set_rm.sort(), node_id_cmp));
        });
}

function sortedArrayDiff(a, b, a_cmp_b)
{
    var a_i = 0,
        b_i = 0,
        ret = [];

    while (a_i < a.length && b_i < b.length) {
        while (a_i < a.length && a_cmp_b(a[a_i], b[b_i]) == -1) {
            ret.push(a[a_i]);
            a_i += 1;
        }
        while (b_i < b.length && a_i < a.length && a_cmp_b(a[a_i], b[b_i]) == 0) {
            b_i += 1;
            a_i += 1;
        }
        while (b_i < b.length && a_cmp_b(a[a_i], b[b_i]) == 1) {
            b_i += 1;
        }
    }
    for (; a_i < a.length ; ++a_i) {
        ret.push(a[a_i]);
    }
    return ret;
}

function updateSelectedNodesBus(new_selected_nodes)
{
    selected_nodes = new_selected_nodes;
    selectionChangedBus.push(selected_nodes);
}

function byVisitors(node_selector, link_selector) {
    var new_selected_nodes = get_rz_core().main_graph.find__by_visitors(node_selector, link_selector);

    clear();
    connectedComponent(new_selected_nodes);
}

function connectedComponent(nodes) {
    var connected = get_rz_core().main_graph.getConnectedNodesAndLinks(nodes, 1),
        i,
        node,
        link,
        data;

    updateSelectedNodesBus(nodes.map(function(x) { return x; }));

    for (i = 0 ; i < connected.nodes.length ; ++i) {
        data = connected.nodes[i];
        node = data.node;
        switch (data.type) {
        case 'exit':
            node.state = 'exit';
            break;
        case 'enter':
            node.state = 'enter';
            break;
        };
    }
    for (i = 0 ; i < connected.links.length ; ++i) {
        data = connected.links[i];
        link = data.link;
        switch (data.type) {
        case 'exit':
            link.state = 'exit';
            break;
        case 'enter':
            link.state = 'enter';
            break;
        };
    }
    nodes.forEach(function (n) { n.state = 'chosen'; });
}

var node_selected = function(node) {
    return node.state == 'chosen' || node.state == 'enter' || node.state == 'exit' || node.state == 'selected'
           || node.state == 'temp' || node.state == 'related';
}

var selected_class = function(node) {
    return selected_nodes.length > 0 ? (node_selected(node) ? "selected" : "notselected") : "";
}

var clear = function() {
    selected_nodes.length = 0;
    get_rz_core().main_graph.setRegularState();
}

function arr_compare(a1, a2)
{
    if (a1.length != a2.length) {
        return false;
    }
    for (var i = 0 ; i < a1.length ; ++i) {
        if (a1[i] != a2[i]) {
            return false;
        }
    }
    return true;
}

var update = function(nodes) {
    var set = !arr_compare(nodes, selected_nodes);
    clear();
    if (set) {
        connectedComponent(nodes);
    }
}

return {
    byVisitors: byVisitors,
    connectedComponent: connectedComponent,
    clear: clear,
    update: update,
    selected_class: selected_class,
    node_selected: node_selected,
    selectionChangedBus: selectionChangedBus,
};

});
<commit_msg>client/selection: fix node selection, use hash for selection instead of Node.state; link selection still broken<commit_after>define(['rz_core', 'Bacon'],
function(rz_core,   Bacon) {

function get_rz_core()
{
    // circular dependency on rz_core, so require.js cannot solve it.
    if (rz_core === undefined) {
        rz_core = require('rz_core');
        listen_on_diff_bus(rz_core.main_graph.diffBus);
    }
    return rz_core;
}

var selected_nodes = [],
    selected_nodes__by_id = {},
    selectionChangedBus = new Bacon.Bus();

function listen_on_diff_bus(diffBus)
{
    diffBus
        .filter(".node_set_rm")
        .onValue(function (diff) {
            var node_node_cmp = (function (a, b) { return a.id > b.id; }),
                node_id_cmp = (function (a, b) { return a.id === b ? 0 : (a.id > b ? 1 : -1); });

            updateSelectedNodesBus(sortedArrayDiff(selected_nodes.sort(node_node_cmp),
                                             diff.node_set_rm.sort(), node_id_cmp));
        });
}

function sortedArrayDiff(a, b, a_cmp_b)
{
    var a_i = 0,
        b_i = 0,
        ret = [];

    while (a_i < a.length && b_i < b.length) {
        while (a_i < a.length && a_cmp_b(a[a_i], b[b_i]) == -1) {
            ret.push(a[a_i]);
            a_i += 1;
        }
        while (b_i < b.length && a_i < a.length && a_cmp_b(a[a_i], b[b_i]) == 0) {
            b_i += 1;
            a_i += 1;
        }
        while (b_i < b.length && a_cmp_b(a[a_i], b[b_i]) == 1) {
            b_i += 1;
        }
    }
    for (; a_i < a.length ; ++a_i) {
        ret.push(a[a_i]);
    }
    return ret;
}

function updateSelectedNodesBus(new_selected_nodes)
{
    selected_nodes = new_selected_nodes;
    selected_nodes__by_id = selected_nodes.reduce(
        function(d, v) {
            d[v.id] = v;
            return d;
        }, {});
    selectionChangedBus.push(selected_nodes);
}

function byVisitors(node_selector, link_selector) {
    var new_selected_nodes = get_rz_core().main_graph.find__by_visitors(node_selector, link_selector);

    clear();
    connectedComponent(new_selected_nodes);
}

function connectedComponent(nodes) {
    var connected = get_rz_core().main_graph.getConnectedNodesAndLinks(nodes, 1),
        i,
        node,
        link,
        data;

    updateSelectedNodesBus(nodes.map(function(x) { return x; }));

    for (i = 0 ; i < connected.nodes.length ; ++i) {
        data = connected.nodes[i];
        node = data.node;
        switch (data.type) {
        case 'exit':
            node.state = 'exit';
            break;
        case 'enter':
            node.state = 'enter';
            break;
        };
    }
    for (i = 0 ; i < connected.links.length ; ++i) {
        data = connected.links[i];
        link = data.link;
        switch (data.type) {
        case 'exit':
            link.state = 'exit';
            break;
        case 'enter':
            link.state = 'enter';
            break;
        };
    }
    nodes.forEach(function (n) { n.state = 'chosen'; });
}

var node_selected = function(node) {
    return selected_nodes__by_id[node.id] !== undefined;
}

var selected_class = function(node) {
    return selected_nodes.length > 0 ? (node_selected(node) ? "selected" : "notselected") : "";
}

var clear = function() {
    updateSelectedNodesBus([]);
    get_rz_core().main_graph.setRegularState();
}

function arr_compare(a1, a2)
{
    if (a1.length != a2.length) {
        return false;
    }
    for (var i = 0 ; i < a1.length ; ++i) {
        if (a1[i] != a2[i]) {
            return false;
        }
    }
    return true;
}

var update = function(nodes) {
    var set = !arr_compare(nodes, selected_nodes);
    clear();
    if (set) {
        connectedComponent(nodes);
    }
}

return {
    byVisitors: byVisitors,
    connectedComponent: connectedComponent,
    clear: clear,
    update: update,
    selected_class: selected_class,
    node_selected: node_selected,
    selectionChangedBus: selectionChangedBus,
};

});
<|endoftext|>


<commit_before>steal.then(function() {
	/**
	 * @class DocumentJS.Tags.constructor
	 * @tag documentation
	 * @parent DocumentJS.Tags
	 * Documents javascript constructor classes typically created like: new MyContructor(args).
	 * A constructor can be described by putting @constructor as the first declaritive.
	 * To describe the construction function, write that after init. Example:
	 *
	 * @codestart
	 *  /* @constructor
	 *   * Person represents a human with a name
	 *   * You must pass in a name.
	 *   * @params {String} name A person's name
	 *   *|
	 *  Person = function(name){
	 *     this.name = name
	 *     Person.count ++;
	 *  }
	 *  /* @Static *|
	 *  steal.Object.extend(Person, {
	 *    /* Number of People *|
	 *    count: 0
	 *  })
	 *  /* @Prototype *|
	 *  Person.prototype = {
	 *    /* Returns a formal name
	 *     * [DocumentJS.Tags.return | @return] {String} the name with "Mrs." added
	 *     *|
	 *    fancy_name : function(){
	 *       return "Mrs. "+this.name;
	 *    }
	 *  }
	 * @codeend
	 */
	DocumentJS.Tags.constructor =
/*
 * @Static
 */
	{
		add: function( line ) {
			var parts = line.match(/\s?@constructor(.*)?/);

			this.construct = parts && parts[1] ? " " + parts[1] + "\n" : ""
			return ["default", 'construct'];
		}
	};
})<commit_msg>Cleaned up contructor tag documentation.<commit_after>steal.then(function() {
	/**
	 * @class DocumentJS.Tags.constructor
	 * @tag documentation
	 * @parent DocumentJS.Tags
	 * Documents javascript constructor classes typically created like: new MyContructor(args).
	 *
	 * A constructor can be described by putting @constructor as the first declaritive.
	 *
	 * ###Example:
	 *
	 * @codestart
	 *  /* @constructor
	 *   * Person represents a human with a name
	 *   * You must pass in a name.
	 *   * @params {String} name A person's name
	 *   *|
	 *  Person = function(name){
	 *     this.name = name
	 *     Person.count ++;
	 *  }
	 *  /* @Static *|
	 *  steal.Object.extend(Person, {
	 *    /* Number of People *|
	 *    count: 0
	 *  })
	 *  /* @Prototype *|
	 *  Person.prototype = {
	 *    /* Returns a formal name
	 *     * [DocumentJS.Tags.return | @return] {String} the name with "Mrs." added
	 *     *|
	 *    fancy_name : function(){
	 *       return "Mrs. "+this.name;
	 *    }
	 *  }
	 * @codeend
	 */
	DocumentJS.Tags.constructor =
/*
 * @Static
 */
	{
		add: function( line ) {
			var parts = line.match(/\s?@constructor(.*)?/);

			this.construct = parts && parts[1] ? " " + parts[1] + "\n" : ""
			return ["default", 'construct'];
		}
	};
})<|endoftext|>


<commit_before>const config = require('config')
const Twitter = require('twitter')
const User = require('../../models/user.js')
const { ERRORS } = require('../../../lib/util')
const logger = require('../../../lib/logger.js')()

exports.get = async function (req, res) {
  // Flag error if user ID is not provided
  if (!req.params.user_id) {
    res.status(400).send('Please provide user ID.')
    return
  }

  const targetUserId = req.params.user_id
  let targetUser

  const findUserById = async function (userId) {
    try {
      targetUser = await User.findOne({ id: userId })
    } catch (err) {
      logger.error(err)
      throw new Error(ERRORS.CANNOT_GET_USER)
    }

    if (!targetUser) {
      throw new Error(ERRORS.USER_NOT_FOUND)
    }

    return targetUser
  }

  let requestUser

  const findUserByLoginToken = async function (loginToken) {
    try {
      requestUser = await User.findOne({ login_tokens: { $in: [ loginToken ] } })
    } catch (err) {
      logger.error(err)
      throw new Error(ERRORS.CANNOT_GET_USER)
    }

    if (!requestUser) {
      throw new Error(ERRORS.UNAUTHORISED_ACCESS)
    }

    // Finding target user
    if (req.user_id !== targetUserId) {
      try {
        targetUser = await findUserById(targetUserId)
      } catch (error) {
        logger.error(error)
        throw new Error(ERRORS.CANNOT_GET_USER)
      }

      if (!targetUser) {
        throw new Error(ERRORS.UNAUTHORISED_ACCESS)
      }
    } else {
      targetUser = requestUser
    }

    return targetUser
  }

  const handleFindUser = function (user) {
    let twitterApiClient

    try {
      twitterApiClient = new Twitter({
        consumer_key: config.twitter.oauth_consumer_key,
        consumer_secret: config.twitter.oauth_consumer_secret,
        access_token_key: user.twitter_credentials.access_token_key,
        access_token_secret: user.twitter_credentials.access_token_secret
      })
    } catch (error) {
      logger.error('Could not initialize Twitter API client: ' + error)
    }

    const sendUserJson = function (data) {
      // If requestUser = targetUser => permission granted
      // If requestUser exists and requestUser != targetUser and requestUser is admin => permission granted
      const auth = (user.login_tokens.indexOf(req.loginToken) !== -1) || (requestUser && requestUser.roles.includes('ADMIN'))

      user.asJson({ auth: auth }, function (err, userJson) {
        if (err) {
          logger.error(err)
          res.status(500).send('Could not render user JSON.')
          return
        }

        if (data) {
          userJson.profileImageUrl = data.twitter_profile_image_url
        } else {
          userJson.profileImageUrl = user.profile_image_url
        }

        res.status(200).send(userJson)
      })
    } // END function - sendUserJson

    let responseAlreadySent = false

    const handleFetchUserProfileFromTwitter = function (err, res) {
      if (err) {
        logger.error('Twitter API call users/show returned error.')
        logger.error(err)
      }

      if (responseAlreadySent) {
        logger.debug({ profile_image_url: res.profile_image_url }, 'Twitter API users/show call returned but response already sent!')
      } else {
        logger.debug({ profile_image_url: res.profile_image_url }, 'Twitter API users/show call returned. Sending response with Twitter data.')
        responseAlreadySent = true

        if (!res) {
          logger.error('Twitter API call users/show did not return any data.')
        }

        sendUserJson({
          twitter_profile_image_url: res.picture
        })
      }
    } // END function - handleFetchUserProfileFromTwitter

    if (twitterApiClient && !user.profile_image_url) {
      logger.debug('About to call Twitter API: /users/show.json?user_id=' + user.twitter_id)
      twitterApiClient.get('/users/show.json', { user_id: user.twitter_id }, handleFetchUserProfileFromTwitter)
      setTimeout(function () {
        if (!responseAlreadySent) {
          logger.debug(`Timing out Twitter API call after %d milliseconds and sending partial response.`, config.twitter.timeout_ms)
          responseAlreadySent = true
          sendUserJson()
        }
      }, config.twitter.timeout_ms)
    } else {
      sendUserJson()
    }
  } // END function - handleFindUser

  const handleError = function (error) {
    switch (error) {
      case ERRORS.USER_NOT_FOUND:
        res.status(404).send('User not found.')
        return
      case ERRORS.CANNOT_GET_USER:
        res.status(500).send('Error finding user.')
        return
      case ERRORS.UNAUTHORISED_ACCESS:
        res.status(401).send('User with that login token not found.')
        return
      default:
        res.status(500).end()
    }
  }

  if (req.loginToken) {
    findUserByLoginToken(req.loginToken)
      .then(handleFindUser)
      .catch(handleError)
  } else {
    findUserById(targetUserId)
      .then(handleFindUser)
      .catch(handleError)
  }
}

exports.put = async function (req, res) {
  let body
  try {
    body = req.body
  } catch (e) {
    res.status(400).send('Could not parse body as JSON.')
    return
  }

  // Flag error if user ID is not provided
  if (!req.params.user_id) {
    res.status(400).send('Please provide user ID.')
    return
  }

  // 1. on auth header, check userId matches their login token
  // 2a. if auth.userId = params.userId => permission granted
  // 2b. if auth.userId != params.userId, auth.userId IS admin => permission granted

  // Find requesting user
  const userId = req.userId
  let user

  try {
    user = await User.findOne({ id: userId })
  } catch (err) {
    logger.error(err)
    res.status(500).send('Error finding user.')
  }

  if (!user) {
    res.status(404).send('User not found.')
    return
  }

  // Is requesting user logged in?
  if (user.login_tokens.indexOf(req.loginToken) === -1) {
    res.status(401).end()
    return
  }

  // Find target user
  const targetUserId = req.params.user_id
  let targetUser
  if (userId !== targetUserId) {
    try {
      targetUser = await User.findOne({ id: targetUserId })
    } catch (err) {
      logger.error(err)
      res.status(500).send('Error finding user.')
    }

    if (!targetUser) {
      res.status(404).send('User not found.')
      return
    }
  } else {
    targetUser = user
  }

  if (userId === targetUserId || user.roles.includes('ADMIN')) {
    targetUser.data = body.data || targetUser.data
    targetUser.flags = body.flags || targetUser.flags

    targetUser.roles = body.roles || targetUser.roles
    targetUser.id = body.id || targetUser.id
    targetUser.profile_image_url = body.profileImageUrl || targetUser.profile_image_url

    targetUser.save().then(user => {
      res.status(204).end()
    }).catch(err => {
      logger.error(err)
      res.status(500).send('Could not update user information.')
    })
  } else {
    res.status(401).end()
  }
} // END function - exports.put

exports.delete = async function (req, res) {
  if (!req.params.user_id) {
    res.status(400).send('Please provide user ID.')
    return
  } else if (!req.loginToken) {
    res.status(400).send('Please provide login token.')
  }

  const userId = req.userId
  const targetUserId = req.params.user_id

  let requestUser

  try {
    requestUser = await User.findOne({ id: userId })
  } catch (error) {
    logger.error(error)
    res.status(500).send('Error finding user.')
  }

  if (!requestUser) {
    res.status(404).send('User not found.')
    return
  }

  // Is requesting user logged in?
  if (requestUser.login_tokens.indexOf(req.loginToken) === -1) {
    res.status(401).end()
    return
  }

  let targetUser
  if (targetUserId !== userId) {
    try {
      targetUser = await User.findOne({ id: targetUserId })
    } catch (error) {
      logger.error(error)
      res.status(500).send('Error finding user.')
    }

    if (!targetUser) {
      res.status(404).send('User not found.')
      return
    }
  } else {
    targetUser = requestUser
  }

  const handleDeleteUser = function (err, user) {
    if (err) {
      logger.error(err)
      res.status(500).send('Error deleting user.')
    }

    if (!user) {
      res.status(404).send('User not found.')
      return
    }

    res.status(204).end()
  }

  if (userId === targetUserId || requestUser.roles.includes('ADMIN')) {
    User.deleteOne({ id: targetUserId }, handleDeleteUser)
  } else {
    res.status(401).end()
  }
}
<commit_msg>when user is successfully deleted, delete corresponding profile image from cloudinary<commit_after>const config = require('config')
const Twitter = require('twitter')
const cloudinary = require('cloudinary')
const User = require('../../models/user.js')
const { ERRORS } = require('../../../lib/util')
const logger = require('../../../lib/logger.js')()

exports.get = async function (req, res) {
  // Flag error if user ID is not provided
  if (!req.params.user_id) {
    res.status(400).send('Please provide user ID.')
    return
  }

  const targetUserId = req.params.user_id
  let targetUser

  const findUserById = async function (userId) {
    try {
      targetUser = await User.findOne({ id: userId })
    } catch (err) {
      logger.error(err)
      throw new Error(ERRORS.CANNOT_GET_USER)
    }

    if (!targetUser) {
      throw new Error(ERRORS.USER_NOT_FOUND)
    }

    return targetUser
  }

  let requestUser

  const findUserByLoginToken = async function (loginToken) {
    try {
      requestUser = await User.findOne({ login_tokens: { $in: [ loginToken ] } })
    } catch (err) {
      logger.error(err)
      throw new Error(ERRORS.CANNOT_GET_USER)
    }

    if (!requestUser) {
      throw new Error(ERRORS.UNAUTHORISED_ACCESS)
    }

    // Finding target user
    if (req.user_id !== targetUserId) {
      try {
        targetUser = await findUserById(targetUserId)
      } catch (error) {
        logger.error(error)
        throw new Error(ERRORS.CANNOT_GET_USER)
      }

      if (!targetUser) {
        throw new Error(ERRORS.UNAUTHORISED_ACCESS)
      }
    } else {
      targetUser = requestUser
    }

    return targetUser
  }

  const handleFindUser = function (user) {
    let twitterApiClient

    try {
      twitterApiClient = new Twitter({
        consumer_key: config.twitter.oauth_consumer_key,
        consumer_secret: config.twitter.oauth_consumer_secret,
        access_token_key: user.twitter_credentials.access_token_key,
        access_token_secret: user.twitter_credentials.access_token_secret
      })
    } catch (error) {
      logger.error('Could not initialize Twitter API client: ' + error)
    }

    const sendUserJson = function (data) {
      // If requestUser = targetUser => permission granted
      // If requestUser exists and requestUser != targetUser and requestUser is admin => permission granted
      const auth = (user.login_tokens.indexOf(req.loginToken) !== -1) || (requestUser && requestUser.roles.includes('ADMIN'))

      user.asJson({ auth: auth }, function (err, userJson) {
        if (err) {
          logger.error(err)
          res.status(500).send('Could not render user JSON.')
          return
        }

        if (data) {
          userJson.profileImageUrl = data.twitter_profile_image_url
        } else {
          userJson.profileImageUrl = user.profile_image_url
        }

        res.status(200).send(userJson)
      })
    } // END function - sendUserJson

    let responseAlreadySent = false

    const handleFetchUserProfileFromTwitter = function (err, res) {
      if (err) {
        logger.error('Twitter API call users/show returned error.')
        logger.error(err)
      }

      if (responseAlreadySent) {
        logger.debug({ profile_image_url: res.profile_image_url }, 'Twitter API users/show call returned but response already sent!')
      } else {
        logger.debug({ profile_image_url: res.profile_image_url }, 'Twitter API users/show call returned. Sending response with Twitter data.')
        responseAlreadySent = true

        if (!res) {
          logger.error('Twitter API call users/show did not return any data.')
        }

        sendUserJson({
          twitter_profile_image_url: res.picture
        })
      }
    } // END function - handleFetchUserProfileFromTwitter

    if (twitterApiClient && !user.profile_image_url) {
      logger.debug('About to call Twitter API: /users/show.json?user_id=' + user.twitter_id)
      twitterApiClient.get('/users/show.json', { user_id: user.twitter_id }, handleFetchUserProfileFromTwitter)
      setTimeout(function () {
        if (!responseAlreadySent) {
          logger.debug(`Timing out Twitter API call after %d milliseconds and sending partial response.`, config.twitter.timeout_ms)
          responseAlreadySent = true
          sendUserJson()
        }
      }, config.twitter.timeout_ms)
    } else {
      sendUserJson()
    }
  } // END function - handleFindUser

  const handleError = function (error) {
    switch (error) {
      case ERRORS.USER_NOT_FOUND:
        res.status(404).send('User not found.')
        return
      case ERRORS.CANNOT_GET_USER:
        res.status(500).send('Error finding user.')
        return
      case ERRORS.UNAUTHORISED_ACCESS:
        res.status(401).send('User with that login token not found.')
        return
      default:
        res.status(500).end()
    }
  }

  if (req.loginToken) {
    findUserByLoginToken(req.loginToken)
      .then(handleFindUser)
      .catch(handleError)
  } else {
    findUserById(targetUserId)
      .then(handleFindUser)
      .catch(handleError)
  }
}

exports.put = async function (req, res) {
  let body
  try {
    body = req.body
  } catch (e) {
    res.status(400).send('Could not parse body as JSON.')
    return
  }

  // Flag error if user ID is not provided
  if (!req.params.user_id) {
    res.status(400).send('Please provide user ID.')
    return
  }

  // 1. on auth header, check userId matches their login token
  // 2a. if auth.userId = params.userId => permission granted
  // 2b. if auth.userId != params.userId, auth.userId IS admin => permission granted

  // Find requesting user
  const userId = req.userId
  let user

  try {
    user = await User.findOne({ id: userId })
  } catch (err) {
    logger.error(err)
    res.status(500).send('Error finding user.')
  }

  if (!user) {
    res.status(404).send('User not found.')
    return
  }

  // Is requesting user logged in?
  if (user.login_tokens.indexOf(req.loginToken) === -1) {
    res.status(401).end()
    return
  }

  // Find target user
  const targetUserId = req.params.user_id
  let targetUser
  if (userId !== targetUserId) {
    try {
      targetUser = await User.findOne({ id: targetUserId })
    } catch (err) {
      logger.error(err)
      res.status(500).send('Error finding user.')
    }

    if (!targetUser) {
      res.status(404).send('User not found.')
      return
    }
  } else {
    targetUser = user
  }

  if (userId === targetUserId || user.roles.includes('ADMIN')) {
    targetUser.data = body.data || targetUser.data
    targetUser.flags = body.flags || targetUser.flags

    targetUser.roles = body.roles || targetUser.roles
    targetUser.id = body.id || targetUser.id
    targetUser.profile_image_url = body.profileImageUrl || targetUser.profile_image_url

    targetUser.save().then(user => {
      res.status(204).end()
    }).catch(err => {
      logger.error(err)
      res.status(500).send('Could not update user information.')
    })
  } else {
    res.status(401).end()
  }
} // END function - exports.put

exports.delete = async function (req, res) {
  if (!req.params.user_id) {
    res.status(400).send('Please provide user ID.')
    return
  } else if (!req.loginToken) {
    res.status(400).send('Please provide login token.')
  }

  const userId = req.userId
  const targetUserId = req.params.user_id

  let requestUser

  try {
    requestUser = await User.findOne({ id: userId })
  } catch (error) {
    logger.error(error)
    res.status(500).send('Error finding user.')
  }

  if (!requestUser) {
    res.status(404).send('User not found.')
    return
  }

  // Is requesting user logged in?
  if (requestUser.login_tokens.indexOf(req.loginToken) === -1) {
    res.status(401).end()
    return
  }

  let targetUser
  if (targetUserId !== userId) {
    try {
      targetUser = await User.findOne({ id: targetUserId })
    } catch (error) {
      logger.error(error)
      res.status(500).send('Error finding user.')
    }

    if (!targetUser) {
      res.status(404).send('User not found.')
      return
    }
  } else {
    targetUser = requestUser
  }

  const handleDeleteUser = async function (err, user) {
    if (err) {
      logger.error(err)
      res.status(500).send('Error deleting user.')
    }

    if (!user) {
      res.status(404).send('User not found.')
      return
    }

    // If successful in deleting user, delete user's profile image cached in cloudinary.
    cloudinary.config({
      cloud_name: 'streetmix',
      api_key: config.cloudinary.api_key,
      api_secret: config.cloudinary.api_secret
    })

    const publicId = `${config.env}/profile_image/${targetUserId}`

    try {
      await cloudinary.v2.uploader.destroy(publicId)
    } catch (error) {
      // If unable to delete user's profile image from cloudinary, log error and continue.
      logger.error(error)
    }

    res.status(204).end()
  }

  if (userId === targetUserId || requestUser.roles.includes('ADMIN')) {
    User.deleteOne({ id: targetUserId }, handleDeleteUser)
  } else {
    res.status(401).end()
  }
}
<|endoftext|>


<commit_before>import Route from '@ember/routing/route';

export default Route.extend({

  actions: {

    resetRoute() {
      this.debug("not implemented");
    }
  }

});
<commit_msg>changed debug message in resetRoute action to an error<commit_after>import Route from '@ember/routing/route';

export default Route.extend({

  actions: {

    resetRoute() {
      const routeName = this.get('routeName');

      throw new Error(`UnsupportedOperationException: Please implement the
      'resetRoute' action in the '${routeName}' route`);
    }
  }

});
<|endoftext|>


<commit_before>var router = require('express').Router(),
	async = require('async'),
	_ = require('lodash'),
	privileges = require('../../helpers/access').privilege,
	Game = require('../../models/game'),
	GameArchive = require('../../models/game-archive'),
	User = require('../../models/user'),
	Release = require('../../models/release'),
	log = require('../../helpers/logger');

/**
 * Abstraction to handle the page errors
 * @param  {String|Array} errors Single or collection of errors
 */
function handleError(req, res, errors)
{
	log.error(errors);
	if (Array.isArray(errors))
	{
		req.flash('errors', errors);
	}
	else
	{
		req.flash('error', 'Unable to update the game: ' + errors);
	}
	res.redirect(req.originalUrl);
}

/**
 * Abstraction to render a page, takes care of all
 * of the access control and populate the page with
 * standard parameters
 * @param  {Object} req The express request object
 * @param {Object} res The express response object
 * @param {string} template The template to use
 * @param {String} [populate='groups.group'] The game fields to populate
 * @param {function} [success] Callback when completed
 */
function renderPage(req, res, template, populate, success)
{
	populate = ['groups.group'].concat(populate || []);
	async.waterfall(
		[
			function(done)
			{
				Game.getBySlug(req.params.slug, done)
					.populate(populate);
			},
			function(game, done)
			{
				if (populate.indexOf('releases'))
				{
					User.populate(game.releases, {
						path: 'updatedBy',
						select: 'name'
					},
					function(err, releases)
					{
						if (err) return done(err);
						done(null, game);
					});
				}
				else
				{
					done(null, game);
				}
			},
			function(game, done)
			{
				if (!game) return res.status(404).render('404');

				game.getAccess(req.user, done);
			}
		],
		function(err, game, access)
		{
			if (err)
			{
				return res.status(401).render('401');
			}
			if (success) success(game);

			res.render(template, {
				game: game,
				capabilities: game.capabilities,
				host: req.headers.host,
				isEditor: access.permission > 0,
				isAdmin: access.permission > 1,
				token: access.token,
				errors: req.flash('errors'),
				success: req.flash('success'),
				error: req.flash('error')
			});
		}
	);
}

/**
 * Abstraction to post a page, takes care of all
 * of the access control and populate the page with
 * standard parameters.
 * @param  {Object} req The express request object
 * @param {Object} res The express response object
 * @param {string} minPrivilege The minimum privilege needed to run all form
 * @param {Object} actions Run custom page actions
 */
function postPage(req, res, minPrivilege, actions)
{
	async.waterfall(
		[
			function(done)
			{
				Game.getBySlug(req.params.slug, done).populate('groups.group');
			},
			function(game, done)
			{
				if (!game) return res.status(404).render('404');

				game.getAccess(req.user, done);
			},
			function(game, access, done)
			{
				var response = function(err)
				{
					if (err) return done(err);
					done(null, game);
				};

				if (access.permission < minPrivilege)
				{
					return done('Invalid form permissions');
				}

				if (!req.body.action || !actions[req.body.action])
				{
					done('Invalid form action');
				}
				actions[req.body.action](response, game, access);
			}
		],
		function(err, game)
		{
			if (err) return handleError(req, res, err);

			req.flash('success', game.title + " updated successfully");
			res.redirect(req.originalUrl);
		}
	);
}

function defaultCapabilities(capabilities)
{
	capabilities.ui = _.assign({
			mouse: false,
			touch: false
		}, capabilities.ui);

	capabilities.sizes = _.assign({
			xsmall: false,
			small: false,
			medium: false,
			large: false,
			xlarge: false
		}, capabilities.sizes);
}

// Generic reponse handler, must bind to game instance
function response(err)
{
	if (err) return done(err);
	done(null, this);
}

router.post('/:slug/releases', function(req, res)
{
	postPage(req, res, privileges.editor,
	{
		removeRelease: function(done)
		{
			Release.removeById(
				req.body.release,
				done
			);
		},
		statusChange: function(done)
		{
			Release.findByIdAndUpdate(
				req.body.release,
				{
					status: req.body.status,
					updated: Date.now(),
					updatedBy: req.body.updatedBy
				},
				done
			);
		}
	});
});

router.post('/:slug/privileges', function(req, res)
{
	postPage(req, res, privileges.admin,
	{
		removeGroup: function(done, game)
		{
			game.removeGroup(
				req.body.group,
				done
			);
		},
		addGroup: function(done, game)
		{
			game.addGroup(
				req.body.group,
				req.body.permission,
				done
			);
		},
		changePermission: function(done, game)
		{
			game.changePermission(
				req.body.group,
				req.body.permission,
				done
			);
		}
	});
});

router.post('/:slug/release', function(req, res)
{
	postPage(req, res, privileges.editor,
	{
		selectRelease: function(done, game, access)
		{
			Release.getById(req.body.release, function(err, release)
			{
				if (err) return done(err);

				res.render("games/release", {
					game: game,
					release: release,
					capabilities: release.capabilities,
					host: req.headers.host,
					isEditor: access.permission > 0,
					isAdmin: access.permission > 1,
					token: access.token,
					errors: req.flash('errors'),
					success: req.flash('success'),
					error: req.flash('error')
				});
			});
		},
		removeRelease: function(done, game)
		{
			Release.removeById(req.body.release, function(err)
			{
				if (err) return done(err);
				req.flash('success', 'Deleted release');
				res.redirect('/games/game/' + game.slug + '/releases');
			});
		},
		updateRelease: function(done, game)
		{
			req.checkBody('commitId', 'Commit is a Git hash').isCommit();
			req.checkBody('status', 'Status must be a valid status').isStatus();
			req.checkBody('notes').optional();

			if (req.body.version)
				req.checkBody('version', 'Version must be a valid semantic version').isSemver();

			defaultCapabilities(req.body.capabilities);

			var errors = req.validationErrors();

			if (errors) return done(errors);

			req.body.updated = Date.now();
			Release.findByIdAndUpdate(req.body.release, req.body, done);
		}
	});
});

router.post('/:slug', function(req, res)
{
	postPage(req, res, privileges.editor,
	{
		updateGame: function(done, game)
		{
			req.checkBody('title', 'Title is required').notEmpty();
			req.checkBody('bundleId', 'Bundle ID is required').isBundleId();
			req.checkBody('slug', 'Slug is required').isSlug();
			req.checkBody('repository', 'Repository needs to be a URL').isURL();
			req.checkBody('location', 'Location needs to be a URL').isURL();
			req.checkBody('description').optional();
			req.checkBody('thumbnail').optional();

			var errors = req.validationErrors();

			if (errors) return done(errors);

			// Set defaults to false for capabilities
			// so the default that are set to true actually
			// get updated
			defaultCapabilities(req.body.capabilities);

			req.body.updated = Date.now();

			// Update the game
			Game.findByIdAndUpdate(game._id, req.body, done);
		},
		removeGame: function(done, game)
		{
			var gameArchive = new GameArchive(game.toObject());

			gameArchive.save(function(err, archive)
			{
				if (err)
				{
					return done(err);
				}
				game.remove(function(err)
				{
					if (err) return done(err);
					res.redirect('/');
				});
			});
		}
	});
});

router.get('/:slug', function(req, res)
{
	renderPage(req, res, 'games/game');
});

router.get('/:slug/privileges', function(req, res)
{
	renderPage(req, res, 'games/privileges');
});

router.get('/:slug/release', function(req, res)
{
	res.redirect('/games/game/' + req.params.slug + '/releases');
});

router.get('/:slug/releases', function(req, res)
{
	renderPage(req, res, 'games/releases',
		['releases'],
		function(game)
		{
			game.releases.reverse();
		}
	);
});

module.exports = router;<commit_msg>replaced indexOf with includes in if statement<commit_after>var router = require('express').Router(),
	async = require('async'),
	_ = require('lodash'),
	privileges = require('../../helpers/access').privilege,
	Game = require('../../models/game'),
	GameArchive = require('../../models/game-archive'),
	User = require('../../models/user'),
	Release = require('../../models/release'),
	log = require('../../helpers/logger');

/**
 * Abstraction to handle the page errors
 * @param  {String|Array} errors Single or collection of errors
 */
function handleError(req, res, errors)
{
	log.error(errors);
	if (Array.isArray(errors))
	{
		req.flash('errors', errors);
	}
	else
	{
		req.flash('error', 'Unable to update the game: ' + errors);
	}
	res.redirect(req.originalUrl);
}

/**
 * Abstraction to render a page, takes care of all
 * of the access control and populate the page with
 * standard parameters
 * @param  {Object} req The express request object
 * @param {Object} res The express response object
 * @param {string} template The template to use
 * @param {String} [populate='groups.group'] The game fields to populate
 * @param {function} [success] Callback when completed
 */
function renderPage(req, res, template, populate, success)
{
	populate = ['groups.group'].concat(populate || []);
	async.waterfall(
		[
			function(done)
			{
				Game.getBySlug(req.params.slug, done)
					.populate(populate);
			},
			function(game, done)
			{
				if (populate.includes('releases'))
				{
					User.populate(game.releases, {
						path: 'updatedBy',
						select: 'name'
					},
					function(err, releases)
					{
						if (err) return done(err);
						done(null, game);
					});
				}
				else
				{
					done(null, game);
				}
			},
			function(game, done)
			{
				if (!game) return res.status(404).render('404');

				game.getAccess(req.user, done);
			}
		],
		function(err, game, access)
		{
			if (err)
			{
				return res.status(401).render('401');
			}
			if (success) success(game);

			res.render(template, {
				game: game,
				capabilities: game.capabilities,
				host: req.headers.host,
				isEditor: access.permission > 0,
				isAdmin: access.permission > 1,
				token: access.token,
				errors: req.flash('errors'),
				success: req.flash('success'),
				error: req.flash('error')
			});
		}
	);
}

/**
 * Abstraction to post a page, takes care of all
 * of the access control and populate the page with
 * standard parameters.
 * @param  {Object} req The express request object
 * @param {Object} res The express response object
 * @param {string} minPrivilege The minimum privilege needed to run all form
 * @param {Object} actions Run custom page actions
 */
function postPage(req, res, minPrivilege, actions)
{
	async.waterfall(
		[
			function(done)
			{
				Game.getBySlug(req.params.slug, done).populate('groups.group');
			},
			function(game, done)
			{
				if (!game) return res.status(404).render('404');

				game.getAccess(req.user, done);
			},
			function(game, access, done)
			{
				var response = function(err)
				{
					if (err) return done(err);
					done(null, game);
				};

				if (access.permission < minPrivilege)
				{
					return done('Invalid form permissions');
				}

				if (!req.body.action || !actions[req.body.action])
				{
					done('Invalid form action');
				}
				actions[req.body.action](response, game, access);
			}
		],
		function(err, game)
		{
			if (err) return handleError(req, res, err);

			req.flash('success', game.title + " updated successfully");
			res.redirect(req.body.slug ? req.baseUrl + `/${req.body.slug}` : req.originalUrl);
		}
	);
}

function defaultCapabilities(capabilities)
{
	capabilities.ui = _.assign({
			mouse: false,
			touch: false
		}, capabilities.ui);

	capabilities.sizes = _.assign({
			xsmall: false,
			small: false,
			medium: false,
			large: false,
			xlarge: false
		}, capabilities.sizes);
}

// Generic reponse handler, must bind to game instance
function response(err)
{
	if (err) return done(err);
	done(null, this);
}

router.post('/:slug/releases', function(req, res)
{
	postPage(req, res, privileges.editor,
	{
		removeRelease: function(done)
		{
			Release.removeById(
				req.body.release,
				done
			);
		},
		statusChange: function(done)
		{
			Release.findByIdAndUpdate(
				req.body.release,
				{
					status: req.body.status,
					updated: Date.now(),
					updatedBy: req.body.updatedBy
				},
				done
			);
		}
	});
});

router.post('/:slug/privileges', function(req, res)
{
	postPage(req, res, privileges.admin,
	{
		removeGroup: function(done, game)
		{
			game.removeGroup(
				req.body.group,
				done
			);
		},
		addGroup: function(done, game)
		{
			game.addGroup(
				req.body.group,
				req.body.permission,
				done
			);
		},
		changePermission: function(done, game)
		{
			game.changePermission(
				req.body.group,
				req.body.permission,
				done
			);
		}
	});
});

router.post('/:slug/release', function(req, res)
{
	postPage(req, res, privileges.editor,
	{
		selectRelease: function(done, game, access)
		{
			Release.getById(req.body.release, function(err, release)
			{
				if (err) return done(err);

				res.render("games/release", {
					game: game,
					release: release,
					capabilities: release.capabilities,
					host: req.headers.host,
					isEditor: access.permission > 0,
					isAdmin: access.permission > 1,
					token: access.token,
					errors: req.flash('errors'),
					success: req.flash('success'),
					error: req.flash('error')
				});
			});
		},
		removeRelease: function(done, game)
		{
			Release.removeById(req.body.release, function(err)
			{
				if (err) return done(err);
				req.flash('success', 'Deleted release');
				res.redirect('/games/game/' + game.slug + '/releases');
			});
		},
		updateRelease: function(done, game)
		{
			req.checkBody('commitId', 'Commit is a Git hash').isCommit();
			req.checkBody('status', 'Status must be a valid status').isStatus();
			req.checkBody('notes').optional();

			if (req.body.version)
				req.checkBody('version', 'Version must be a valid semantic version').isSemver();

			defaultCapabilities(req.body.capabilities);

			var errors = req.validationErrors();

			if (errors) return done(errors);

			req.body.updated = Date.now();
			Release.findByIdAndUpdate(req.body.release, req.body, done);
		}
	});
});

router.post('/:slug', function(req, res)
{
	postPage(req, res, privileges.editor,
	{
		updateGame: function(done, game)
		{
			req.checkBody('title', 'Title is required').notEmpty();
			req.checkBody('bundleId', 'Bundle ID is required').isBundleId();
			req.checkBody('slug', 'Slug is required').isSlug();
			req.checkBody('repository', 'Repository needs to be a URL').isURL();
			req.checkBody('location', 'Location needs to be a URL').isURL();
			req.checkBody('description').optional();
			req.checkBody('thumbnail').optional();

			var errors = req.validationErrors();

			if (errors) return done(errors);

			// Set defaults to false for capabilities
			// so the default that are set to true actually
			// get updated
			defaultCapabilities(req.body.capabilities);

			req.body.updated = Date.now();

			// Update the game
			Game.findByIdAndUpdate(game._id, req.body, done);
		},
		removeGame: function(done, game)
		{
			var gameArchive = new GameArchive(game.toObject());

			gameArchive.save(function(err, archive)
			{
				if (err)
				{
					return done(err);
				}
				game.remove(function(err)
				{
					if (err) return done(err);
					res.redirect('/');
				});
			});
		}
	});
});

router.get('/:slug', function(req, res)
{
	renderPage(req, res, 'games/game');
});

router.get('/:slug/privileges', function(req, res)
{
	renderPage(req, res, 'games/privileges');
});

router.get('/:slug/release', function(req, res)
{
	res.redirect('/games/game/' + req.params.slug + '/releases');
});

router.get('/:slug/releases', function(req, res)
{
	renderPage(req, res, 'games/releases',
		['releases'],
		function(game)
		{
			game.releases.reverse();
		}
	);
});

module.exports = router;<|endoftext|>


<commit_before>// For token sale holders:
// 1. Add the address users are sending to
// 2. Add the gas limit users should use to send successfully (this avoids OOG errors)
// 3. Add any data if applicable
// 4. Add a message if you want.

// Token Calendar:
// http://www.tokensalecalendar.com/
// If you aren't on the above, you should get on it.

//

module.exports = [
 {
  // Vibehub (VIBEX) ICO Sale
  to:         '0x5454af9d2ba75a60fa5b0419c251810544cea21d',
  gasLimit:   200000,
  data:       '',
  msg:        'WeBetCrypto ICO Sale. Thank you for your support!'
},{
  // Vibehub (VIBEX) ICO Sale
  to:         '0x882448f83d90b2bf477af2ea79327fdea1335d93',
  gasLimit:   200000,
  data:       '',
  msg:        'Vibehub ICO Sale. Thank you for your support!'
},
{
  // Substratum (SUB) Network ICO Sale
  to:         '0xaf518d65f84e4695a4da0450ec02c1248f56b668',
  gasLimit:   200000,
  data:       '',
  msg:        'Substratum Network ICO Sale. Thank you for your support!'
},
{
  // CryptoMart
  to:         '0x96028Fc22499A7Bad7727baB017eC2352308C15a',
  gasLimit:   200000,
  data:       '',
  msg:        'CryptoMart ICO | 25/082017-25/09/2017'
},
{
  // YUPIE (YUPIE)
  to:         '0x0F33bb20a282A7649C7B3AFf644F084a9348e933',
  gasLimit:   400000,
  data:       '',
  msg:        'YUPIE (YUPIE) ICO'
},
{
  // Atlant.io ATLANT ICO Sale
  to:         '0xF46ede17c0bAb20d87Cd079a2632f71433A407b9',
  gasLimit:   150000,
  data:       '',
  msg:        'ATLANT ICO Sale. Thank you for your support. '
},
{
  // DCORP
  to:         '0x7cB57B5A97eAbe94205C07890BE4c1aD31E486A8',
  gasLimit:   21000,
  data:       '',
  msg:        'Thank you for your donation! TO THE MOON! 🌝'
},
{
  // District0x
  to:         '0xF8094e15c897518B5Ac5287d7070cA5850eFc6ff',
  gasLimit:   200000,
  data:       '',
  msg:        'district0x. WEBSITE: contribution.district0x.io STARTS: Tue, 18 Jul 2017 08:00:00 REMEMBER: Use gas price of 50 GWEI (look in header) ENS: district0x.eth'
},
{
  // BattleDrome
  to:         '0xeaAf270436a0ed397ED23BBF64DF7b1DCAfF142F',
  gasLimit:   85000,
  data:       '',
  msg:        'BattleDrome ICO/Crowdsale. Thanks for your support!'
},
{
  // OpenANX
  to:         '0xd6Cd31F283d24cfb442cBA1Bcf42290c07C15792',
  gasLimit:   150000,
  data:       '',
  msg:        'Kyber Network. Starts September 15, 2017.'
},
{
  // Adchain
  to:         '0x0000',
  gasLimit:   200000,
  data:       '',
  msg:        'Adchain. Starts June 26, 2017.'
},
{
  // LookRev
  to:         '0x21ae23b882a340a22282162086bc98d3e2b73018',
  gasLimit:   200000,
  data:       '',
  msg:        'LookRev. ICO Starts August 30, 2017.'
},
{
  // OmiseGo
  to:         '0x0000',
  gasLimit:   200000,
  data:       '',
  msg:        'OmiseGo. Starts June 27, 2017.'
},
{
  // Nimiq
  to:         '0xcfb98637bcae43C13323EAa1731cED2B716962fD',
  gasLimit:   300000,
  data:       '0xb4427263',
  msg:        'Nimiq. Starts June 28, 2017 at block 3,942,670.'
},
{
  // Santiment
  to:         '0x0000',
  gasLimit:   200000,
  data:       '',
  msg:        'Santiment. Starts June 30, 2017.'
},
{
  // iDice
  to:         '0x5a84969bb663fb64F6d015DcF9F622Aedc796750',
  gasLimit:   -1,
  data:       '',
  msg:        'iDice. *** Reminder: Always do your due diligence & understand risks before sending. - MEW ***'
},
{
  // Moeda
  to:         '0x4870E705a3def9DDa6da7A953D1cd3CCEDD08573',
  gasLimit:   200000,
  data:       '',
  msg:        'Moeda. Ends at block 4,111,557.'
},
{
  // FAKE STATUS
  to:         '0xc9f32ce1127e44c51cbd182d6364f3d707fd0d47',
  gasLimit:   -1,
  data:       '',
  msg:        '⚠ DO NOT SEND. THIS IS *** NOT *** THE ADDRESS FOR STATUS!!'
},
{
  // SunContract
  to:         '0x5fb3D432bae33FCd418edE263D98D7440E7fA3ea',
  gasLimit:   200000,
  data:       '',
  msg:        'SunContract ICO address - suncontract.org'
},
{
  // SoerenGelderCom
  to:         '0x08FF699987510D299Eca5cfE258B19d7F4d53ae9',
  gasLimit:   200000,
  data:       '',
  msg:        'The above address was preICO-adress valid til 23rd of July 2017! Greets from Soeren Gelder'
},
  {
  // SoerenGelderCom
  to:         '0xA64AD5dA94137327F0895AC8C3A10924af73400d',
  gasLimit:   200000,
  data:       '',
  msg:        'SGelderGER (symbol: GELD) coin ICO starts 07th of August 2017'
},
  {
  // BlockCAT Token Sale
  to:         '0x56ba2Ee7890461f463F7be02aAC3099f6d5811A8',
  gasLimit:   200000,
  data:       '0xb4427263',
  msg:        'The BlockCAT Token Sale (CAT) starts July 15th, 2017 on Ethereum block number 4027470.'
},
{
  // NVC
  to:         '0xd88755197e107603C139df6E709ed09EEC6b6bB3',
  gasLimit:   200000,
  data:       '',
  msg:        'NVC Fund'
},
{
  // fake coindash
  to:         '0x6a164122d5cf7c840d26e829b46dcc4ed6c0ae48',
  gasLimit:   -1,
  data:       '',
  msg:        'fake coindash do not send pleaseeeeeeeee!'
},
{
  // maybe fake coindash??
  to:         '0x7da6955457b72fdd0e80709d704520ff85d79e39',
  gasLimit:   -1,
  data:       '',
  msg:        'This may be a fake address promoted by phishers. go research first please.'
},
{
  // Ohni
  to:         '0x2a8a7afa955d8616e2e60e454e5a9c6b6c0a60fc',
  gasLimit:   200000,
  data:       '',
  msg:        'OHNI ICO. Restoration of our communities!'
},
 {
  // Relex
  to:         '0xb306b47b36705332f48537b02b581ea36d108b79',
  gasLimit:   200000,
  data:       '',
  msg:        'Developing real estate, at the speed of crowd. Relex ICO October 16, 2017'
},
{
  // Avalon
  to:         '0xeD247980396B10169BB1d36f6e278eD16700a60f',
  gasLimit:   60000,
  data:       '',
  msg:        'Avalon ICO starts August 25, 2017 - avalon.nu'
},
{
  // Maecenas
  to:         '0x9B60874D7bc4e4fBDd142e0F5a12002e4F7715a6',
  gasLimit:   400000,
  data:       '',
  msg:        'Maecenas ICO starts September 5, 2017 - www.maecenas.co'
},
 {
  // Mitrav (MTR) ICO Sale
  to:         '0x7FC408011165760eE31bE2BF20dAf450356692Af',
  gasLimit:   200000,
  data:       '',
  msg:        'Mitrav ICO Sale. Thank you for your support!'
},
{
  // BMCHAIN ICO
  to:         '0x19d7a9ad3b49252fd2ef640d0e43dfd651168499',
  gasLimit:   100000,
  data:       '',
  msg:        'BMChain ICO - Platform of digital reputation - Official site https://bmchain.io'
},
{
  // Kin Token Sale
  to:         '0x8BfE5EbB128eE82F4BA80f56bb32409CC87bC6fb',
  gasLimit:   200000,
  data:       '',
  msg:        'Kin Token Sale'
},
{
  // Cobinhood Token Sale
  to:         '0xb2f7eb1f2c37645be61d73953035360e768d81e6',
  gasLimit:   200000,
  data:       '',
  msg:        'Cobinhood Token Sale'
},
{
  // fake Santiment token last-sale
  to:         '0xdccea2e783d873acb4ccfb7dead1004d90ad481d',
  gasLimit:   -1,
  data:       '',
  msg:        'This may be a fake santiment address promoted by phishers. Go research first please.'
},
{
  // Zeus exchange token sale
  to:         '0xe386B139Ed3715Ca4B18Fd52671bDcea1cdFE4b1',
  gasLimit:   200000,
  data:       '',
  msg:        'Zeus Exchange - The First Hybrid Trading Platform for Traditional Stock Investors and Crypto Traders. Official site https://zeus.exchange'
}
]

<commit_msg>Manual gas limits for upcoming TBT token<commit_after>// For token sale holders:
// 1. Add the address users are sending to
// 2. Add the gas limit users should use to send successfully (this avoids OOG errors)
// 3. Add any data if applicable
// 4. Add a message if you want.

// Token Calendar:
// http://www.tokensalecalendar.com/
// If you aren't on the above, you should get on it.

//

module.exports = [
 {
  // Vibehub (VIBEX) ICO Sale
  to:         '0x5454af9d2ba75a60fa5b0419c251810544cea21d',
  gasLimit:   200000,
  data:       '',
  msg:        'WeBetCrypto ICO Sale. Thank you for your support!'
},{
  // Vibehub (VIBEX) ICO Sale
  to:         '0x882448f83d90b2bf477af2ea79327fdea1335d93',
  gasLimit:   200000,
  data:       '',
  msg:        'Vibehub ICO Sale. Thank you for your support!'
},
{
  // Substratum (SUB) Network ICO Sale
  to:         '0xaf518d65f84e4695a4da0450ec02c1248f56b668',
  gasLimit:   200000,
  data:       '',
  msg:        'Substratum Network ICO Sale. Thank you for your support!'
},
{
  // CryptoMart
  to:         '0x96028Fc22499A7Bad7727baB017eC2352308C15a',
  gasLimit:   200000,
  data:       '',
  msg:        'CryptoMart ICO | 25/082017-25/09/2017'
},
{
  // YUPIE (YUPIE)
  to:         '0x0F33bb20a282A7649C7B3AFf644F084a9348e933',
  gasLimit:   400000,
  data:       '',
  msg:        'YUPIE (YUPIE) ICO'
},
{
  // Atlant.io ATLANT ICO Sale
  to:         '0xF46ede17c0bAb20d87Cd079a2632f71433A407b9',
  gasLimit:   150000,
  data:       '',
  msg:        'ATLANT ICO Sale. Thank you for your support. '
},
{
  // DCORP
  to:         '0x7cB57B5A97eAbe94205C07890BE4c1aD31E486A8',
  gasLimit:   21000,
  data:       '',
  msg:        'Thank you for your donation! TO THE MOON! 🌝'
},
{
  // District0x
  to:         '0xF8094e15c897518B5Ac5287d7070cA5850eFc6ff',
  gasLimit:   200000,
  data:       '',
  msg:        'district0x. WEBSITE: contribution.district0x.io STARTS: Tue, 18 Jul 2017 08:00:00 REMEMBER: Use gas price of 50 GWEI (look in header) ENS: district0x.eth'
},
{
  // BattleDrome
  to:         '0xeaAf270436a0ed397ED23BBF64DF7b1DCAfF142F',
  gasLimit:   85000,
  data:       '',
  msg:        'BattleDrome ICO/Crowdsale. Thanks for your support!'
},
{
  // OpenANX
  to:         '0xd6Cd31F283d24cfb442cBA1Bcf42290c07C15792',
  gasLimit:   150000,
  data:       '',
  msg:        'Kyber Network. Starts September 15, 2017.'
},
{
  // Adchain
  to:         '0x0000',
  gasLimit:   200000,
  data:       '',
  msg:        'Adchain. Starts June 26, 2017.'
},
{
  // LookRev
  to:         '0x21ae23b882a340a22282162086bc98d3e2b73018',
  gasLimit:   200000,
  data:       '',
  msg:        'LookRev. ICO Starts August 30, 2017.'
},
{
  // OmiseGo
  to:         '0x0000',
  gasLimit:   200000,
  data:       '',
  msg:        'OmiseGo. Starts June 27, 2017.'
},
{
  // Nimiq
  to:         '0xcfb98637bcae43C13323EAa1731cED2B716962fD',
  gasLimit:   300000,
  data:       '0xb4427263',
  msg:        'Nimiq. Starts June 28, 2017 at block 3,942,670.'
},
{
  // Santiment
  to:         '0x0000',
  gasLimit:   200000,
  data:       '',
  msg:        'Santiment. Starts June 30, 2017.'
},
{
  // iDice
  to:         '0x5a84969bb663fb64F6d015DcF9F622Aedc796750',
  gasLimit:   -1,
  data:       '',
  msg:        'iDice. *** Reminder: Always do your due diligence & understand risks before sending. - MEW ***'
},
{
  // Moeda
  to:         '0x4870E705a3def9DDa6da7A953D1cd3CCEDD08573',
  gasLimit:   200000,
  data:       '',
  msg:        'Moeda. Ends at block 4,111,557.'
},
{
  // FAKE STATUS
  to:         '0xc9f32ce1127e44c51cbd182d6364f3d707fd0d47',
  gasLimit:   -1,
  data:       '',
  msg:        '⚠ DO NOT SEND. THIS IS *** NOT *** THE ADDRESS FOR STATUS!!'
},
{
  // SunContract
  to:         '0x5fb3D432bae33FCd418edE263D98D7440E7fA3ea',
  gasLimit:   200000,
  data:       '',
  msg:        'SunContract ICO address - suncontract.org'
},
{
  // SoerenGelderCom
  to:         '0x08FF699987510D299Eca5cfE258B19d7F4d53ae9',
  gasLimit:   200000,
  data:       '',
  msg:        'The above address was preICO-adress valid til 23rd of July 2017! Greets from Soeren Gelder'
},
  {
  // SoerenGelderCom
  to:         '0xA64AD5dA94137327F0895AC8C3A10924af73400d',
  gasLimit:   200000,
  data:       '',
  msg:        'SGelderGER (symbol: GELD) coin ICO starts 07th of August 2017'
},
  {
  // BlockCAT Token Sale
  to:         '0x56ba2Ee7890461f463F7be02aAC3099f6d5811A8',
  gasLimit:   200000,
  data:       '0xb4427263',
  msg:        'The BlockCAT Token Sale (CAT) starts July 15th, 2017 on Ethereum block number 4027470.'
},
{
  // NVC
  to:         '0xd88755197e107603C139df6E709ed09EEC6b6bB3',
  gasLimit:   200000,
  data:       '',
  msg:        'NVC Fund'
},
{
  // fake coindash
  to:         '0x6a164122d5cf7c840d26e829b46dcc4ed6c0ae48',
  gasLimit:   -1,
  data:       '',
  msg:        'fake coindash do not send pleaseeeeeeeee!'
},
{
  // maybe fake coindash??
  to:         '0x7da6955457b72fdd0e80709d704520ff85d79e39',
  gasLimit:   -1,
  data:       '',
  msg:        'This may be a fake address promoted by phishers. go research first please.'
},
{
  // Ohni
  to:         '0x2a8a7afa955d8616e2e60e454e5a9c6b6c0a60fc',
  gasLimit:   200000,
  data:       '',
  msg:        'OHNI ICO. Restoration of our communities!'
},
 {
  // Relex
  to:         '0xb306b47b36705332f48537b02b581ea36d108b79',
  gasLimit:   200000,
  data:       '',
  msg:        'Developing real estate, at the speed of crowd. Relex ICO October 16, 2017'
},
{
  // Avalon
  to:         '0xeD247980396B10169BB1d36f6e278eD16700a60f',
  gasLimit:   60000,
  data:       '',
  msg:        'Avalon ICO starts August 25, 2017 - avalon.nu'
},
{
  // Maecenas
  to:         '0x9B60874D7bc4e4fBDd142e0F5a12002e4F7715a6',
  gasLimit:   400000,
  data:       '',
  msg:        'Maecenas ICO starts September 5, 2017 - www.maecenas.co'
},
 {
  // Mitrav (MTR) ICO Sale
  to:         '0x7FC408011165760eE31bE2BF20dAf450356692Af',
  gasLimit:   200000,
  data:       '',
  msg:        'Mitrav ICO Sale. Thank you for your support!'
},
{
  // BMCHAIN ICO
  to:         '0x19d7a9ad3b49252fd2ef640d0e43dfd651168499',
  gasLimit:   100000,
  data:       '',
  msg:        'BMChain ICO - Platform of digital reputation - Official site https://bmchain.io'
},
{
  // Kin Token Sale
  to:         '0x8BfE5EbB128eE82F4BA80f56bb32409CC87bC6fb',
  gasLimit:   200000,
  data:       '',
  msg:        'Kin Token Sale'
},
 {
  // TBOT Token Sale
  to:         '0xAFe60511341a37488de25Bef351952562E31fCc1',
  gasLimit:   200000,
  data:       '',
  msg:        'Tbot ICO Sale.'
},
{
  // Cobinhood Token Sale
  to:         '0xb2f7eb1f2c37645be61d73953035360e768d81e6',
  gasLimit:   200000,
  data:       '',
  msg:        'Cobinhood Token Sale'
},
{
  // fake Santiment token last-sale
  to:         '0xdccea2e783d873acb4ccfb7dead1004d90ad481d',
  gasLimit:   -1,
  data:       '',
  msg:        'This may be a fake santiment address promoted by phishers. Go research first please.'
},
{
  // Zeus exchange token sale
  to:         '0xe386B139Ed3715Ca4B18Fd52671bDcea1cdFE4b1',
  gasLimit:   200000,
  data:       '',
  msg:        'Zeus Exchange - The First Hybrid Trading Platform for Traditional Stock Investors and Crypto Traders. Official site https://zeus.exchange'
}
]

<|endoftext|>


<commit_before>/**
 *  A module for working with the overpass api
 */
'use strict';

var $ = require('jquery');
var locationMath = require('location-math');
var nominatimUrl = 'http://nominatim.openstreetmap.org/search?';
var defaultParams = {
  format: 'json',
  polygon: 0,
  limit: 15,
  addressdetails: 1
};
var callback;

function createBoundingBoxStr(position) {
  console.log(locationMath.getBoundingBox(position, 1000));
  var boundingBox = locationMath.getBoundingBox(position, 1000);

  return boundingBox.lonMin + ',' + boundingBox.latMin + ',' +
                boundingBox.lonMax + ',' + boundingBox.latMax;
}

/**
 * Function to retrieve stuff from overpass. Query in the form of ["leisure"="playground"]
 */
function requestWithPosition(query, currentPosition, cb) {
  callback = cb;
  var nominatimParams = {};
  nominatimParams.q = query;
  nominatimParams.viewbox = createBoundingBoxStr(currentPosition);
  nominatimParams.bounded = 1;
  $.extend(nominatimParams, defaultParams);

  var urlStr = nominatimUrl + decodeURIComponent($.param(nominatimParams));
  console.log(urlStr);
  $.getJSON(urlStr,
    function(data) {
      data = parse(data);
      callback(parse(data));
      //parse(data);
    });
}

function requestWithPostcode(query, postcode, cb) {
  callback = cb;

  var nominatimParams = {};
  nominatimParams.q = query + ' in ' + postcode;
  $.extend(nominatimParams, defaultParams);

  var urlStr = nominatimUrl + decodeURIComponent($.param(nominatimParams));
  console.log(urlStr);
  $.getJSON(urlStr,
    function(data) {
      data = parse(data);
      callback(parse(data));
      //parse(data);
    });
}

function parse(data) {
  for (var i = 0; i < data.length; i++) {
    data[i].osmId = parseFloat(data[i].osm_id);
    data[i].osmType = data[i].osm_type;
    data[i].lat = parseFloat(data[i].lat);
    data[i].lon = parseFloat(data[i].lon);
  }

  return data;
}

exports.requestWithPosition = requestWithPosition;
exports.requestWithPostcode = requestWithPostcode;
<commit_msg>nominatim: use https<commit_after>/**
 *  A module for working with the overpass api
 */
'use strict';

var $ = require('jquery');
var locationMath = require('location-math');
var nominatimUrl = 'https://nominatim.openstreetmap.org/search?';
var defaultParams = {
  format: 'json',
  polygon: 0,
  limit: 15,
  addressdetails: 1
};
var callback;

function createBoundingBoxStr(position) {
  console.log(locationMath.getBoundingBox(position, 1000));
  var boundingBox = locationMath.getBoundingBox(position, 1000);

  return boundingBox.lonMin + ',' + boundingBox.latMin + ',' +
                boundingBox.lonMax + ',' + boundingBox.latMax;
}

/**
 * Function to retrieve stuff from overpass. Query in the form of ["leisure"="playground"]
 */
function requestWithPosition(query, currentPosition, cb) {
  callback = cb;
  var nominatimParams = {};
  nominatimParams.q = query;
  nominatimParams.viewbox = createBoundingBoxStr(currentPosition);
  nominatimParams.bounded = 1;
  $.extend(nominatimParams, defaultParams);

  var urlStr = nominatimUrl + decodeURIComponent($.param(nominatimParams));
  console.log(urlStr);
  $.getJSON(urlStr,
    function(data) {
      data = parse(data);
      callback(parse(data));
      //parse(data);
    });
}

function requestWithPostcode(query, postcode, cb) {
  callback = cb;

  var nominatimParams = {};
  nominatimParams.q = query + ' in ' + postcode;
  $.extend(nominatimParams, defaultParams);

  var urlStr = nominatimUrl + decodeURIComponent($.param(nominatimParams));
  console.log(urlStr);
  $.getJSON(urlStr,
    function(data) {
      data = parse(data);
      callback(parse(data));
      //parse(data);
    });
}

function parse(data) {
  for (var i = 0; i < data.length; i++) {
    data[i].osmId = parseFloat(data[i].osm_id);
    data[i].osmType = data[i].osm_type;
    data[i].lat = parseFloat(data[i].lat);
    data[i].lon = parseFloat(data[i].lon);
  }

  return data;
}

exports.requestWithPosition = requestWithPosition;
exports.requestWithPostcode = requestWithPostcode;
<|endoftext|>


<commit_before>class Media {
  constructor(options) {
    this.id = options.item.id;
    this.code = options.item.code;
    this.fetcher = options.fetcher;
    this.username = options.item.owner.username;
  }

  static template(item, i) {
    let base = 'https://www.instagram.com/';
    let location = item.location ? item.location.name : '';
    let locationId = item.location ? item.location.id : '';
    let date = moment(item.date * 1000);
    let timeago = date.fromNow(true);
    let fulldate = date.format('LLLL');
    let caption = item.caption || '';
    let profile = `${base}${item.owner.username}/`;
    let link = `${base}p/${item.code}/`;
    let likeIcon = `favorite${item.likes.viewer_has_liked ? '' : '_border'}`;
    let itemCard = (item.is_video ?
      `

        play_arrow
        ` :
      `

        `) +
      `<img src="${item.display_src}"></a></div>`;

    return `

            <time data-date="${+date}" title="${fulldate}">${timeago}</time>

            ${item.owner.username}

              ${location}

        ${itemCard}

${caption}

            ${likeIcon}
            ${item.likes.count}

            chat_bubble_outline
            ${item.comments.count}

            refresh

`;
  }

  like(liked) {
    let state = liked ? 'unlike' : 'like';
    let url = `web/likes/${this.id}/${state}/`;
    return this.fetcher.post(url)
      .then(body => {
        if (body.status === 'ok') {
          return this.updateCache();
        }
      });
  }

  updateCache() {
    return this.fetcher.getJSON(`p/${this.code}/?taken-by=${this.username}&__a=1`)
      .then(body => {
        if (body.media) {
          let key = moment(body.media.date * 1000).startOf('day') / 100000;
          return this.fetcher.storeItem([body.media])
            .then(() => (DB.g(key + '')))
            .then(items => {
              let found = items.filter(item => (item.id === body.media.id));
              return found.length ? found[0] : null;
            });
        } else {
          return Promise.resolve(null);
        }
      });
  }

  static updateTimeElements() {
    $('time').each((i, e) => {
      let date = moment(+e.dataset.date);
      e.textContent = date.fromNow(true);
    });
  }
}
<commit_msg>Fix update item<commit_after>class Media {
  constructor(options) {
    this.id = options.item.id;
    this.code = options.item.code;
    this.fetcher = options.fetcher;
    this.username = options.item.owner.username;
  }

  static template(item, i) {
    let base = 'https://www.instagram.com/';
    let location = item.location ? item.location.name : '';
    let locationId = item.location ? item.location.id : '';
    let date = moment(item.date * 1000);
    let timeago = date.fromNow(true);
    let fulldate = date.format('LLLL');
    let caption = item.caption || '';
    let profile = `${base}${item.owner.username}/`;
    let link = `${base}p/${item.code}/`;
    let likeIcon = `favorite${item.likes.viewer_has_liked ? '' : '_border'}`;
    let itemCard = (item.is_video ?
      `

        play_arrow
        ` :
      `

        `) +
      `<img src="${item.display_src}"></a></div>`;

    return `

            <time data-date="${+date}" title="${fulldate}">${timeago}</time>

            ${item.owner.username}

              ${location}

        ${itemCard}

${caption}

            ${likeIcon}
            ${item.likes.count}

            chat_bubble_outline
            ${item.comments.count}

            refresh

`;
  }

  like(liked) {
    let state = liked ? 'unlike' : 'like';
    let url = `web/likes/${this.id}/${state}/`;
    return this.fetcher.post(url)
      .then(body => {
        if (body.status === 'ok') {
          return this.updateCache();
        }
      });
  }

  updateCache() {
    return this.fetcher.getJSON(`p/${this.code}/?taken-by=${this.username}&__a=1`)
      .then(body => {
        let media = body.graphql.shortcode_media;
        if (media) {
          let date = media.taken_at_timestamp;
          let key = moment(date * 1000).startOf('day') / 100000;
          return this.fetcher.storeItem([{ node: media }])
            .then(() => (DB.g(key + '')))
            .then(items => {
              let found = items.filter(item => (item.id === media.id));
              return found.length ? found[0] : null;
            });
        } else {
          return Promise.resolve(null);
        }
      });
  }

  static updateTimeElements() {
    $('time').each((i, e) => {
      let date = moment(+e.dataset.date);
      e.textContent = date.fromNow(true);
    });
  }
}
<|endoftext|>


<commit_before>(function() {
'use strict';
// ------------------------------------
angular
  .module("myApp")
  .factory("tripService", tripService)

function tripService() {
  var s = {};
  var selectedTripIndex = null;

  s.trips = [
    {
      destination: { name: 'Dubai', coordinates: { latitude: 25.253834, longitude: 55.364814 } },
      startDate: new Date(2015, 7, 14),
      endDate: new Date(2015, 7, 15),
      stops: []
    },
    {
      destination: { name: 'Melbourne', coordinates: { latitude: -37.8589546, longitude: 144.5191752 } },
      startDate: new Date(2015, 10, 14),
      endDate: new Date(2015, 10, 15),
      stops: []
    }
  ];

  recalculateBounds();
  recalculateCenter();

  s.changeCurrentTrip = function(t) {
    s.trips[selectedTripIndex] = t;
  };

  s.selectTrip = function(index) {
    selectedTripIndex = index;
    recalculateBounds();
    recalculateCenter();
  };

  s.getCurrentTripCopy = function() {
    return _.cloneDeep(s.trips[selectedTripIndex])
  }

  s.isTripSelected = function() {
    return selectedTripIndex !== null;
  }

  function recalculateBounds() {
    var coordinateLoop = [];

    // collect the coordinates that should be included in the boundary
    if (selectedTripIndex !== null) {
      coordinateLoop = [s.trips[selectedTripIndex].destination.coordinates];
      coordinateLoop.concat(s.trips[selectedTripIndex].stops)
    } else {
      coordinateLoop = _.map(s.trips, 'destination.coordinates')
    }

    if (coordinateLoop.length < 2) return;

    var latitudes  = _.map(coordinateLoop, 'latitude');
    var longitudes = _.map(coordinateLoop, 'longitude');

    // reset boundary
    s.coordinateBoundaries = {
      northeast: {
        latitude: _.max(latitudes),
        longitude: _.max(longitudes)
      },
      southwest: {
        latitude: _.min(latitudes),
        longitude: _.min(longitudes)
      }
    };
  }

  function recalculateCenter() {
    if (selectedTripIndex !== null) {
      s.coordinateCenter = s.trips[selectedTripIndex].destination.coordinates
    } else {
      s.coordinateCenter = { latitude: 0, longitude: 0 }
    }
  }

  return s;
}

// ------------------------------------
})();<commit_msg>Remove deprecated center-finding function<commit_after>(function() {
'use strict';
// ------------------------------------
angular
  .module("myApp")
  .factory("tripService", tripService)

function tripService() {
  var s = {};
  var selectedTripIndex = null;

  s.trips = [
    {
      destination: { name: 'Dubai', coordinates: { latitude: 25.253834, longitude: 55.364814 } },
      startDate: new Date(2015, 7, 14),
      endDate: new Date(2015, 7, 15),
      stops: []
    },
    {
      destination: { name: 'Melbourne', coordinates: { latitude: -37.8589546, longitude: 144.5191752 } },
      startDate: new Date(2015, 10, 14),
      endDate: new Date(2015, 10, 15),
      stops: []
    }
  ];

  recalculateBounds();

  s.changeCurrentTrip = function(t) {
    s.trips[selectedTripIndex] = t;
  };

  s.selectTrip = function(index) {
    selectedTripIndex = index;
    recalculateBounds();
  };

  s.getCurrentTripCopy = function() {
    return _.cloneDeep(s.trips[selectedTripIndex])
  }

  s.isTripSelected = function() {
    return selectedTripIndex !== null;
  }

  function recalculateBounds() {
    var coordinateLoop = [];

    // collect the coordinates that should be included in the boundary
    if (selectedTripIndex !== null) {
      coordinateLoop = [s.trips[selectedTripIndex].destination.coordinates];
      coordinateLoop.concat(s.trips[selectedTripIndex].stops)
    } else {
      coordinateLoop = _.map(s.trips, 'destination.coordinates')
    }

    if (coordinateLoop.length < 2) return;

    var latitudes  = _.map(coordinateLoop, 'latitude');
    var longitudes = _.map(coordinateLoop, 'longitude');

    // reset boundary
    s.coordinateBoundaries = {
      northeast: {
        latitude: _.max(latitudes),
        longitude: _.max(longitudes)
      },
      southwest: {
        latitude: _.min(latitudes),
        longitude: _.min(longitudes)
      }
    };
  }

  return s;
}

// ------------------------------------
})();<|endoftext|>


<commit_before>'use strict';

angular.module('myApp.twitch', ['ngRoute'])

.config(['$routeProvider', function($routeProvider) {
  $routeProvider.when('/twitch', {
    templateUrl: 'twitch/twitch.html',
    controller: 'TwitchCtrl'
  });
}])

.controller('TwitchCtrl', [function() {
      $(".menu-item").removeClass("active");
      $("#twitch").addClass("active");
}]);<commit_msg>completed controller<commit_after>'use strict';

angular.module('myApp.twitch', ['ngRoute'])

    .config(['$routeProvider', function ($routeProvider) {
        $routeProvider.when('/twitch', {
            templateUrl: 'twitch/twitch.html',
            controller: 'TwitchCtrl',
            resolve: {
                streams: function (twitchStreams) {
                    return getStreams(twitchStreams);
                }
            }
        });
    }])

    .controller('TwitchCtrl', ['$scope', 'streams', function ($scope, streams) {
        $(".menu-item").removeClass("active");
        $("#twitch").addClass("active");
        $scope.streams = formatStreams(streams);
    }]);<|endoftext|>